diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 91f31875fdd..58358c6e594 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -33,15 +33,15 @@ jobs: with: name: errors path: job-initiate-error-tracking.txt - build_jdk_8: - name: Build JDK 8 + build_jdk_11: + name: Build JDK 11 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Cache Gradle packages uses: actions/cache@v2 with: @@ -64,41 +64,6 @@ jobs: with: name: errors path: job-${{ github.job }}.txt - test_alternate_jdks: - name: Test JDK 11 and 12 - runs-on: ubuntu-latest - strategy: - matrix: - jdk: [11, 12] - fail-fast: false - steps: - - uses: actions/checkout@v2 - - name: Set up JDK ${{ matrix.jdk }} - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.jdk }} - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - - name: Test with Gradle - run: | - export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" - export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" - export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" - ./gradlew test -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" --stacktrace - - name: Track error step - uses: spring-projects/track-build-errors-action@v1 - if: ${{ failure() }} - with: - job-name: ${{ github.job }}-${{ matrix.jdk }} - - name: Export errors file - uses: actions/upload-artifact@v2 - if: ${{ failure() }} - with: - name: errors - path: job-${{ github.job }}-${{ matrix.jdk }}.txt snapshot_tests: name: Test against snapshots runs-on: ubuntu-latest @@ -107,7 +72,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Snapshot Tests run: | export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" @@ -136,7 +101,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Run Sonar on given (non-master) branch if: ${{ github.ref != 'refs/heads/master' }} run: | @@ -165,21 +130,21 @@ jobs: path: job-${{ github.job }}.txt deploy_artifacts: name: Deploy Artifacts - needs: [build_jdk_8, test_alternate_jdks, snapshot_tests, sonar_analysis] + needs: [build_jdk_11, snapshot_tests, sonar_analysis] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Deploy artifacts run: | export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" export VERSION_HEADER=$'Version: GnuPG v2\n\n' - export ORG_GRADLE_PROJECT_signingKey=${GPG_PRIVATE_KEY#"$VERSION_HEADER"} + export ORG_GRADLE_PROJECT_signingKey=${GPG_PRIVATE_KEY_NO_HEADER#"$VERSION_HEADER"} export ORG_GRADLE_PROJECT_signingPassword="$GPG_PASSPHRASE" ./gradlew deployArtifacts -PossrhUsername="$OSSRH_TOKEN_USERNAME" -PossrhPassword="$OSSRH_TOKEN_PASSWORD" -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" --stacktrace --no-parallel ./gradlew finalizeDeployArtifacts -PossrhUsername="$OSSRH_TOKEN_USERNAME" -PossrhPassword="$OSSRH_TOKEN_PASSWORD" -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" --stacktrace --no-parallel @@ -203,14 +168,14 @@ jobs: path: job-${{ github.job }}.txt deploy_docs: name: Deploy Docs - needs: [build_jdk_8, test_alternate_jdks, snapshot_tests, sonar_analysis] + needs: [build_jdk_11, snapshot_tests, sonar_analysis] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Deploy Docs run: | export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" @@ -234,14 +199,14 @@ jobs: path: job-${{ github.job }}.txt deploy_schema: name: Deploy Schema - needs: [build_jdk_8, test_alternate_jdks, snapshot_tests, sonar_analysis] + needs: [build_jdk_11, snapshot_tests, sonar_analysis] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Deploy Schema run: | export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" @@ -265,7 +230,7 @@ jobs: path: job-${{ github.job }}.txt notify_result: name: Check for failures - needs: [build_jdk_8, test_alternate_jdks, snapshot_tests, sonar_analysis, deploy_artifacts, deploy_docs, deploy_schema] + needs: [build_jdk_11, snapshot_tests, sonar_analysis, deploy_artifacts, deploy_docs, deploy_schema] if: always() runs-on: ubuntu-latest steps: diff --git a/.github/workflows/pr-build-workflow.yml b/.github/workflows/pr-build-workflow.yml index 4c2fc44d1a9..7d98efeaa08 100644 --- a/.github/workflows/pr-build-workflow.yml +++ b/.github/workflows/pr-build-workflow.yml @@ -12,7 +12,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: '8' + java-version: '11' - name: Cache Gradle packages uses: actions/cache@v2 with: diff --git a/README.adoc b/README.adoc index 7551064039a..4fda007d1a0 100644 --- a/README.adoc +++ b/README.adoc @@ -30,9 +30,9 @@ In the instructions below, https://vimeo.com/34436402[`./gradlew`] is invoked fr a cross-platform, self-contained bootstrap mechanism for the build. === Prerequisites -https://help.github.com/set-up-git-redirect[Git] and the https://www.oracle.com/technetwork/java/javase/downloads[JDK8 build]. +https://help.github.com/set-up-git-redirect[Git] and the https://www.oracle.com/technetwork/java/javase/downloads[JDK11 build]. -Be sure that your `JAVA_HOME` environment variable points to the `jdk1.8.0` folder extracted from the JDK download. +Be sure that your `JAVA_HOME` environment variable points to the `jdk-11` folder extracted from the JDK download. === Check out sources [indent=0] diff --git a/acl/spring-security-acl.gradle b/acl/spring-security-acl.gradle index 53b4864c38e..27dc016aaf5 100644 --- a/acl/spring-security-acl.gradle +++ b/acl/spring-security-acl.gradle @@ -1,18 +1,19 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-tx' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'org.springframework:spring-aop' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-jdbc' + api 'org.springframework:spring-tx' optional 'net.sf.ehcache:ehcache' - testCompile 'org.springframework:spring-beans' - testCompile 'org.springframework:spring-context-support' - testCompile 'org.springframework:spring-test' + testImplementation 'org.springframework:spring-beans' + testImplementation 'org.springframework:spring-context-support' + testImplementation 'org.springframework:spring-test' - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' } diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java b/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java index 34e62babb5a..4753f973f41 100644 --- a/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java +++ b/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java @@ -93,11 +93,17 @@ public void securityCheck(Acl acl, int changeType) { && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) { return; } - // Not authorized by ACL ownership; try via adminstrative permissions - GrantedAuthority requiredAuthority = getRequiredAuthority(changeType); // Iterate this principal's authorities to determine right Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities()); + if (acl.getOwner() instanceof GrantedAuthoritySid + && authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) { + return; + } + + // Not authorized by ACL ownership; try via adminstrative permissions + GrantedAuthority requiredAuthority = getRequiredAuthority(changeType); + if (authorities.contains(requiredAuthority.getAuthority())) { return; } diff --git a/acl/src/test/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImplTests.java b/acl/src/test/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImplTests.java index e1b06b74180..992a036569e 100644 --- a/acl/src/test/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImplTests.java +++ b/acl/src/test/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImplTests.java @@ -31,6 +31,8 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; +import static org.mockito.BDDMockito.given; + /** * @author Rob Winch * @@ -66,6 +68,14 @@ public void securityCheckWhenCustomAuthorityThenNameIsUsed() { this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL); } + // gh-9425 + @Test + public void securityCheckWhenAclOwnedByGrantedAuthority() { + given(this.acl.getOwner()).willReturn(new GrantedAuthoritySid("ROLE_AUTH")); + this.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_SYSTEM_ADMIN")); + this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL); + } + @SuppressWarnings("serial") class CustomAuthority implements GrantedAuthority { diff --git a/aspects/spring-security-aspects.gradle b/aspects/spring-security-aspects.gradle index 6b50ef72350..d66ebe447c1 100644 --- a/aspects/spring-security-aspects.gradle +++ b/aspects/spring-security-aspects.gradle @@ -2,13 +2,14 @@ apply plugin: 'io.spring.convention.spring-module' apply plugin: 'io.freefair.aspectj' dependencies { - compile "org.aspectj:aspectjrt" - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' + management platform(project(":spring-security-dependencies")) + api "org.aspectj:aspectjrt" + api project(':spring-security-core') + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' - testCompile 'org.springframework:spring-aop' + testImplementation 'org.springframework:spring-aop' testAspect sourceSets.main.output } diff --git a/build.gradle b/build.gradle index ea5a5b6c893..d7287fe39fd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,8 @@ buildscript { dependencies { - classpath 'io.spring.gradle:spring-build-conventions:0.0.36' classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion" - classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" classpath 'io.spring.nohttp:nohttp-gradle:0.0.5.RELEASE' - classpath "io.freefair.gradle:aspectj-plugin:5.0.1" + classpath "io.freefair.gradle:aspectj-plugin:5.3.3.3" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } repositories { @@ -17,6 +15,7 @@ apply plugin: 'io.spring.nohttp' apply plugin: 'locks' apply plugin: 'io.spring.convention.root' apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'org.springframework.security.update-dependencies' group = 'org.springframework.security' description = 'Spring Security' @@ -25,12 +24,58 @@ ext.snapshotBuild = version.contains("SNAPSHOT") ext.releaseBuild = version.contains("SNAPSHOT") ext.milestoneBuild = !(snapshotBuild || releaseBuild) -dependencyManagementExport.projects = subprojects.findAll { !it.name.contains('-boot') } - repositories { mavenCentral() } +updateDependenciesSettings { + gitHub { + organization = "spring-projects" + repository = "spring-security" + } + addFiles({ + return [ + project.file("buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java"), + project.file("buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy") + ] + }) + dependencyExcludes { + majorVersionBump() + alphaBetaVersions() + releaseCandidatesVersions() + milestoneVersions() + snapshotVersions() + addRule { components -> + components.withModule("commons-codec:commons-codec") { selection -> + ModuleComponentIdentifier candidate = selection.getCandidate(); + if (!candidate.getVersion().equals(selection.getCurrentVersion())) { + selection.reject("commons-codec updates break saml tests"); + } + } + components.withModule("org.python:jython") { selection -> + ModuleComponentIdentifier candidate = selection.getCandidate(); + if (!candidate.getVersion().equals(selection.getCurrentVersion())) { + selection.reject("jython updates break integration tests"); + } + } + components.withModule("com.nimbusds:nimbus-jose-jwt") { selection -> + ModuleComponentIdentifier candidate = selection.getCandidate(); + if (!candidate.getVersion().equals(selection.getCurrentVersion())) { + selection.reject("nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency"); + } + } + components.all { selection -> + ModuleComponentIdentifier candidate = selection.getCandidate(); + // Do not compare version due to multiple versions existing + // will cause opensaml 3.x to be updated to 4.x + if (candidate.getGroup().equals("org.opensaml")) { + selection.reject("org.opensaml maintains two different versions, so it must be updated manually"); + } + } + } + } +} + subprojects { plugins.withType(JavaPlugin) { project.sourceCompatibility='1.8' @@ -40,6 +85,7 @@ subprojects { } } + allprojects { if (!['spring-security-bom', 'spring-security-docs'].contains(project.name)) { apply plugin: 'io.spring.javaformat' @@ -75,4 +121,6 @@ if (hasProperty('buildScan')) { nohttp { allowlistFile = project.file("etc/nohttp/allowlist.lines") + source.exclude "buildSrc/build/**" + } diff --git a/buildSrc/.idea/compiler.xml b/buildSrc/.idea/compiler.xml new file mode 100644 index 00000000000..61a9130cd96 --- /dev/null +++ b/buildSrc/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="1.8" /> + </component> +</project> \ No newline at end of file diff --git a/buildSrc/.idea/gradle.xml b/buildSrc/.idea/gradle.xml new file mode 100644 index 00000000000..5c59556b93d --- /dev/null +++ b/buildSrc/.idea/gradle.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="GradleMigrationSettings" migrationVersion="1" /> + <component name="GradleSettings"> + <option name="linkedExternalProjectsSettings"> + <GradleProjectSettings> + <option name="distributionType" value="DEFAULT_WRAPPED" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="gradleHome" value="$USER_HOME$/.sdkman/candidates/gradle/current" /> + <option name="gradleJvm" value="11.0.9.hs-adpt" /> + <option name="modules"> + <set> + <option value="$PROJECT_DIR$" /> + </set> + </option> + </GradleProjectSettings> + </option> + </component> +</project> \ No newline at end of file diff --git a/buildSrc/.idea/jarRepositories.xml b/buildSrc/.idea/jarRepositories.xml new file mode 100644 index 00000000000..36cc8fcb9de --- /dev/null +++ b/buildSrc/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RemoteRepositoriesConfiguration"> + <remote-repository> + <option name="id" value="central" /> + <option name="name" value="Maven Central repository" /> + <option name="url" value="https://repo1.maven.org/maven2" /> + </remote-repository> + <remote-repository> + <option name="id" value="jboss.community" /> + <option name="name" value="JBoss Community repository" /> + <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenRepo" /> + <option name="name" value="MavenRepo" /> + <option name="url" value="https://repo.maven.apache.org/maven2/" /> + </remote-repository> + <remote-repository> + <option name="id" value="Gradle Central Plugin Repository" /> + <option name="name" value="Gradle Central Plugin Repository" /> + <option name="url" value="https://plugins.gradle.org/m2" /> + </remote-repository> + <remote-repository> + <option name="id" value="BintrayJCenter" /> + <option name="name" value="BintrayJCenter" /> + <option name="url" value="https://jcenter.bintray.com/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://repo.spring.io/plugins-release/" /> + </remote-repository> + </component> +</project> \ No newline at end of file diff --git a/buildSrc/.idea/misc.xml b/buildSrc/.idea/misc.xml new file mode 100644 index 00000000000..3a9d81e2a8f --- /dev/null +++ b/buildSrc/.idea/misc.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ExternalStorageConfigurationManager" enabled="true" /> + <component name="FrameworkDetectionExcludesConfiguration"> + <file type="web" url="file://$PROJECT_DIR$" /> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="11" project-jdk-type="JavaSDK" /> +</project> \ No newline at end of file diff --git a/buildSrc/.idea/uiDesigner.xml b/buildSrc/.idea/uiDesigner.xml new file mode 100644 index 00000000000..e96534fb27b --- /dev/null +++ b/buildSrc/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project> \ No newline at end of file diff --git a/buildSrc/.idea/workspace.xml b/buildSrc/.idea/workspace.xml new file mode 100644 index 00000000000..2ca122bc89f --- /dev/null +++ b/buildSrc/.idea/workspace.xml @@ -0,0 +1,206 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="AutoImportSettings"> + <option name="autoReloadType" value="SELECTIVE" /> + </component> + <component name="ChangeListManager"> + <list default="true" id="1650039e-90b3-4603-bb34-47f415f4e67a" name="Default Changelist" comment=""> + <change beforePath="$PROJECT_DIR$/gradlew" beforeDir="false" afterPath="$PROJECT_DIR$/gradlew" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/gradlew.bat" beforeDir="false" afterPath="$PROJECT_DIR$/gradlew.bat" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java" afterDir="false" /> + </list> + <option name="SHOW_DIALOG" value="false" /> + <option name="HIGHLIGHT_CONFLICTS" value="true" /> + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> + <option name="LAST_RESOLUTION" value="IGNORE" /> + </component> + <component name="ExternalProjectsData"> + <projectState path="$PROJECT_DIR$"> + <ProjectState /> + </projectState> + </component> + <component name="ExternalProjectsManager"> + <system id="GRADLE"> + <state> + <task path="$PROJECT_DIR$"> + <activation /> + </task> + <projects_view> + <tree_state> + <expand> + <path> + <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" /> + <item name="buildSrc" type="f1a62948:ProjectNode" /> + </path> + <path> + <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" /> + <item name="buildSrc" type="f1a62948:ProjectNode" /> + <item name="Run Configurations" type="7b0102dc:RunConfigurationsNode" /> + </path> + </expand> + <select /> + </tree_state> + </projects_view> + </state> + </system> + </component> + <component name="FileTemplateManagerImpl"> + <option name="RECENT_TEMPLATES"> + <list> + <option value="Class" /> + </list> + </option> + </component> + <component name="Git.Settings"> + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." /> + </component> + <component name="GitSEFilterConfiguration"> + <file-type-list> + <filtered-out-file-type name="LOCAL_BRANCH" /> + <filtered-out-file-type name="REMOTE_BRANCH" /> + <filtered-out-file-type name="TAG" /> + <filtered-out-file-type name="COMMIT_BY_MESSAGE" /> + </file-type-list> + </component> + <component name="ProjectId" id="1qXvSePBfcQC8Y4nOwtetSNoy4Q" /> + <component name="ProjectLevelVcsManager" settingsEditedManually="true" /> + <component name="ProjectViewState"> + <option name="hideEmptyMiddlePackages" value="true" /> + <option name="showLibraryContents" value="true" /> + </component> + <component name="PropertiesComponent"> + <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> + <property name="RunOnceActivity.ShowReadmeOnStart" value="true" /> + <property name="WebServerToolWindowFactoryState" value="false" /> + <property name="aspect.path.notification.shown" value="true" /> + <property name="last_opened_file_path" value="$PROJECT_DIR$" /> + <property name="node.js.detected.package.eslint" value="true" /> + <property name="node.js.detected.package.tslint" value="true" /> + <property name="node.js.path.for.package.eslint" value="project" /> + <property name="node.js.path.for.package.tslint" value="project" /> + <property name="node.js.selected.package.eslint" value="(autodetect)" /> + <property name="node.js.selected.package.tslint" value="(autodetect)" /> + </component> + <component name="ReactorSettings"> + <option name="notificationShown" value="true" /> + </component> + <component name="RecentsManager"> + <key name="MoveClassesOrPackagesDialog.RECENTS_KEY"> + <recent name="org.springframework.security.convention" /> + </key> + </component> + <component name="RunManager" selected="Gradle.DocsPluginITest.build triggers docs"> + <configuration default="true" type="ArquillianJUnit" factoryName="" nameIsGenerated="true"> + <option name="arquillianRunConfiguration"> + <value> + <option name="containerStateName" value="" /> + </value> + </option> + <option name="TEST_OBJECT" value="class" /> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> + <configuration name="DocsPluginITest.build triggers docs" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="--tests "io.spring.gradle.convention.DocsPluginITest.build triggers docs"" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value=":test" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> + <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> + <DebugAllEnabled>false</DebugAllEnabled> + <method v="2" /> + </configuration> + <configuration name="GitHubApiTests.findCreateIssueInput" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="--tests "org.springframework.security.convention.versions.GitHubApiTests.findCreateIssueInput"" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value=":test" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> + <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> + <DebugAllEnabled>false</DebugAllEnabled> + <method v="2" /> + </configuration> + <configuration name="GitHubApiTests.findRepositoryId" type="GradleRunConfiguration" factoryName="Gradle" temporary="true"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="--tests "org.springframework.security.convention.versions.GitHubApiTests.findRepositoryId"" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value=":test" /> + </list> + </option> + <option name="vmOptions" /> + </ExternalSystemSettings> + <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess> + <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> + <DebugAllEnabled>false</DebugAllEnabled> + <method v="2" /> + </configuration> + <recent_temporary> + <list> + <item itemvalue="Gradle.DocsPluginITest.build triggers docs" /> + <item itemvalue="Gradle.GitHubApiTests.findCreateIssueInput" /> + <item itemvalue="Gradle.GitHubApiTests.findRepositoryId" /> + </list> + </recent_temporary> + </component> + <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> + <component name="TaskManager"> + <task active="true" id="Default" summary="Default task"> + <changelist id="1650039e-90b3-4603-bb34-47f415f4e67a" name="Default Changelist" comment="" /> + <created>1617238749852</created> + <option name="number" value="Default" /> + <option name="presentableId" value="Default" /> + <updated>1617238749852</updated> + <workItem from="1617238751049" duration="982000" /> + <workItem from="1617239768987" duration="22510000" /> + <workItem from="1617314253813" duration="9753000" /> + <workItem from="1617372923830" duration="9278000" /> + </task> + <servers /> + </component> + <component name="TypeScriptGeneratedFilesManager"> + <option name="version" value="3" /> + </component> + <component name="Vcs.Log.Tabs.Properties"> + <option name="TAB_STATES"> + <map> + <entry key="MAIN"> + <value> + <State /> + </value> + </entry> + </map> + </option> + <option name="oldMeFiltersMigrated" value="true" /> + </component> +</project> \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index d3eeb801df4..f1472f53b06 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,7 +1,18 @@ -apply plugin: "java-gradle-plugin" +plugins { + id "java-gradle-plugin" + id "java" + id "groovy" + id 'com.apollographql.apollo' version '2.4.5' +} + + +sourceCompatibility = 1.8 repositories { + jcenter() + gradlePluginPortal() mavenCentral() + maven { url 'https://repo.spring.io/plugins-release/' } } gradlePlugin { @@ -14,15 +25,49 @@ gradlePlugin { id = "locks" implementationClass = "lock.GlobalLockPlugin" } + managementConfiguration { + id = "io.spring.convention.management-configuration" + implementationClass = "io.spring.gradle.convention.ManagementConfigurationPlugin" + } + updateDependencies { + id = "org.springframework.security.update-dependencies" + implementationClass = "org.springframework.security.convention.versions.UpdateDependenciesPlugin" + } } } -dependencies { - compile 'com.thaiopensource:trang:20091111' - compile 'net.sourceforge.saxon:saxon:9.1.0.8' +configurations { + implementation { + exclude module: 'groovy-all' + } } -task ide(type: Copy) { - from configurations.runtime - into 'ide' +dependencies { + implementation 'com.thaiopensource:trang:20091111' + implementation 'net.sourceforge.saxon:saxon:9.1.0.8' + implementation localGroovy() + + implementation 'io.projectreactor:reactor-core:3.4.4' + implementation 'gradle.plugin.org.gretty:gretty:3.0.1' + implementation 'com.apollographql.apollo:apollo-runtime:2.4.5' + implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0' + implementation 'io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.21.1' + implementation 'io.spring.gradle:docbook-reference-plugin:0.3.1' + implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15' + implementation 'io.spring.nohttp:nohttp-gradle:0.0.5.RELEASE' + implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0' + implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0' + implementation 'org.hidetake:gradle-ssh-plugin:2.10.1' + implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.9.10' + implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' + + testImplementation 'junit:junit:4.12' + testImplementation 'org.apache.commons:commons-io:1.3.2' + testImplementation 'org.assertj:assertj-core:3.13.2' + testImplementation 'org.mockito:mockito-core:3.0.0' + testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5' } + + +test.onlyIf { !project.hasProperty("buildSrc.skipTests") } diff --git a/buildSrc/gradle/wrapper/gradle-wrapper.jar b/buildSrc/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..e708b1c023e Binary files /dev/null and b/buildSrc/gradle/wrapper/gradle-wrapper.jar differ diff --git a/buildSrc/gradle/wrapper/gradle-wrapper.properties b/buildSrc/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..2a563242c11 --- /dev/null +++ b/buildSrc/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/buildSrc/src/main/graphql/org/springframework/security/convention/versions/CreateIssue.graphql b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/CreateIssue.graphql new file mode 100644 index 00000000000..b86498bfe02 --- /dev/null +++ b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/CreateIssue.graphql @@ -0,0 +1,7 @@ +mutation CreateIssueInput($assigneeId: ID!, $labelIds: [ID!], $milestoneId: ID!, $repositoryId: ID!, $title: String!) { + createIssue(input: {assigneeIds: [$assigneeId], labelIds: $labelIds, milestoneId: $milestoneId, projectIds: [], repositoryId: $repositoryId, title: $title}) { + issue { + number + } + } +} diff --git a/buildSrc/src/main/graphql/org/springframework/security/convention/versions/FindCreateIssueInput.graphql b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/FindCreateIssueInput.graphql new file mode 100644 index 00000000000..275fa19b341 --- /dev/null +++ b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/FindCreateIssueInput.graphql @@ -0,0 +1,20 @@ +query FindCreateIssueInput($owner: String!, $name: String!, $labelQuery: String, $milestoneName: String) { + repository(owner: $owner, name: $name) { + id + labels(query: $labelQuery, first: 1) { + nodes { + id + name + } + } + milestones(query: $milestoneName, states: [OPEN], first: 1) { + nodes { + id + title + } + } + } + viewer { + id + } +} diff --git a/buildSrc/src/main/graphql/org/springframework/security/convention/versions/RateLimit.graphql b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/RateLimit.graphql new file mode 100644 index 00000000000..a9676394629 --- /dev/null +++ b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/RateLimit.graphql @@ -0,0 +1,8 @@ +query RateLimit { + rateLimit { + limit + cost + remaining + resetAt + } +} diff --git a/buildSrc/src/main/graphql/org/springframework/security/convention/versions/schema.json b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/schema.json new file mode 100644 index 00000000000..f04bf4e8b81 --- /dev/null +++ b/buildSrc/src/main/graphql/org/springframework/security/convention/versions/schema.json @@ -0,0 +1 @@ +{"data":{"__schema":{"queryType":{"name":"Query"},"mutationType":{"name":"Mutation"},"subscriptionType":null,"types":[{"kind":"INPUT_OBJECT","name":"AcceptEnterpriseAdministratorInvitationInput","description":"Autogenerated input type of AcceptEnterpriseAdministratorInvitation","fields":null,"inputFields":[{"name":"invitationId","description":"The id of the invitation being accepted","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AcceptEnterpriseAdministratorInvitationPayload","description":"Autogenerated return type of AcceptEnterpriseAdministratorInvitation","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"invitation","description":"The invitation that was accepted.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of accepting an administrator invitation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AcceptTopicSuggestionInput","description":"Autogenerated input type of AcceptTopicSuggestion","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the suggested topic.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AcceptTopicSuggestionPayload","description":"Autogenerated return type of AcceptTopicSuggestion","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topic","description":"The accepted topic.","args":[],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Actor","description":"Represents an object which can take actions on GitHub. Typically a User or Bot.","fields":[{"name":"avatarUrl","description":"A URL pointing to the actor's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username of the actor.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this actor.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this actor.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Bot","ofType":null},{"kind":"OBJECT","name":"EnterpriseUserAccount","ofType":null},{"kind":"OBJECT","name":"Mannequin","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"ActorLocation","description":"Location information for an actor","fields":[{"name":"city","description":"City","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"country","description":"Country name","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"countryCode","description":"Country code","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"region","description":"Region name","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"regionCode","description":"Region or state code","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddAssigneesToAssignableInput","description":"Autogenerated input type of AddAssigneesToAssignable","fields":null,"inputFields":[{"name":"assignableId","description":"The id of the assignable object to add assignees to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"assigneeIds","description":"The id of users to add as assignees.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddAssigneesToAssignablePayload","description":"Autogenerated return type of AddAssigneesToAssignable","fields":[{"name":"assignable","description":"The item that was assigned.","args":[],"type":{"kind":"INTERFACE","name":"Assignable","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddCommentInput","description":"Autogenerated input type of AddComment","fields":null,"inputFields":[{"name":"subjectId","description":"The Node ID of the subject to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The contents of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddCommentPayload","description":"Autogenerated return type of AddComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commentEdge","description":"The edge from the subject's comment connection.","args":[],"type":{"kind":"OBJECT","name":"IssueCommentEdge","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"The subject","args":[],"type":{"kind":"INTERFACE","name":"Node","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"timelineEdge","description":"The edge from the subject's timeline connection.","args":[],"type":{"kind":"OBJECT","name":"IssueTimelineItemEdge","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddEnterpriseSupportEntitlementInput","description":"Autogenerated input type of AddEnterpriseSupportEntitlement","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the Enterprise which the admin belongs to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"login","description":"The login of a member who will receive the support entitlement.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddEnterpriseSupportEntitlementPayload","description":"Autogenerated return type of AddEnterpriseSupportEntitlement","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of adding the support entitlement.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddLabelsToLabelableInput","description":"Autogenerated input type of AddLabelsToLabelable","fields":null,"inputFields":[{"name":"labelableId","description":"The id of the labelable object to add labels to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"labelIds","description":"The ids of the labels to add.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddLabelsToLabelablePayload","description":"Autogenerated return type of AddLabelsToLabelable","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labelable","description":"The item that was labeled.","args":[],"type":{"kind":"INTERFACE","name":"Labelable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddProjectCardInput","description":"Autogenerated input type of AddProjectCard","fields":null,"inputFields":[{"name":"projectColumnId","description":"The Node ID of the ProjectColumn.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"contentId","description":"The content of the card. Must be a member of the ProjectCardItem union","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"note","description":"The note on the card.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddProjectCardPayload","description":"Autogenerated return type of AddProjectCard","fields":[{"name":"cardEdge","description":"The edge from the ProjectColumn's card connection.","args":[],"type":{"kind":"OBJECT","name":"ProjectCardEdge","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projectColumn","description":"The ProjectColumn","args":[],"type":{"kind":"OBJECT","name":"ProjectColumn","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddProjectColumnInput","description":"Autogenerated input type of AddProjectColumn","fields":null,"inputFields":[{"name":"projectId","description":"The Node ID of the project.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the column.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddProjectColumnPayload","description":"Autogenerated return type of AddProjectColumn","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"columnEdge","description":"The edge from the project's column connection.","args":[],"type":{"kind":"OBJECT","name":"ProjectColumnEdge","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The project","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewCommentInput","description":"Autogenerated input type of AddPullRequestReviewComment","fields":null,"inputFields":[{"name":"pullRequestId","description":"The node ID of the pull request reviewing","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestReviewId","description":"The Node ID of the review to modify.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"commitOID","description":"The SHA of the commit to comment on.","type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"defaultValue":null},{"name":"body","description":"The text of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"path","description":"The relative path of the file to comment on.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"position","description":"The line index in the diff to comment on.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"inReplyTo","description":"The comment id to reply to.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddPullRequestReviewCommentPayload","description":"Autogenerated return type of AddPullRequestReviewComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"comment","description":"The newly created comment.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commentEdge","description":"The edge from the review's comment connection.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewCommentEdge","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewInput","description":"Autogenerated input type of AddPullRequestReview","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Node ID of the pull request to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"commitOID","description":"The commit OID the review pertains to.","type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"defaultValue":null},{"name":"body","description":"The contents of the review body comment.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"event","description":"The event to perform on the pull request review.","type":{"kind":"ENUM","name":"PullRequestReviewEvent","ofType":null},"defaultValue":null},{"name":"comments","description":"The review line comments.","type":{"kind":"LIST","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DraftPullRequestReviewComment","ofType":null}},"defaultValue":null},{"name":"threads","description":"The review line comment threads.","type":{"kind":"LIST","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DraftPullRequestReviewThread","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddPullRequestReviewPayload","description":"Autogenerated return type of AddPullRequestReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The newly created pull request review.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reviewEdge","description":"The edge from the pull request's review connection.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewEdge","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewThreadInput","description":"Autogenerated input type of AddPullRequestReviewThread","fields":null,"inputFields":[{"name":"path","description":"Path to the file being commented on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"Body of the thread's first comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"pullRequestId","description":"The node ID of the pull request reviewing","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestReviewId","description":"The Node ID of the review to modify.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"line","description":"The line of the blob to which the thread refers. The end of the line range for multi-line comments.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"side","description":"The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.","type":{"kind":"ENUM","name":"DiffSide","ofType":null},"defaultValue":"RIGHT"},{"name":"startLine","description":"The first line of the range to which the comment refers.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"startSide","description":"The side of the diff on which the start line resides.","type":{"kind":"ENUM","name":"DiffSide","ofType":null},"defaultValue":"RIGHT"},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddPullRequestReviewThreadPayload","description":"Autogenerated return type of AddPullRequestReviewThread","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"thread","description":"The newly created thread.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddReactionInput","description":"Autogenerated input type of AddReaction","fields":null,"inputFields":[{"name":"subjectId","description":"The Node ID of the subject to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"content","description":"The name of the emoji to react with.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReactionContent","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddReactionPayload","description":"Autogenerated return type of AddReaction","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reaction","description":"The reaction object.","args":[],"type":{"kind":"OBJECT","name":"Reaction","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"The reactable subject.","args":[],"type":{"kind":"INTERFACE","name":"Reactable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddStarInput","description":"Autogenerated input type of AddStar","fields":null,"inputFields":[{"name":"starrableId","description":"The Starrable ID to star.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddStarPayload","description":"Autogenerated return type of AddStar","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"starrable","description":"The starrable.","args":[],"type":{"kind":"INTERFACE","name":"Starrable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"AddVerifiableDomainInput","description":"Autogenerated input type of AddVerifiableDomain","fields":null,"inputFields":[{"name":"ownerId","description":"The ID of the owner to add the domain to","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"domain","description":"The URL of the domain","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddVerifiableDomainPayload","description":"Autogenerated return type of AddVerifiableDomain","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"domain","description":"The verifiable domain that was added.","args":[],"type":{"kind":"OBJECT","name":"VerifiableDomain","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AddedToProjectEvent","description":"Represents a 'added_to_project' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"App","description":"A GitHub App.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the app.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"logoBackgroundColor","description":"The hex color code, without the leading '#', for the logo background.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"logoUrl","description":"A URL pointing to the app's logo.","args":[{"name":"size","description":"The size of the resulting image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the app.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"A slug based on the name of the app for use in URLs.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The URL to the app's homepage.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ApproveVerifiableDomainInput","description":"Autogenerated input type of ApproveVerifiableDomain","fields":null,"inputFields":[{"name":"id","description":"The ID of the verifiable domain to approve.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ApproveVerifiableDomainPayload","description":"Autogenerated return type of ApproveVerifiableDomain","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"domain","description":"The verifiable domain that was approved.","args":[],"type":{"kind":"OBJECT","name":"VerifiableDomain","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ArchiveRepositoryInput","description":"Autogenerated input type of ArchiveRepository","fields":null,"inputFields":[{"name":"repositoryId","description":"The ID of the repository to mark as archived.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ArchiveRepositoryPayload","description":"Autogenerated return type of ArchiveRepository","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that was marked as archived.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Assignable","description":"An object that can have users assigned to it.","fields":[{"name":"assignees","description":"A list of Users assigned to this object.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"OBJECT","name":"AssignedEvent","description":"Represents an 'assigned' event on any assignable object.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"assignable","description":"Identifies the assignable associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Assignable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"assignee","description":"Identifies the user or mannequin that was assigned.","args":[],"type":{"kind":"UNION","name":"Assignee","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"Identifies the user who was assigned.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":true,"deprecationReason":"Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC."}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"Assignee","description":"Types that can be assigned to issues.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Bot","ofType":null},{"kind":"OBJECT","name":"Mannequin","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"INTERFACE","name":"AuditEntry","description":"An entry in the audit log.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgBlockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveOutsideCollaboratorAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUnblockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateDefaultRepositoryPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null}]},{"kind":"UNION","name":"AuditEntryActor","description":"Types that can initiate an audit log event.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Bot","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"INPUT_OBJECT","name":"AuditLogOrder","description":"Ordering options for Audit Log connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order Audit Logs by.","type":{"kind":"ENUM","name":"AuditLogOrderField","ofType":null},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"ENUM","name":"OrderDirection","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"AuditLogOrderField","description":"Properties by which Audit Log connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order audit log entries by timestamp","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"AutoMergeDisabledEvent","description":"Represents a 'auto_merge_disabled' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"disabler","description":"The user who disabled auto-merge for this Pull Request","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reason","description":"The reason auto-merge was disabled","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reasonCode","description":"The reason_code relating to why auto-merge was disabled","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutoMergeEnabledEvent","description":"Represents a 'auto_merge_enabled' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enabler","description":"The user who enabled auto-merge for this Pull Request","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutoMergeRequest","description":"Represents an auto-merge request for a pull request","fields":[{"name":"authorEmail","description":"The email address of the author of this auto-merge request.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commitBody","description":"The commit message of the auto-merge request.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commitHeadline","description":"The commit title of the auto-merge request.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enabledAt","description":"When was this auto-merge request was enabled.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enabledBy","description":"The actor who created the auto-merge request.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeMethod","description":"The merge method of the auto-merge request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that this auto-merge request is set against.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutoRebaseEnabledEvent","description":"Represents a 'auto_rebase_enabled' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enabler","description":"The user who enabled auto-merge (rebase) for this Pull Request","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutoSquashEnabledEvent","description":"Represents a 'auto_squash_enabled' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enabler","description":"The user who enabled auto-merge (squash) for this Pull Request","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutomaticBaseChangeFailedEvent","description":"Represents a 'automatic_base_change_failed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"newBase","description":"The new base for this PR","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oldBase","description":"The old base for this PR","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"AutomaticBaseChangeSucceededEvent","description":"Represents a 'automatic_base_change_succeeded' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"newBase","description":"The new base for this PR","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oldBase","description":"The old base for this PR","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BaseRefChangedEvent","description":"Represents a 'base_ref_changed' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"currentRefName","description":"Identifies the name of the base ref for the pull request after it was changed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"previousRefName","description":"Identifies the name of the base ref for the pull request before it was changed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BaseRefDeletedEvent","description":"Represents a 'base_ref_deleted' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"baseRefName","description":"Identifies the name of the Ref associated with the `base_ref_deleted` event.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BaseRefForcePushedEvent","description":"Represents a 'base_ref_force_pushed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"afterCommit","description":"Identifies the after commit SHA for the 'base_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"beforeCommit","description":"Identifies the before commit SHA for the 'base_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"Identifies the fully qualified ref name for the 'base_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Blame","description":"Represents a Git blame.","fields":[{"name":"ranges","description":"The list of ranges from a Git blame.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"BlameRange","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BlameRange","description":"Represents a range of information from a Git blame.","fields":[{"name":"age","description":"Identifies the recency of the change, from 1 (new) to 10 (old). This is calculated as a 2-quantile and determines the length of distance between the median age of all the changes in the file and the recency of the current range's change.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the line author","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"endingLine","description":"The ending line for the range","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"startingLine","description":"The starting line for the range","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Blob","description":"Represents a Git blob.","fields":[{"name":"abbreviatedOid","description":"An abbreviated version of the Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"byteSize","description":"Byte size of Blob object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitResourcePath","description":"The HTTP path for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitUrl","description":"The HTTP URL for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isBinary","description":"Indicates whether the Blob is binary or text. Returns null if unable to determine the encoding.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isTruncated","description":"Indicates whether the contents is truncated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"The Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the Git object belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"text","description":"UTF8 text data or null if the Blob is binary","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"GitObject","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Boolean","description":"Represents `true` or `false` values.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Bot","description":"A special type of user which takes actions on behalf of GitHub Apps.","fields":[{"name":"avatarUrl","description":"A URL pointing to the GitHub App's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username of the actor.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this bot","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this bot","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Actor","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRule","description":"A branch protection rule.","fields":[{"name":"allowsDeletions","description":"Can this branch be deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"allowsForcePushes","description":"Are force pushes allowed on this branch.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"branchProtectionRuleConflicts","description":"A list of conflicts matching branches protection rule and other branch protection rules","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRuleConflictConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"The actor who created this branch protection rule.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismissesStaleReviews","description":"Will new commits pushed to matching branches dismiss pull request review approvals.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isAdminEnforced","description":"Can admins overwrite branch protection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"matchingRefs","description":"Repository refs that are protected by this rule","args":[{"name":"query","description":"Filters refs with query on name","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RefConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pattern","description":"Identifies the protection rule pattern.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pushAllowances","description":"A list push allowances for this branch protection rule.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PushAllowanceConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this branch protection rule.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requiredApprovingReviewCount","description":"Number of approving reviews required to update matching branches.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requiredStatusCheckContexts","description":"List of required status check contexts that must pass for commits to be accepted to matching branches.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresApprovingReviews","description":"Are approving reviews required to update matching branches.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresCodeOwnerReviews","description":"Are reviews from code owners required to update matching branches.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresCommitSignatures","description":"Are commits required to be signed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresLinearHistory","description":"Are merge commits prohibited from being pushed to this branch.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresStatusChecks","description":"Are status checks required to update matching branches.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresStrictStatusChecks","description":"Are branches required to be up to date before merging.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"restrictsPushes","description":"Is pushing to matching branches restricted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"restrictsReviewDismissals","description":"Is dismissal of pull request reviews restricted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reviewDismissalAllowances","description":"A list review dismissal allowances for this branch protection rule.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReviewDismissalAllowanceConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRuleConflict","description":"A conflict between two branch protection rules.","fields":[{"name":"branchProtectionRule","description":"Identifies the branch protection rule.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"conflictingBranchProtectionRule","description":"Identifies the conflicting branch protection rule.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"Identifies the branch ref that has conflicting rules","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRuleConflictConnection","description":"The connection type for BranchProtectionRuleConflict.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRuleConflictEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRuleConflict","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRuleConflictEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRuleConflict","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRuleConnection","description":"The connection type for BranchProtectionRule.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRuleEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"BranchProtectionRuleEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CVSS","description":"The Common Vulnerability Scoring System","fields":[{"name":"score","description":"The CVSS score associated with this advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"vectorString","description":"The CVSS vector string associated with this advisory","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CWE","description":"A common weakness enumeration","fields":[{"name":"cweId","description":"The id of the CWE","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"A detailed description of this CWE","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":"ID of the object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of this CWE","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CWEConnection","description":"The connection type for CWE.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CWEEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CWE","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CWEEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CWE","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CancelEnterpriseAdminInvitationInput","description":"Autogenerated input type of CancelEnterpriseAdminInvitation","fields":null,"inputFields":[{"name":"invitationId","description":"The Node ID of the pending enterprise administrator invitation.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CancelEnterpriseAdminInvitationPayload","description":"Autogenerated return type of CancelEnterpriseAdminInvitation","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"invitation","description":"The invitation that was canceled.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of canceling an administrator invitation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ChangeUserStatusInput","description":"Autogenerated input type of ChangeUserStatus","fields":null,"inputFields":[{"name":"emoji","description":"The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"message","description":"A short description of your current status.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"organizationId","description":"The ID of the organization whose members will be allowed to see the status. If omitted, the status will be publicly visible.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"limitedAvailability","description":"Whether this status should indicate you are not fully available on GitHub, e.g., you are away.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"expiresAt","description":"If set, the user status will not be shown after this date.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ChangeUserStatusPayload","description":"Autogenerated return type of ChangeUserStatus","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"Your updated status.","args":[],"type":{"kind":"OBJECT","name":"UserStatus","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckAnnotation","description":"A single check annotation.","fields":[{"name":"annotationLevel","description":"The annotation's severity level.","args":[],"type":{"kind":"ENUM","name":"CheckAnnotationLevel","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blobUrl","description":"The path to the file that this annotation was made on.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"location","description":"The position of this annotation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CheckAnnotationSpan","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"The annotation's message.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The path that this annotation was made on.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"rawDetails","description":"Additional information about the annotation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"The annotation's title","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckAnnotationConnection","description":"The connection type for CheckAnnotation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckAnnotationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckAnnotation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckAnnotationData","description":"Information from a check run analysis to specific lines of code.","fields":null,"inputFields":[{"name":"path","description":"The path of the file to add an annotation to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"location","description":"The location of the annotation","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckAnnotationRange","ofType":null}},"defaultValue":null},{"name":"annotationLevel","description":"Represents an annotation's information level","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CheckAnnotationLevel","ofType":null}},"defaultValue":null},{"name":"message","description":"A short description of the feedback for these lines of code.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"title","description":"The title that represents the annotation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"rawDetails","description":"Details about this annotation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckAnnotationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CheckAnnotation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"CheckAnnotationLevel","description":"Represents an annotation's information level.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"FAILURE","description":"An annotation indicating an inescapable error.","isDeprecated":false,"deprecationReason":null},{"name":"NOTICE","description":"An annotation indicating some information.","isDeprecated":false,"deprecationReason":null},{"name":"WARNING","description":"An annotation indicating an ignorable error.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"CheckAnnotationPosition","description":"A character position in a check annotation.","fields":[{"name":"column","description":"Column number (1 indexed).","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"line","description":"Line number (1 indexed).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckAnnotationRange","description":"Information from a check run analysis to specific lines of code.","fields":null,"inputFields":[{"name":"startLine","description":"The starting line of the range.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"startColumn","description":"The starting column of the range.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"endLine","description":"The ending line of the range.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"endColumn","description":"The ending column of the range.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckAnnotationSpan","description":"An inclusive pair of positions for a check annotation.","fields":[{"name":"end","description":"End position (inclusive).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CheckAnnotationPosition","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"start","description":"Start position (inclusive).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CheckAnnotationPosition","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"CheckConclusionState","description":"The possible states for a check suite or run conclusion.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ACTION_REQUIRED","description":"The check suite or run requires action.","isDeprecated":false,"deprecationReason":null},{"name":"TIMED_OUT","description":"The check suite or run has timed out.","isDeprecated":false,"deprecationReason":null},{"name":"CANCELLED","description":"The check suite or run has been cancelled.","isDeprecated":false,"deprecationReason":null},{"name":"FAILURE","description":"The check suite or run has failed.","isDeprecated":false,"deprecationReason":null},{"name":"SUCCESS","description":"The check suite or run has succeeded.","isDeprecated":false,"deprecationReason":null},{"name":"NEUTRAL","description":"The check suite or run was neutral.","isDeprecated":false,"deprecationReason":null},{"name":"SKIPPED","description":"The check suite or run was skipped.","isDeprecated":false,"deprecationReason":null},{"name":"STARTUP_FAILURE","description":"The check suite or run has failed at startup.","isDeprecated":false,"deprecationReason":null},{"name":"STALE","description":"The check suite or run was marked stale by GitHub. Only GitHub can use this conclusion.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"CheckRun","description":"A check run.","fields":[{"name":"annotations","description":"The check run's annotations","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CheckAnnotationConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"checkSuite","description":"The check suite that this run is a part of.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CheckSuite","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"completedAt","description":"Identifies the date and time when the check run was completed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"conclusion","description":"The conclusion of the check run.","args":[],"type":{"kind":"ENUM","name":"CheckConclusionState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"detailsUrl","description":"The URL from which to find full details of the check run on the integrator's site.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"externalId","description":"A reference for the check run on the integrator's system.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRequired","description":"Whether this is required to pass before merging for a specific pull request.","args":[{"name":"pullRequestId","description":"The id of the pull request this is required for","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestNumber","description":"The number of the pull request this is required for","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the check for this check run.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permalink","description":"The permalink to the check run summary.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this check run.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this check run.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"startedAt","description":"Identifies the date and time when the check run was started.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"The current status of the check run.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CheckStatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"summary","description":"A string representing the check run's summary","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"text","description":"A string representing the check run's text","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"A string representing the check run","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this check run.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"RequirableByPullRequest","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckRunAction","description":"Possible further actions the integrator can perform.","fields":null,"inputFields":[{"name":"label","description":"The text to be displayed on a button in the web UI.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"description","description":"A short explanation of what this action would do.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"identifier","description":"A reference for the action on the integrator's system. ","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckRunConnection","description":"The connection type for CheckRun.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckRunEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckRun","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckRunEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CheckRun","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckRunFilter","description":"The filters that are available when fetching check runs.","fields":null,"inputFields":[{"name":"checkType","description":"Filters the check runs by this type.","type":{"kind":"ENUM","name":"CheckRunType","ofType":null},"defaultValue":null},{"name":"appId","description":"Filters the check runs created by this application ID.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"checkName","description":"Filters the check runs by this name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"status","description":"Filters the check runs by this status.","type":{"kind":"ENUM","name":"CheckStatusState","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckRunOutput","description":"Descriptive details about the check run.","fields":null,"inputFields":[{"name":"title","description":"A title to provide for this check run.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"summary","description":"The summary of the check run (supports Commonmark).","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"text","description":"The details of the check run (supports Commonmark).","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"annotations","description":"The annotations that are made as part of the check run.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckAnnotationData","ofType":null}}},"defaultValue":null},{"name":"images","description":"Images attached to the check run output displayed in the GitHub pull request UI.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckRunOutputImage","ofType":null}}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckRunOutputImage","description":"Images attached to the check run output displayed in the GitHub pull request UI.","fields":null,"inputFields":[{"name":"alt","description":"The alternative text for the image.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"imageUrl","description":"The full URL of the image.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"defaultValue":null},{"name":"caption","description":"A short image description.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"CheckRunType","description":"The possible types of check runs.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ALL","description":"Every check run available.","isDeprecated":false,"deprecationReason":null},{"name":"LATEST","description":"The latest check run.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"CheckStatusState","description":"The possible states for a check suite or run status.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUEUED","description":"The check suite or run has been queued.","isDeprecated":false,"deprecationReason":null},{"name":"IN_PROGRESS","description":"The check suite or run is in progress.","isDeprecated":false,"deprecationReason":null},{"name":"COMPLETED","description":"The check suite or run has been completed.","isDeprecated":false,"deprecationReason":null},{"name":"WAITING","description":"The check suite or run is in waiting state.","isDeprecated":false,"deprecationReason":null},{"name":"REQUESTED","description":"The check suite or run has been requested.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"CheckSuite","description":"A check suite.","fields":[{"name":"app","description":"The GitHub App which created this check suite.","args":[],"type":{"kind":"OBJECT","name":"App","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"branch","description":"The name of the branch for this check suite.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"checkRuns","description":"The check runs associated with a check suite.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"filterBy","description":"Filters the check runs by this type.","type":{"kind":"INPUT_OBJECT","name":"CheckRunFilter","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CheckRunConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"The commit for this check suite","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"conclusion","description":"The conclusion of this check suite.","args":[],"type":{"kind":"ENUM","name":"CheckConclusionState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"matchingPullRequests","description":"A list of open pull requests matching the check suite.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"push","description":"The push that triggered this check suite.","args":[],"type":{"kind":"OBJECT","name":"Push","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this check suite.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this check suite","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"The status of this check suite.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CheckStatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this check suite","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckSuiteAutoTriggerPreference","description":"The auto-trigger preferences that are available for check suites.","fields":null,"inputFields":[{"name":"appId","description":"The node ID of the application that owns the check suite.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"setting","description":"Set to `true` to enable automatic creation of CheckSuite events upon pushes to the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckSuiteConnection","description":"The connection type for CheckSuite.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckSuiteEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CheckSuite","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CheckSuiteEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CheckSuite","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CheckSuiteFilter","description":"The filters that are available when fetching check suites.","fields":null,"inputFields":[{"name":"appId","description":"Filters the check suites created by this application ID.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"checkName","description":"Filters the check suites by this name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ClearLabelsFromLabelableInput","description":"Autogenerated input type of ClearLabelsFromLabelable","fields":null,"inputFields":[{"name":"labelableId","description":"The id of the labelable object to clear the labels from.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ClearLabelsFromLabelablePayload","description":"Autogenerated return type of ClearLabelsFromLabelable","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labelable","description":"The item that was unlabeled.","args":[],"type":{"kind":"INTERFACE","name":"Labelable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CloneProjectInput","description":"Autogenerated input type of CloneProject","fields":null,"inputFields":[{"name":"targetOwnerId","description":"The owner ID to create the project under.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"sourceId","description":"The source project to clone.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"includeWorkflows","description":"Whether or not to clone the source project's workflows.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the project.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"The description of the project.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"public","description":"The visibility of the project, defaults to false (private).","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CloneProjectPayload","description":"Autogenerated return type of CloneProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"jobStatusId","description":"The id of the JobStatus for populating cloned fields.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The new cloned project.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CloneTemplateRepositoryInput","description":"Autogenerated input type of CloneTemplateRepository","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the template repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the new repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"ownerId","description":"The ID of the owner for the new repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"description","description":"A short description of the new repository.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"visibility","description":"Indicates the repository's visibility level.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryVisibility","ofType":null}},"defaultValue":null},{"name":"includeAllBranches","description":"Whether to copy all branches from the template to the new repository. Defaults to copying only the default branch of the template.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CloneTemplateRepositoryPayload","description":"Autogenerated return type of CloneTemplateRepository","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The new repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Closable","description":"An object that can be closed","fields":[{"name":"closed","description":"`true` if the object is closed (definition of closed may depend on type)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closedAt","description":"Identifies the date and time when the object was closed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"Milestone","ofType":null},{"kind":"OBJECT","name":"Project","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"INPUT_OBJECT","name":"CloseIssueInput","description":"Autogenerated input type of CloseIssue","fields":null,"inputFields":[{"name":"issueId","description":"ID of the issue to be closed.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CloseIssuePayload","description":"Autogenerated return type of CloseIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was closed.","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ClosePullRequestInput","description":"Autogenerated input type of ClosePullRequest","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to be closed.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ClosePullRequestPayload","description":"Autogenerated return type of ClosePullRequest","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that was closed.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ClosedEvent","description":"Represents a 'closed' event on any `Closable`.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"closable","description":"Object that was closed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Closable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closer","description":"Object which triggered the creation of this event.","args":[],"type":{"kind":"UNION","name":"Closer","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this closed event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this closed event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"Closer","description":"The object which triggered a `ClosedEvent`.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"OBJECT","name":"CodeOfConduct","description":"The Code of Conduct for a repository","fields":[{"name":"body","description":"The body of the Code of Conduct","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"The key for the Code of Conduct","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The formal name of the Code of Conduct","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this Code of Conduct","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this Code of Conduct","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"CollaboratorAffiliation","description":"Collaborators affiliation level with a subject.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OUTSIDE","description":"All outside collaborators of an organization-owned subject.","isDeprecated":false,"deprecationReason":null},{"name":"DIRECT","description":"All collaborators with permissions to an organization-owned subject, regardless of organization membership status.","isDeprecated":false,"deprecationReason":null},{"name":"ALL","description":"All collaborators the authenticated user can see.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"Comment","description":"Represents a comment.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The body as Markdown.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}]},{"kind":"ENUM","name":"CommentAuthorAssociation","description":"A comment author association with repository.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MEMBER","description":"Author is a member of the organization that owns the repository.","isDeprecated":false,"deprecationReason":null},{"name":"OWNER","description":"Author is the owner of the repository.","isDeprecated":false,"deprecationReason":null},{"name":"MANNEQUIN","description":"Author is a placeholder for an unclaimed user.","isDeprecated":false,"deprecationReason":null},{"name":"COLLABORATOR","description":"Author has been invited to collaborate on the repository.","isDeprecated":false,"deprecationReason":null},{"name":"CONTRIBUTOR","description":"Author has previously committed to the repository.","isDeprecated":false,"deprecationReason":null},{"name":"FIRST_TIME_CONTRIBUTOR","description":"Author has not previously committed to the repository.","isDeprecated":false,"deprecationReason":null},{"name":"FIRST_TIMER","description":"Author has not previously committed to GitHub.","isDeprecated":false,"deprecationReason":null},{"name":"NONE","description":"Author has no association with the repository.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"CommentCannotUpdateReason","description":"The possible errors that will prevent a user from updating a comment.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ARCHIVED","description":"Unable to create comment because repository is archived.","isDeprecated":false,"deprecationReason":null},{"name":"INSUFFICIENT_ACCESS","description":"You must be the author or have write access to this repository to update this comment.","isDeprecated":false,"deprecationReason":null},{"name":"LOCKED","description":"Unable to create comment because issue is locked.","isDeprecated":false,"deprecationReason":null},{"name":"LOGIN_REQUIRED","description":"You must be logged in to update this comment.","isDeprecated":false,"deprecationReason":null},{"name":"MAINTENANCE","description":"Repository is under maintenance.","isDeprecated":false,"deprecationReason":null},{"name":"VERIFIED_EMAIL_REQUIRED","description":"At least one email address must be verified to update this comment.","isDeprecated":false,"deprecationReason":null},{"name":"DENIED","description":"You cannot update this comment","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"CommentDeletedEvent","description":"Represents a 'comment_deleted' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletedCommentAuthor","description":"The user who authored the deleted comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Commit","description":"Represents a Git commit.","fields":[{"name":"abbreviatedOid","description":"An abbreviated version of the Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"additions","description":"The number of additions in this commit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"associatedPullRequests","description":"The pull requests associated with a commit","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests.","type":{"kind":"INPUT_OBJECT","name":"PullRequestOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"}],"type":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"author","description":"Authorship details of the commit.","args":[],"type":{"kind":"OBJECT","name":"GitActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authoredByCommitter","description":"Check if the committer and the author match.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"authoredDate","description":"The datetime when this commit was authored.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"authors","description":"The list of authors for this commit based on the git author and the Co-authored-by\nmessage trailer. The git author will always be first.\n","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GitActorConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"blame","description":"Fetches `git blame` information.","args":[{"name":"path","description":"The file whose Git blame information you want.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Blame","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"changedFiles","description":"The number of changed files in this commit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"checkSuites","description":"The check suites associated with a commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"filterBy","description":"Filters the check suites by this type.","type":{"kind":"INPUT_OBJECT","name":"CheckSuiteFilter","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CheckSuiteConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"comments","description":"Comments made on the commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitResourcePath","description":"The HTTP path for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitUrl","description":"The HTTP URL for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"committedDate","description":"The datetime when this commit was committed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"committedViaWeb","description":"Check if committed via GitHub web UI.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"committer","description":"Committer details of the commit.","args":[],"type":{"kind":"OBJECT","name":"GitActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletions","description":"The number of deletions in this commit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deployments","description":"The deployments associated with a commit.","args":[{"name":"environments","description":"Environments to list deployments for","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for deployments returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"DeploymentOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeploymentConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"file","description":"The tree entry representing the file located at the given path.","args":[{"name":"path","description":"The path for the file","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"TreeEntry","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"history","description":"The linear commit history starting from (and including) this commit, in the same order as `git log`.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"path","description":"If non-null, filters history to only show commits touching files under this path.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"author","description":"If non-null, filters history to only show commits with matching authorship.","type":{"kind":"INPUT_OBJECT","name":"CommitAuthor","ofType":null},"defaultValue":null},{"name":"since","description":"Allows specifying a beginning time or date for fetching commits.","type":{"kind":"SCALAR","name":"GitTimestamp","ofType":null},"defaultValue":null},{"name":"until","description":"Allows specifying an ending time or date for fetching commits.","type":{"kind":"SCALAR","name":"GitTimestamp","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitHistoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"The Git commit message","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"messageBody","description":"The Git commit message body","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"messageBodyHTML","description":"The commit message body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"messageHeadline","description":"The Git commit message headline","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"messageHeadlineHTML","description":"The commit message headline rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"The Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"onBehalfOf","description":"The organization this commit was made on behalf of.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parents","description":"The parents of a commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pushedDate","description":"The datetime when this commit was pushed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository this commit belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signature","description":"Commit signing information, if present.","args":[],"type":{"kind":"INTERFACE","name":"GitSignature","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"Status information for this commit","args":[],"type":{"kind":"OBJECT","name":"Status","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"statusCheckRollup","description":"Check and Status rollup information for this commit.","args":[],"type":{"kind":"OBJECT","name":"StatusCheckRollup","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"submodules","description":"Returns a list of all submodules in this repository as of this Commit parsed from the .gitmodules file.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SubmoduleConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tarballUrl","description":"Returns a URL to download a tarball archive for a repository.\nNote: For private repositories, these links are temporary and expire after five minutes.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tree","description":"Commit's root Tree","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Tree","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"treeResourcePath","description":"The HTTP path for the tree of this commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"treeUrl","description":"The HTTP URL for the tree of this commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"zipballUrl","description":"Returns a URL to download a zipball archive for a repository.\nNote: For private repositories, these links are temporary and expire after five minutes.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"GitObject","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CommitAuthor","description":"Specifies an author for filtering Git commits.","fields":null,"inputFields":[{"name":"id","description":"ID of a User to filter by. If non-null, only commits authored by this user will be returned. This field takes precedence over emails.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"emails","description":"Email addresses to filter by. Commits authored by any of the specified email addresses will be returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitComment","description":"Represents a comment on a given Commit.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"Identifies the comment body.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the commit associated with the comment, if the commit exists.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMinimized","description":"Returns whether or not a comment has been minimized.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedReason","description":"Returns why the comment was minimized.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"Identifies the file path associated with the comment.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"Identifies the line position associated with the comment.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path permalink for this commit comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL permalink for this commit comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanMinimize","description":"Check if the current viewer can minimize this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Minimizable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitCommentConnection","description":"The connection type for CommitComment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CommitComment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitCommentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CommitComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitCommentThread","description":"A thread of comments on a commit.","fields":[{"name":"comments","description":"The comments that exist in this thread.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"The commit the comments were made on.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The file the comments were made on.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"The position in the diff for the commit that the comment was made on.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitConnection","description":"The connection type for Commit.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CommitEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CommitContributionOrder","description":"Ordering options for commit contribution connections.","fields":null,"inputFields":[{"name":"field","description":"The field by which to order commit contributions.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommitContributionOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"CommitContributionOrderField","description":"Properties by which commit contribution connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OCCURRED_AT","description":"Order commit contributions by when they were made.","isDeprecated":false,"deprecationReason":null},{"name":"COMMIT_COUNT","description":"Order commit contributions by how many commits they represent.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"CommitContributionsByRepository","description":"This aggregates commits made by a user within one repository.","fields":[{"name":"contributions","description":"The commit contributions, each representing a day.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for commit contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"CommitContributionOrder","ofType":null},"defaultValue":"{field: OCCURRED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedCommitContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository in which the commits were made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for the user's commits to the repository in this time range.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for the user's commits to the repository in this time range.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CommitHistoryConnection","description":"The connection type for Commit.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CommitEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ConnectedEvent","description":"Represents a 'connected' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Reference originated in a different repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"source","description":"Issue or pull request that made the reference.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Issue or pull request which was connected.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Contribution","description":"Represents a contribution a user made on GitHub, such as opening an issue.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CreatedCommitContribution","ofType":null},{"kind":"OBJECT","name":"CreatedIssueContribution","ofType":null},{"kind":"OBJECT","name":"CreatedPullRequestContribution","ofType":null},{"kind":"OBJECT","name":"CreatedPullRequestReviewContribution","ofType":null},{"kind":"OBJECT","name":"CreatedRepositoryContribution","ofType":null},{"kind":"OBJECT","name":"JoinedGitHubContribution","ofType":null},{"kind":"OBJECT","name":"RestrictedContribution","ofType":null}]},{"kind":"OBJECT","name":"ContributionCalendar","description":"A calendar of contributions made on GitHub by a user.","fields":[{"name":"colors","description":"A list of hex color codes used in this calendar. The darker the color, the more contributions it represents.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"isHalloween","description":"Determine if the color set was chosen because it's currently Halloween.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"months","description":"A list of the months of contributions in this calendar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ContributionCalendarMonth","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"totalContributions","description":"The count of total contributions in the calendar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"weeks","description":"A list of the weeks of contributions in this calendar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ContributionCalendarWeek","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ContributionCalendarDay","description":"Represents a single day of contributions on GitHub by a user.","fields":[{"name":"color","description":"The hex color code that represents how many contributions were made on this day compared to others in the calendar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"contributionCount","description":"How many contributions were made by the user on this day.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"contributionLevel","description":"Indication of contributions, relative to other days. Can be used to indicate which color to represent this day on a calendar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ContributionLevel","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"date","description":"The day this square represents.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Date","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"weekday","description":"A number representing which day of the week this square represents, e.g., 1 is Monday.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ContributionCalendarMonth","description":"A month of contributions in a user's contribution graph.","fields":[{"name":"firstDay","description":"The date of the first day of this month.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Date","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the month.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalWeeks","description":"How many weeks started in this month.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"year","description":"The year the month occurred in.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ContributionCalendarWeek","description":"A week of contributions in a user's contribution graph.","fields":[{"name":"contributionDays","description":"The days of contributions in this week.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ContributionCalendarDay","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"firstDay","description":"The date of the earliest square in this week.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Date","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ContributionLevel","description":"Varying levels of contributions from none to many.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NONE","description":"No contributions occurred.","isDeprecated":false,"deprecationReason":null},{"name":"FIRST_QUARTILE","description":"Lowest 25% of days of contributions.","isDeprecated":false,"deprecationReason":null},{"name":"SECOND_QUARTILE","description":"Second lowest 25% of days of contributions. More contributions than the first quartile.","isDeprecated":false,"deprecationReason":null},{"name":"THIRD_QUARTILE","description":"Second highest 25% of days of contributions. More contributions than second quartile, less than the fourth quartile.","isDeprecated":false,"deprecationReason":null},{"name":"FOURTH_QUARTILE","description":"Highest 25% of days of contributions. More contributions than the third quartile.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ContributionOrder","description":"Ordering options for contribution connections.","fields":null,"inputFields":[{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ContributionsCollection","description":"A contributions collection aggregates contributions such as opened issues and commits created by a user.","fields":[{"name":"commitContributionsByRepository","description":"Commit contributions made by the user, grouped by repository.","args":[{"name":"maxRepositories","description":"How many repositories should be included.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"25"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitContributionsByRepository","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"contributionCalendar","description":"A calendar of this user's contributions on GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ContributionCalendar","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"contributionYears","description":"The years the user has been making contributions with the most recent year first.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"doesEndInCurrentMonth","description":"Determine if this collection's time span ends in the current month.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"earliestRestrictedContributionDate","description":"The date of the first restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.","args":[],"type":{"kind":"SCALAR","name":"Date","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"endedAt","description":"The ending date and time of this collection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"firstIssueContribution","description":"The first issue the user opened on GitHub. This will be null if that issue was opened outside the collection's time range and ignoreTimeRange is false. If the issue is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.","args":[],"type":{"kind":"UNION","name":"CreatedIssueOrRestrictedContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"firstPullRequestContribution","description":"The first pull request the user opened on GitHub. This will be null if that pull request was opened outside the collection's time range and ignoreTimeRange is not true. If the pull request is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.","args":[],"type":{"kind":"UNION","name":"CreatedPullRequestOrRestrictedContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"firstRepositoryContribution","description":"The first repository the user created on GitHub. This will be null if that first repository was created outside the collection's time range and ignoreTimeRange is false. If the repository is not visible, then a RestrictedContribution is returned.","args":[],"type":{"kind":"UNION","name":"CreatedRepositoryOrRestrictedContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"hasActivityInThePast","description":"Does the user have any more activity in the timeline that occurred prior to the collection's time range?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasAnyContributions","description":"Determine if there are any contributions in this collection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasAnyRestrictedContributions","description":"Determine if the user made any contributions in this time frame whose details are not visible because they were made in a private repository. Can only be true if the user enabled private contribution counts.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSingleDay","description":"Whether or not the collector's time span is all within the same day.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issueContributions","description":"A list of issues the user opened.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"excludeFirst","description":"Should the user's first issue ever be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented issue be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedIssueContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issueContributionsByRepository","description":"Issue contributions made by the user, grouped by repository.","args":[{"name":"maxRepositories","description":"How many repositories should be included.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"25"},{"name":"excludeFirst","description":"Should the user's first issue ever be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented issue be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueContributionsByRepository","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"joinedGitHubContribution","description":"When the user signed up for GitHub. This will be null if that sign up date falls outside the collection's time range and ignoreTimeRange is false.","args":[],"type":{"kind":"OBJECT","name":"JoinedGitHubContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"latestRestrictedContributionDate","description":"The date of the most recent restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.","args":[],"type":{"kind":"SCALAR","name":"Date","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mostRecentCollectionWithActivity","description":"When this collection's time range does not include any activity from the user, use this\nto get a different collection from an earlier time range that does have activity.\n","args":[],"type":{"kind":"OBJECT","name":"ContributionsCollection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mostRecentCollectionWithoutActivity","description":"Returns a different contributions collection from an earlier time range than this one\nthat does not have any contributions.\n","args":[],"type":{"kind":"OBJECT","name":"ContributionsCollection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"popularIssueContribution","description":"The issue the user opened on GitHub that received the most comments in the specified\ntime frame.\n","args":[],"type":{"kind":"OBJECT","name":"CreatedIssueContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"popularPullRequestContribution","description":"The pull request the user opened on GitHub that received the most comments in the\nspecified time frame.\n","args":[],"type":{"kind":"OBJECT","name":"CreatedPullRequestContribution","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestContributions","description":"Pull request contributions made by the user.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"excludeFirst","description":"Should the user's first pull request ever be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented pull request be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestContributionsByRepository","description":"Pull request contributions made by the user, grouped by repository.","args":[{"name":"maxRepositories","description":"How many repositories should be included.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"25"},{"name":"excludeFirst","description":"Should the user's first pull request ever be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented pull request be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestContributionsByRepository","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReviewContributions","description":"Pull request review contributions made by the user.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestReviewContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReviewContributionsByRepository","description":"Pull request review contributions made by the user, grouped by repository.","args":[{"name":"maxRepositories","description":"How many repositories should be included.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"25"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewContributionsByRepository","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryContributions","description":"A list of repositories owned by the user that the user created in this time range.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"excludeFirst","description":"Should the user's first repository ever be excluded from the result.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedRepositoryContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"restrictedContributionsCount","description":"A count of contributions made by the user that the viewer cannot access. Only non-zero when the user has chosen to share their private contribution counts.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"startedAt","description":"The beginning date and time of this collection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCommitContributions","description":"How many commits were made by the user in this time span.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalIssueContributions","description":"How many issues the user opened.","args":[{"name":"excludeFirst","description":"Should the user's first issue ever be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented issue be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalPullRequestContributions","description":"How many pull requests the user opened.","args":[{"name":"excludeFirst","description":"Should the user's first pull request ever be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented pull request be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalPullRequestReviewContributions","description":"How many pull request reviews the user left.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalRepositoriesWithContributedCommits","description":"How many different repositories the user committed to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalRepositoriesWithContributedIssues","description":"How many different repositories the user opened issues in.","args":[{"name":"excludeFirst","description":"Should the user's first issue ever be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented issue be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalRepositoriesWithContributedPullRequestReviews","description":"How many different repositories the user left pull request reviews in.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalRepositoriesWithContributedPullRequests","description":"How many different repositories the user opened pull requests in.","args":[{"name":"excludeFirst","description":"Should the user's first pull request ever be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"excludePopular","description":"Should the user's most commented pull request be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalRepositoryContributions","description":"How many repositories the user created.","args":[{"name":"excludeFirst","description":"Should the user's first repository ever be excluded from this count.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made the contributions in this collection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ConvertProjectCardNoteToIssueInput","description":"Autogenerated input type of ConvertProjectCardNoteToIssue","fields":null,"inputFields":[{"name":"projectCardId","description":"The ProjectCard ID to convert.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"The ID of the repository to create the issue in.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"title","description":"The title of the newly created issue. Defaults to the card's note text.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"body","description":"The body of the newly created issue.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ConvertProjectCardNoteToIssuePayload","description":"Autogenerated return type of ConvertProjectCardNoteToIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projectCard","description":"The updated ProjectCard.","args":[],"type":{"kind":"OBJECT","name":"ProjectCard","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ConvertToDraftEvent","description":"Represents a 'convert_to_draft' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this convert to draft event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this convert to draft event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ConvertedNoteToIssueEvent","description":"Represents a 'converted_note_to_issue' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateBranchProtectionRuleInput","description":"Autogenerated input type of CreateBranchProtectionRule","fields":null,"inputFields":[{"name":"repositoryId","description":"The global relay id of the repository in which a new branch protection rule should be created in.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"pattern","description":"The glob-like pattern used to determine matching branches.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"requiresApprovingReviews","description":"Are approving reviews required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiredApprovingReviewCount","description":"Number of approving reviews required to update matching branches.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"requiresCommitSignatures","description":"Are commits required to be signed.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresLinearHistory","description":"Are merge commits prohibited from being pushed to this branch.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"allowsForcePushes","description":"Are force pushes allowed on this branch.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"allowsDeletions","description":"Can this branch be deleted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"isAdminEnforced","description":"Can admins overwrite branch protection.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresStatusChecks","description":"Are status checks required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresStrictStatusChecks","description":"Are branches required to be up to date before merging.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresCodeOwnerReviews","description":"Are reviews from code owners required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"dismissesStaleReviews","description":"Will new commits pushed to matching branches dismiss pull request review approvals.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"restrictsReviewDismissals","description":"Is dismissal of pull request reviews restricted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"reviewDismissalActorIds","description":"A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"restrictsPushes","description":"Is pushing to matching branches restricted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"pushActorIds","description":"A list of User, Team or App IDs allowed to push to matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"requiredStatusCheckContexts","description":"List of required status check contexts that must pass for commits to be accepted to matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateBranchProtectionRulePayload","description":"Autogenerated return type of CreateBranchProtectionRule","fields":[{"name":"branchProtectionRule","description":"The newly created BranchProtectionRule.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateCheckRunInput","description":"Autogenerated input type of CreateCheckRun","fields":null,"inputFields":[{"name":"repositoryId","description":"The node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the check.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"headSha","description":"The SHA of the head commit.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"defaultValue":null},{"name":"detailsUrl","description":"The URL of the integrator's site that has the full details of the check.","type":{"kind":"SCALAR","name":"URI","ofType":null},"defaultValue":null},{"name":"externalId","description":"A reference for the run on the integrator's system.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"status","description":"The current status.","type":{"kind":"ENUM","name":"RequestableCheckStatusState","ofType":null},"defaultValue":null},{"name":"startedAt","description":"The time that the check run began.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"conclusion","description":"The final conclusion of the check.","type":{"kind":"ENUM","name":"CheckConclusionState","ofType":null},"defaultValue":null},{"name":"completedAt","description":"The time that the check run finished.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"output","description":"Descriptive details about the run.","type":{"kind":"INPUT_OBJECT","name":"CheckRunOutput","ofType":null},"defaultValue":null},{"name":"actions","description":"Possible further actions the integrator can perform, which a user may trigger.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckRunAction","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateCheckRunPayload","description":"Autogenerated return type of CreateCheckRun","fields":[{"name":"checkRun","description":"The newly created check run.","args":[],"type":{"kind":"OBJECT","name":"CheckRun","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateCheckSuiteInput","description":"Autogenerated input type of CreateCheckSuite","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"headSha","description":"The SHA of the head commit.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateCheckSuitePayload","description":"Autogenerated return type of CreateCheckSuite","fields":[{"name":"checkSuite","description":"The newly created check suite.","args":[],"type":{"kind":"OBJECT","name":"CheckSuite","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateEnterpriseOrganizationInput","description":"Autogenerated input type of CreateEnterpriseOrganization","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise owning the new organization.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"login","description":"The login of the new organization.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"profileName","description":"The profile name of the new organization.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"billingEmail","description":"The email used for sending billing receipts.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"adminLogins","description":"The logins for the administrators of the new organization.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateEnterpriseOrganizationPayload","description":"Autogenerated return type of CreateEnterpriseOrganization","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise that owns the created organization.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization that was created.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateIpAllowListEntryInput","description":"Autogenerated input type of CreateIpAllowListEntry","fields":null,"inputFields":[{"name":"ownerId","description":"The ID of the owner for which to create the new IP allow list entry.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"allowListValue","description":"An IP address or range of addresses in CIDR notation.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"name","description":"An optional name for the IP allow list entry.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"isActive","description":"Whether the IP allow list entry is active when an IP allow list is enabled.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateIpAllowListEntryPayload","description":"Autogenerated return type of CreateIpAllowListEntry","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEntry","description":"The IP allow list entry that was created.","args":[],"type":{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateIssueInput","description":"Autogenerated input type of CreateIssue","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"title","description":"The title for the issue.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"The body for the issue description.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"assigneeIds","description":"The Node ID for the user assignee for this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"milestoneId","description":"The Node ID of the milestone for this issue.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"labelIds","description":"An array of Node IDs of labels for this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"projectIds","description":"An array of Node IDs for projects associated with this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"issueTemplate","description":"The name of an issue template in the repository, assigns labels and assignees from the template to the issue","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateIssuePayload","description":"Autogenerated return type of CreateIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The new issue.","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateProjectInput","description":"Autogenerated input type of CreateProject","fields":null,"inputFields":[{"name":"ownerId","description":"The owner ID to create the project under.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of project.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"The description of project.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"template","description":"The name of the GitHub-provided template.","type":{"kind":"ENUM","name":"ProjectTemplate","ofType":null},"defaultValue":null},{"name":"repositoryIds","description":"A list of repository IDs to create as linked repositories for the project","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateProjectPayload","description":"Autogenerated return type of CreateProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The new project.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreatePullRequestInput","description":"Autogenerated input type of CreatePullRequest","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"baseRefName","description":"The name of the branch you want your changes pulled into. This should be an existing branch\non the current repository. You cannot update the base branch on a pull request to point\nto another repository.\n","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"headRefName","description":"The name of the branch where your changes are implemented. For cross-repository pull requests\nin the same network, namespace `head_ref_name` with a user like this: `username:branch`.\n","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"title","description":"The title of the pull request.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"The contents of the pull request.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"maintainerCanModify","description":"Indicates whether maintainers can modify the pull request.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"true"},{"name":"draft","description":"Indicates whether this pull request should be a draft.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatePullRequestPayload","description":"Autogenerated return type of CreatePullRequest","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The new pull request.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateRefInput","description":"Autogenerated input type of CreateRef","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the Repository to create the Ref in.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The fully qualified name of the new Ref (ie: `refs/heads/my_new_branch`).","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"oid","description":"The GitObjectID that the new Ref shall target. Must point to a commit.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateRefPayload","description":"Autogenerated return type of CreateRef","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"The newly created ref.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateRepositoryInput","description":"Autogenerated input type of CreateRepository","fields":null,"inputFields":[{"name":"name","description":"The name of the new repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"ownerId","description":"The ID of the owner for the new repository.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"description","description":"A short description of the new repository.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"visibility","description":"Indicates the repository's visibility level.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryVisibility","ofType":null}},"defaultValue":null},{"name":"template","description":"Whether this repository should be marked as a template such that anyone who can access it can create new repositories with the same files and directory structure.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"homepageUrl","description":"The URL for a web page about this repository.","type":{"kind":"SCALAR","name":"URI","ofType":null},"defaultValue":null},{"name":"hasWikiEnabled","description":"Indicates if the repository should have the wiki feature enabled.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"hasIssuesEnabled","description":"Indicates if the repository should have the issues feature enabled.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"true"},{"name":"teamId","description":"When an organization is specified as the owner, this ID identifies the team that should be granted access to the new repository.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateRepositoryPayload","description":"Autogenerated return type of CreateRepository","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The new repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateTeamDiscussionCommentInput","description":"Autogenerated input type of CreateTeamDiscussionComment","fields":null,"inputFields":[{"name":"discussionId","description":"The ID of the discussion to which the comment belongs.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The content of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateTeamDiscussionCommentPayload","description":"Autogenerated return type of CreateTeamDiscussionComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussionComment","description":"The new comment.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"CreateTeamDiscussionInput","description":"Autogenerated input type of CreateTeamDiscussion","fields":null,"inputFields":[{"name":"teamId","description":"The ID of the team to which the discussion belongs.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"title","description":"The title of the discussion.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"body","description":"The content of the discussion.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"private","description":"If true, restricts the visibility of this discussion to team members and organization admins. If false or not specified, allows any organization member to view this discussion.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreateTeamDiscussionPayload","description":"Autogenerated return type of CreateTeamDiscussion","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussion","description":"The new discussion.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedCommitContribution","description":"Represents the contribution a user made by committing to a repository.","fields":[{"name":"commitCount","description":"How many commits were made on this day to this repository by the user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository the user made a commit in.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedCommitContributionConnection","description":"The connection type for CreatedCommitContribution.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedCommitContributionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedCommitContribution","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of commits across days and repositories in the connection.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedCommitContributionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CreatedCommitContribution","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedIssueContribution","description":"Represents the contribution a user made on GitHub by opening an issue.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was opened.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedIssueContributionConnection","description":"The connection type for CreatedIssueContribution.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedIssueContributionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedIssueContribution","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedIssueContributionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CreatedIssueContribution","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"CreatedIssueOrRestrictedContribution","description":"Represents either a issue the viewer can access or a restricted contribution.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CreatedIssueContribution","ofType":null},{"kind":"OBJECT","name":"RestrictedContribution","ofType":null}]},{"kind":"OBJECT","name":"CreatedPullRequestContribution","description":"Represents the contribution a user made on GitHub by opening a pull request.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that was opened.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedPullRequestContributionConnection","description":"The connection type for CreatedPullRequestContribution.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestContributionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestContribution","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedPullRequestContributionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CreatedPullRequestContribution","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"CreatedPullRequestOrRestrictedContribution","description":"Represents either a pull request the viewer can access or a restricted contribution.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CreatedPullRequestContribution","ofType":null},{"kind":"OBJECT","name":"RestrictedContribution","ofType":null}]},{"kind":"OBJECT","name":"CreatedPullRequestReviewContribution","description":"Represents the contribution a user made by leaving a review on a pull request.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request the user reviewed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The review the user left on the pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReview","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository containing the pull request that the user reviewed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedPullRequestReviewContributionConnection","description":"The connection type for CreatedPullRequestReviewContribution.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestReviewContributionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestReviewContribution","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedPullRequestReviewContributionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CreatedPullRequestReviewContribution","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedRepositoryContribution","description":"Represents the contribution a user made on GitHub by creating a repository.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedRepositoryContributionConnection","description":"The connection type for CreatedRepositoryContribution.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedRepositoryContributionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CreatedRepositoryContribution","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"CreatedRepositoryContributionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"CreatedRepositoryContribution","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"CreatedRepositoryOrRestrictedContribution","description":"Represents either a repository the viewer can access or a restricted contribution.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CreatedRepositoryContribution","ofType":null},{"kind":"OBJECT","name":"RestrictedContribution","ofType":null}]},{"kind":"OBJECT","name":"CrossReferencedEvent","description":"Represents a mention made by one issue or pull request to another.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Reference originated in a different repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"referencedAt","description":"Identifies when the reference was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"source","description":"Issue or pull request that made the reference.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"target","description":"Issue or pull request to which the reference was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"willCloseTarget","description":"Checks if the target will be closed when the source is merged.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"Date","description":"An ISO-8601 encoded date string.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"DateTime","description":"An ISO-8601 encoded UTC date string.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeclineTopicSuggestionInput","description":"Autogenerated input type of DeclineTopicSuggestion","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the suggested topic.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"reason","description":"The reason why the suggested topic is declined.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TopicSuggestionDeclineReason","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeclineTopicSuggestionPayload","description":"Autogenerated return type of DeclineTopicSuggestion","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topic","description":"The declined topic.","args":[],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"DefaultRepositoryPermissionField","description":"The possible default permissions for repositories.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NONE","description":"No access","isDeprecated":false,"deprecationReason":null},{"name":"READ","description":"Can read repos by default","isDeprecated":false,"deprecationReason":null},{"name":"WRITE","description":"Can read and write repos by default","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Can read, write, and administrate repos by default","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"Deletable","description":"Entities that can be deleted.","fields":[{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}]},{"kind":"INPUT_OBJECT","name":"DeleteBranchProtectionRuleInput","description":"Autogenerated input type of DeleteBranchProtectionRule","fields":null,"inputFields":[{"name":"branchProtectionRuleId","description":"The global relay id of the branch protection rule to be deleted.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteBranchProtectionRulePayload","description":"Autogenerated return type of DeleteBranchProtectionRule","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteDeploymentInput","description":"Autogenerated input type of DeleteDeployment","fields":null,"inputFields":[{"name":"id","description":"The Node ID of the deployment to be deleted.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteDeploymentPayload","description":"Autogenerated return type of DeleteDeployment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteIpAllowListEntryInput","description":"Autogenerated input type of DeleteIpAllowListEntry","fields":null,"inputFields":[{"name":"ipAllowListEntryId","description":"The ID of the IP allow list entry to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteIpAllowListEntryPayload","description":"Autogenerated return type of DeleteIpAllowListEntry","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEntry","description":"The IP allow list entry that was deleted.","args":[],"type":{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteIssueCommentInput","description":"Autogenerated input type of DeleteIssueComment","fields":null,"inputFields":[{"name":"id","description":"The ID of the comment to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteIssueCommentPayload","description":"Autogenerated return type of DeleteIssueComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteIssueInput","description":"Autogenerated input type of DeleteIssue","fields":null,"inputFields":[{"name":"issueId","description":"The ID of the issue to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteIssuePayload","description":"Autogenerated return type of DeleteIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository the issue belonged to","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteProjectCardInput","description":"Autogenerated input type of DeleteProjectCard","fields":null,"inputFields":[{"name":"cardId","description":"The id of the card to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteProjectCardPayload","description":"Autogenerated return type of DeleteProjectCard","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"column","description":"The column the deleted card was in.","args":[],"type":{"kind":"OBJECT","name":"ProjectColumn","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletedCardId","description":"The deleted card ID.","args":[],"type":{"kind":"SCALAR","name":"ID","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteProjectColumnInput","description":"Autogenerated input type of DeleteProjectColumn","fields":null,"inputFields":[{"name":"columnId","description":"The id of the column to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteProjectColumnPayload","description":"Autogenerated return type of DeleteProjectColumn","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletedColumnId","description":"The deleted column ID.","args":[],"type":{"kind":"SCALAR","name":"ID","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The project the deleted column was in.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteProjectInput","description":"Autogenerated input type of DeleteProject","fields":null,"inputFields":[{"name":"projectId","description":"The Project ID to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteProjectPayload","description":"Autogenerated return type of DeleteProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The repository or organization the project was removed from.","args":[],"type":{"kind":"INTERFACE","name":"ProjectOwner","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeletePullRequestReviewCommentInput","description":"Autogenerated input type of DeletePullRequestReviewComment","fields":null,"inputFields":[{"name":"id","description":"The ID of the comment to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeletePullRequestReviewCommentPayload","description":"Autogenerated return type of DeletePullRequestReviewComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The pull request review the deleted comment belonged to.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeletePullRequestReviewInput","description":"Autogenerated input type of DeletePullRequestReview","fields":null,"inputFields":[{"name":"pullRequestReviewId","description":"The Node ID of the pull request review to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeletePullRequestReviewPayload","description":"Autogenerated return type of DeletePullRequestReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The deleted pull request review.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteRefInput","description":"Autogenerated input type of DeleteRef","fields":null,"inputFields":[{"name":"refId","description":"The Node ID of the Ref to be deleted.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteRefPayload","description":"Autogenerated return type of DeleteRef","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteTeamDiscussionCommentInput","description":"Autogenerated input type of DeleteTeamDiscussionComment","fields":null,"inputFields":[{"name":"id","description":"The ID of the comment to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteTeamDiscussionCommentPayload","description":"Autogenerated return type of DeleteTeamDiscussionComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteTeamDiscussionInput","description":"Autogenerated input type of DeleteTeamDiscussion","fields":null,"inputFields":[{"name":"id","description":"The discussion ID to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteTeamDiscussionPayload","description":"Autogenerated return type of DeleteTeamDiscussion","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeleteVerifiableDomainInput","description":"Autogenerated input type of DeleteVerifiableDomain","fields":null,"inputFields":[{"name":"id","description":"The ID of the verifiable domain to delete.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeleteVerifiableDomainPayload","description":"Autogenerated return type of DeleteVerifiableDomain","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The owning account from which the domain was deleted.","args":[],"type":{"kind":"UNION","name":"VerifiableDomainOwner","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DemilestonedEvent","description":"Represents a 'demilestoned' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"milestoneTitle","description":"Identifies the milestone title associated with the 'demilestoned' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Object referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"MilestoneItem","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeployKey","description":"A repository deploy key.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"The deploy key.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"readOnly","description":"Whether or not the deploy key is read only.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"The deploy key title.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"verified","description":"Whether or not the deploy key has been verified.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeployKeyConnection","description":"The connection type for DeployKey.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"DeployKeyEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"DeployKey","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeployKeyEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"DeployKey","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeployedEvent","description":"Represents a 'deployed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deployment","description":"The deployment associated with the 'deployed' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Deployment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"The ref associated with the 'deployed' event.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Deployment","description":"Represents triggered deployment instance.","fields":[{"name":"commit","description":"Identifies the commit sha of the deployment.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commitOid","description":"Identifies the oid of the deployment commit, even if the commit has been deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"Identifies the actor who triggered the deployment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Actor","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The deployment description.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"environment","description":"The latest environment to which this deployment was made.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"latestEnvironment","description":"The latest environment to which this deployment was made.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"latestStatus","description":"The latest status of this deployment.","args":[],"type":{"kind":"OBJECT","name":"DeploymentStatus","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"originalEnvironment","description":"The original environment to which this deployment was made.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"Extra information that a deployment system might need.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"Identifies the Ref of the deployment, if the deployment was created by ref.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Identifies the repository associated with the deployment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The current state of the deployment.","args":[],"type":{"kind":"ENUM","name":"DeploymentState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"statuses","description":"A list of statuses associated with the deployment.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeploymentStatusConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"task","description":"The deployment task.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentConnection","description":"The connection type for Deployment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"DeploymentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Deployment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Deployment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentEnvironmentChangedEvent","description":"Represents a 'deployment_environment_changed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deploymentStatus","description":"The deployment status that updated the deployment environment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"DeploymentStatus","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DeploymentOrder","description":"Ordering options for deployment connections","fields":null,"inputFields":[{"name":"field","description":"The field to order deployments by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"DeploymentOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"DeploymentOrderField","description":"Properties by which deployment connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order collection by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"DeploymentState","description":"The possible states in which a deployment can be.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ABANDONED","description":"The pending deployment was not updated after 30 minutes.","isDeprecated":false,"deprecationReason":null},{"name":"ACTIVE","description":"The deployment is currently active.","isDeprecated":false,"deprecationReason":null},{"name":"DESTROYED","description":"An inactive transient deployment.","isDeprecated":false,"deprecationReason":null},{"name":"ERROR","description":"The deployment experienced an error.","isDeprecated":false,"deprecationReason":null},{"name":"FAILURE","description":"The deployment has failed.","isDeprecated":false,"deprecationReason":null},{"name":"INACTIVE","description":"The deployment is inactive.","isDeprecated":false,"deprecationReason":null},{"name":"PENDING","description":"The deployment is pending.","isDeprecated":false,"deprecationReason":null},{"name":"QUEUED","description":"The deployment has queued","isDeprecated":false,"deprecationReason":null},{"name":"IN_PROGRESS","description":"The deployment is in progress.","isDeprecated":false,"deprecationReason":null},{"name":"WAITING","description":"The deployment is waiting.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentStatus","description":"Describes the status of a given deployment attempt.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"Identifies the actor who triggered the deployment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Actor","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deployment","description":"Identifies the deployment associated with status.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Deployment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"Identifies the description of the deployment.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"environmentUrl","description":"Identifies the environment URL of the deployment.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"logUrl","description":"Identifies the log URL of the deployment.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the current state of the deployment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"DeploymentStatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentStatusConnection","description":"The connection type for DeploymentStatus.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"DeploymentStatusEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"DeploymentStatus","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DeploymentStatusEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"DeploymentStatus","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"DeploymentStatusState","description":"The possible states for a deployment status.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PENDING","description":"The deployment is pending.","isDeprecated":false,"deprecationReason":null},{"name":"SUCCESS","description":"The deployment was successful.","isDeprecated":false,"deprecationReason":null},{"name":"FAILURE","description":"The deployment has failed.","isDeprecated":false,"deprecationReason":null},{"name":"INACTIVE","description":"The deployment is inactive.","isDeprecated":false,"deprecationReason":null},{"name":"ERROR","description":"The deployment experienced an error.","isDeprecated":false,"deprecationReason":null},{"name":"QUEUED","description":"The deployment is queued","isDeprecated":false,"deprecationReason":null},{"name":"IN_PROGRESS","description":"The deployment is in progress.","isDeprecated":false,"deprecationReason":null},{"name":"WAITING","description":"The deployment is waiting.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"DiffSide","description":"The possible sides of a diff.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LEFT","description":"The left side of the diff.","isDeprecated":false,"deprecationReason":null},{"name":"RIGHT","description":"The right side of the diff.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DisablePullRequestAutoMergeInput","description":"Autogenerated input type of DisablePullRequestAutoMerge","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to disable auto merge on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DisablePullRequestAutoMergePayload","description":"Autogenerated return type of DisablePullRequestAutoMerge","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request auto merge was disabled on.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DisconnectedEvent","description":"Represents a 'disconnected' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Reference originated in a different repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"source","description":"Issue or pull request from which the issue was disconnected.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Issue or pull request which was disconnected.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DismissPullRequestReviewInput","description":"Autogenerated input type of DismissPullRequestReview","fields":null,"inputFields":[{"name":"pullRequestReviewId","description":"The Node ID of the pull request review to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"message","description":"The contents of the pull request review dismissal message.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"DismissPullRequestReviewPayload","description":"Autogenerated return type of DismissPullRequestReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The dismissed pull request review.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DraftPullRequestReviewComment","description":"Specifies a review comment to be left with a Pull Request Review.","fields":null,"inputFields":[{"name":"path","description":"Path to the file being commented on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"position","description":"Position in the file to leave a comment on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"body","description":"Body of the comment to leave.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"DraftPullRequestReviewThread","description":"Specifies a review comment thread to be left with a Pull Request Review.","fields":null,"inputFields":[{"name":"path","description":"Path to the file being commented on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"line","description":"The line of the blob to which the thread refers. The end of the line range for multi-line comments.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null},{"name":"side","description":"The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.","type":{"kind":"ENUM","name":"DiffSide","ofType":null},"defaultValue":"RIGHT"},{"name":"startLine","description":"The first line of the range to which the comment refers.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"startSide","description":"The side of the diff on which the start line resides.","type":{"kind":"ENUM","name":"DiffSide","ofType":null},"defaultValue":"RIGHT"},{"name":"body","description":"Body of the comment to leave.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnablePullRequestAutoMergeInput","description":"Autogenerated input type of EnablePullRequestAutoMerge","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to enable auto-merge on.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"commitHeadline","description":"Commit headline to use for the commit when the PR is mergable; if omitted, a default message will be used.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"commitBody","description":"Commit body to use for the commit when the PR is mergable; if omitted, a default message will be used.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"mergeMethod","description":"The merge method to use. If omitted, defaults to 'MERGE'","type":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null},"defaultValue":"MERGE"},{"name":"authorEmail","description":"The email address to associate with this merge.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnablePullRequestAutoMergePayload","description":"Autogenerated return type of EnablePullRequestAutoMerge","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request auto-merge was enabled on.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Enterprise","description":"An account to manage multiple organizations with consolidated policy and billing.","fields":[{"name":"avatarUrl","description":"A URL pointing to the enterprise's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"billingInfo","description":"Enterprise billing information visible to enterprise billing managers.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseBillingInfo","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The description of the enterprise as HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"location","description":"The location of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"members","description":"A list of users who are members of this enterprise.","args":[{"name":"organizationLogins","description":"Only return members within the organizations with these logins","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for members returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseMemberOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"role","description":"The role of the user in the enterprise organization or server.","type":{"kind":"ENUM","name":"EnterpriseUserAccountMembershipRole","ofType":null},"defaultValue":null},{"name":"deployment","description":"Only return members within the selected GitHub Enterprise deployment","type":{"kind":"ENUM","name":"EnterpriseUserDeployment","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseMemberConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizations","description":"A list of organizations that belong to this enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ownerInfo","description":"Enterprise information only visible to enterprise owners.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseOwnerInfo","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"The URL-friendly identifier for the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userAccounts","description":"A list of user accounts on this enterprise.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseUserAccountConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsAdmin","description":"Is the current viewer an admin of this enterprise?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"websiteUrl","description":"The URL of the enterprise website.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseAdministratorConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseAdministratorEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseAdministratorEdge","description":"A User who is an administrator of an enterprise.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The role of the administrator.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","description":"An invitation for a user to become an owner or billing manager of an enterprise.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The email of the person who was invited to the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise the invitation is for.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Enterprise","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitee","description":"The user who was invited to the enterprise.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"inviter","description":"The user who created the invitation.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The invitee's pending role in the enterprise (owner or billing_manager).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseAdministratorInvitationConnection","description":"The connection type for EnterpriseAdministratorInvitation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseAdministratorInvitationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseAdministratorInvitationOrder","description":"Ordering options for enterprise administrator invitation connections","fields":null,"inputFields":[{"name":"field","description":"The field to order enterprise administrator invitations by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseAdministratorInvitationOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseAdministratorInvitationOrderField","description":"Properties by which enterprise administrator invitation connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order enterprise administrator member invitations by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseAdministratorRole","description":"The possible administrator roles in an enterprise account.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OWNER","description":"Represents an owner of the enterprise account.","isDeprecated":false,"deprecationReason":null},{"name":"BILLING_MANAGER","description":"Represents a billing manager of the enterprise account.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","description":"Metadata for an audit entry containing enterprise account information.","fields":[{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"EnterpriseBillingInfo","description":"Enterprise billing information visible to enterprise billing managers and owners.","fields":[{"name":"allLicensableUsersCount","description":"The number of licenseable users/emails across the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"assetPacks","description":"The number of data packs used by all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"availableSeats","description":"The number of available seats across all owned organizations based on the unique number of billable users.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":true,"deprecationReason":"`availableSeats` will be replaced with `totalAvailableLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalAvailableLicenses instead. Removal on 2020-01-01 UTC."},{"name":"bandwidthQuota","description":"The bandwidth quota in GB for all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bandwidthUsage","description":"The bandwidth usage in GB for all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bandwidthUsagePercentage","description":"The bandwidth usage as a percentage of the bandwidth quota.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"seats","description":"The total seats across all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":true,"deprecationReason":"`seats` will be replaced with `totalLicenses` to provide more clarity on the value being returned Use EnterpriseBillingInfo.totalLicenses instead. Removal on 2020-01-01 UTC."},{"name":"storageQuota","description":"The storage quota in GB for all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"storageUsage","description":"The storage usage in GB for all organizations owned by the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"storageUsagePercentage","description":"The storage usage as a percentage of the storage quota.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalAvailableLicenses","description":"The number of available licenses across all owned organizations based on the unique number of billable users.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalLicenses","description":"The total number of licenses allocated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseDefaultRepositoryPermissionSettingValue","description":"The possible values for the enterprise default repository permission setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NO_POLICY","description":"Organizations in the enterprise choose default repository permissions for their members.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Organization members will be able to clone, pull, push, and add new collaborators to all organization repositories.","isDeprecated":false,"deprecationReason":null},{"name":"WRITE","description":"Organization members will be able to clone, pull, and push all organization repositories.","isDeprecated":false,"deprecationReason":null},{"name":"READ","description":"Organization members will be able to clone and pull all organization repositories.","isDeprecated":false,"deprecationReason":null},{"name":"NONE","description":"Organization members will only be able to clone and pull public repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","description":"The possible values for an enabled/disabled enterprise setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENABLED","description":"The setting is enabled for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"The setting is disabled for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null},{"name":"NO_POLICY","description":"There is no policy set for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseEnabledSettingValue","description":"The possible values for an enabled/no policy enterprise setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENABLED","description":"The setting is enabled for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null},{"name":"NO_POLICY","description":"There is no policy set for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseIdentityProvider","description":"An identity provider configured to provision identities for an enterprise.","fields":[{"name":"digestMethod","description":"The digest algorithm used to sign SAML requests for the identity provider.","args":[],"type":{"kind":"ENUM","name":"SamlDigestAlgorithm","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise this identity provider belongs to.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"externalIdentities","description":"ExternalIdentities provisioned by this identity provider.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ExternalIdentityConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"idpCertificate","description":"The x509 certificate used by the identity provider to sign assertions and responses.","args":[],"type":{"kind":"SCALAR","name":"X509Certificate","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issuer","description":"The Issuer Entity ID for the SAML identity provider.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"recoveryCodes","description":"Recovery codes that can be used by admins to access the enterprise if the identity provider is unavailable.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"signatureMethod","description":"The signature algorithm used to sign SAML requests for the identity provider.","args":[],"type":{"kind":"ENUM","name":"SamlSignatureAlgorithm","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ssoUrl","description":"The URL endpoint for the identity provider's SAML SSO.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"EnterpriseMember","description":"An object that is a member of an enterprise.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"EnterpriseUserAccount","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"EnterpriseMemberConnection","description":"The connection type for EnterpriseMember.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseMemberEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"EnterpriseMember","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseMemberEdge","description":"A User who is a member of an enterprise through one or more organizations.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnlicensed","description":"Whether the user does not have a license for the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"All members consume a license Removal on 2021-01-01 UTC."},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"EnterpriseMember","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseMemberOrder","description":"Ordering options for enterprise member connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order enterprise members by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseMemberOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseMemberOrderField","description":"Properties by which enterprise member connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LOGIN","description":"Order enterprise members by login","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order enterprise members by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseMembersCanCreateRepositoriesSettingValue","description":"The possible values for the enterprise members can create repositories setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NO_POLICY","description":"Organization administrators choose whether to allow members to create repositories.","isDeprecated":false,"deprecationReason":null},{"name":"ALL","description":"Members will be able to create public and private repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"Members will be able to create only public repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"Members will be able to create only private repositories.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"Members will not be able to create public or private repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseMembersCanMakePurchasesSettingValue","description":"The possible values for the members can make purchases setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENABLED","description":"The setting is enabled for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"The setting is disabled for organizations in the enterprise.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseOrganizationMembershipConnection","description":"The connection type for Organization.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseOrganizationMembershipEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Organization","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseOrganizationMembershipEdge","description":"An enterprise organization that a user is a member of.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The role of the user in the enterprise membership.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseUserAccountMembershipRole","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseOutsideCollaboratorConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseOutsideCollaboratorEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseOutsideCollaboratorEdge","description":"A User who is an outside collaborator of an enterprise through one or more organizations.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnlicensed","description":"Whether the outside collaborator does not have a license for the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"All outside collaborators consume a license Removal on 2021-01-01 UTC."},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"The enterprise organization repositories this user is a member of.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories.","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":"{field: NAME, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseRepositoryInfoConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseOwnerInfo","description":"Enterprise information only visible to enterprise owners.","fields":[{"name":"admins","description":"A list of all of the administrators for this enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"role","description":"The role to filter by.","type":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for administrators returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseMemberOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseAdministratorConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"affiliatedUsersWithTwoFactorDisabled","description":"A list of users in the enterprise who currently have two-factor authentication disabled.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"affiliatedUsersWithTwoFactorDisabledExist","description":"Whether or not affiliated users with two-factor authentication disabled exist in the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"allowPrivateRepositoryForkingSetting","description":"The setting value for whether private repository forking is enabled for repositories in organizations in this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"allowPrivateRepositoryForkingSettingOrganizations","description":"A list of enterprise organizations configured with the provided private repository forking setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"defaultRepositoryPermissionSetting","description":"The setting value for base repository permissions for organizations in this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseDefaultRepositoryPermissionSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"defaultRepositoryPermissionSettingOrganizations","description":"A list of enterprise organizations configured with the provided default repository permission.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The permission to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"DefaultRepositoryPermissionField","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"domains","description":"A list of domains owned by the enterprise.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isVerified","description":"Filter whether or not the domain is verified.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"null"},{"name":"orderBy","description":"Ordering options for verifiable domains returned.","type":{"kind":"INPUT_OBJECT","name":"VerifiableDomainOrder","ofType":null},"defaultValue":"{field: DOMAIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"VerifiableDomainConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseServerInstallations","description":"Enterprise Server installations owned by the enterprise.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"connectedOnly","description":"Whether or not to only return installations discovered via GitHub Connect.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for Enterprise Server installations returned.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseServerInstallationOrder","ofType":null},"defaultValue":"{field: HOST_NAME, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerInstallationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEnabledSetting","description":"The setting value for whether the enterprise has an IP allow list enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IpAllowListEnabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEntries","description":"The IP addresses that are allowed to access resources owned by the enterprise.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for IP allow list entries returned.","type":{"kind":"INPUT_OBJECT","name":"IpAllowListEntryOrder","ofType":null},"defaultValue":"{field: ALLOW_LIST_VALUE, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IpAllowListEntryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUpdatingDefaultRepositoryPermission","description":"Whether or not the default repository permission is currently being updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUpdatingTwoFactorRequirement","description":"Whether the two-factor authentication requirement is currently being enforced.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanChangeRepositoryVisibilitySetting","description":"The setting value for whether organization members with admin permissions on a repository can change repository visibility.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanChangeRepositoryVisibilitySettingOrganizations","description":"A list of enterprise organizations configured with the provided can change repository visibility setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanCreateInternalRepositoriesSetting","description":"The setting value for whether members of organizations in the enterprise can create internal repositories.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanCreatePrivateRepositoriesSetting","description":"The setting value for whether members of organizations in the enterprise can create private repositories.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanCreatePublicRepositoriesSetting","description":"The setting value for whether members of organizations in the enterprise can create public repositories.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanCreateRepositoriesSetting","description":"The setting value for whether members of organizations in the enterprise can create repositories.","args":[],"type":{"kind":"ENUM","name":"EnterpriseMembersCanCreateRepositoriesSettingValue","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanCreateRepositoriesSettingOrganizations","description":"A list of enterprise organizations configured with the provided repository creation setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrganizationMembersCanCreateRepositoriesSettingValue","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanDeleteIssuesSetting","description":"The setting value for whether members with admin permissions for repositories can delete issues.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanDeleteIssuesSettingOrganizations","description":"A list of enterprise organizations configured with the provided members can delete issues setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanDeleteRepositoriesSetting","description":"The setting value for whether members with admin permissions for repositories can delete or transfer repositories.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanDeleteRepositoriesSettingOrganizations","description":"A list of enterprise organizations configured with the provided members can delete repositories setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanInviteCollaboratorsSetting","description":"The setting value for whether members of organizations in the enterprise can invite outside collaborators.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanInviteCollaboratorsSettingOrganizations","description":"A list of enterprise organizations configured with the provided members can invite collaborators setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanMakePurchasesSetting","description":"Indicates whether members of this enterprise's organizations can purchase additional services for those organizations.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseMembersCanMakePurchasesSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanUpdateProtectedBranchesSetting","description":"The setting value for whether members with admin permissions for repositories can update protected branches.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanUpdateProtectedBranchesSettingOrganizations","description":"A list of enterprise organizations configured with the provided members can update protected branches setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanViewDependencyInsightsSetting","description":"The setting value for whether members can view dependency insights.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersCanViewDependencyInsightsSettingOrganizations","description":"A list of enterprise organizations configured with the provided members can view dependency insights setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"notificationDeliveryRestrictionEnabledSetting","description":"Indicates if email notification delivery for this enterprise is restricted to verified domains.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"NotificationRestrictionSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationProjectsSetting","description":"The setting value for whether organization projects are enabled for organizations in this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationProjectsSettingOrganizations","description":"A list of enterprise organizations configured with the provided organization projects setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"outsideCollaborators","description":"A list of outside collaborators across the repositories in the enterprise.","args":[{"name":"login","description":"The login of one specific outside collaborator.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for outside collaborators returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseMemberOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"visibility","description":"Only return outside collaborators on repositories with this visibility.","type":{"kind":"ENUM","name":"RepositoryVisibility","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseOutsideCollaboratorConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pendingAdminInvitations","description":"A list of pending administrator invitations for the enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pending enterprise administrator invitations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseAdministratorInvitationOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"},{"name":"role","description":"The role to filter by.","type":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pendingCollaboratorInvitations","description":"A list of pending collaborator invitations across the repositories in the enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pending repository collaborator invitations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"RepositoryInvitationOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryInvitationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pendingCollaborators","description":"A list of pending collaborators across the repositories in the enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pending repository collaborator invitations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"RepositoryInvitationOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterprisePendingCollaboratorConnection","ofType":null}},"isDeprecated":true,"deprecationReason":"Repository invitations can now be associated with an email, not only an invitee. Use the `pendingCollaboratorInvitations` field instead. Removal on 2020-10-01 UTC."},{"name":"pendingMemberInvitations","description":"A list of pending member invitations for organizations in the enterprise.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterprisePendingMemberInvitationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryProjectsSetting","description":"The setting value for whether repository projects are enabled in this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryProjectsSettingOrganizations","description":"A list of enterprise organizations configured with the provided repository projects setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"samlIdentityProvider","description":"The SAML Identity Provider for the enterprise.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseIdentityProvider","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"samlIdentityProviderSettingOrganizations","description":"A list of enterprise organizations configured with the SAML single sign-on setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IdentityProviderConfigurationState","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"supportEntitlements","description":"A list of members with a support entitlement.","args":[{"name":"orderBy","description":"Ordering options for support entitlement users returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseMemberOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseMemberConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussionsSetting","description":"The setting value for whether team discussions are enabled for organizations in this enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussionsSettingOrganizations","description":"A list of enterprise organizations configured with the provided team discussions setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"twoFactorRequiredSetting","description":"The setting value for whether the enterprise requires two-factor authentication for its organizations and users.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"twoFactorRequiredSettingOrganizations","description":"A list of enterprise organizations configured with the two-factor authentication setting value.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"value","description":"The setting value to find organizations for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations with this setting.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterprisePendingCollaboratorConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterprisePendingCollaboratorEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterprisePendingCollaboratorEdge","description":"A user with an invitation to be a collaborator on a repository owned by an organization in an enterprise.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnlicensed","description":"Whether the invited collaborator does not have a license for the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"All pending collaborators consume a license Removal on 2021-01-01 UTC."},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"The enterprise organization repositories this user is a member of.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories.","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":"{field: NAME, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseRepositoryInfoConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterprisePendingMemberInvitationConnection","description":"The connection type for OrganizationInvitation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterprisePendingMemberInvitationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalUniqueUserCount","description":"Identifies the total count of unique users in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterprisePendingMemberInvitationEdge","description":"An invitation to be a member in an enterprise organization.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnlicensed","description":"Whether the invitation has a license for the enterprise.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"All pending members consume a license Removal on 2020-07-01 UTC."},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseRepositoryInfo","description":"A subset of repository information queryable from an enterprise.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrivate","description":"Identifies if the repository is private.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The repository's name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nameWithOwner","description":"The repository's name with owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseRepositoryInfoConnection","description":"The connection type for EnterpriseRepositoryInfo.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseRepositoryInfoEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseRepositoryInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseRepositoryInfoEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseRepositoryInfo","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerInstallation","description":"An Enterprise Server installation.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"customerName","description":"The customer name to which the Enterprise Server installation belongs.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hostName","description":"The host name of the Enterprise Server installation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isConnected","description":"Whether or not the installation is connected to an Enterprise Server installation via GitHub Connect.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userAccounts","description":"User accounts on this Enterprise Server installation.","args":[{"name":"orderBy","description":"Ordering options for Enterprise Server user accounts returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userAccountsUploads","description":"User accounts uploads for the Enterprise Server installation.","args":[{"name":"orderBy","description":"Ordering options for Enterprise Server user accounts uploads returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountsUploadOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUploadConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerInstallationConnection","description":"The connection type for EnterpriseServerInstallation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerInstallationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerInstallation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerInstallationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseServerInstallation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseServerInstallationOrder","description":"Ordering options for Enterprise Server installation connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order Enterprise Server installations by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseServerInstallationOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseServerInstallationOrderField","description":"Properties by which Enterprise Server installation connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"HOST_NAME","description":"Order Enterprise Server installations by host name","isDeprecated":false,"deprecationReason":null},{"name":"CUSTOMER_NAME","description":"Order Enterprise Server installations by customer name","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order Enterprise Server installations by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccount","description":"A user account on an Enterprise Server installation.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"emails","description":"User emails belonging to this user account.","args":[{"name":"orderBy","description":"Ordering options for Enterprise Server user account emails returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountEmailOrder","ofType":null},"defaultValue":"{field: EMAIL, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmailConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseServerInstallation","description":"The Enterprise Server installation on which this user account exists.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerInstallation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSiteAdmin","description":"Whether the user account is a site administrator on the Enterprise Server installation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The login of the user account on the Enterprise Server installation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"profileName","description":"The profile name of the user account on the Enterprise Server installation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"remoteCreatedAt","description":"The date and time when the user account was created on the Enterprise Server installation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"remoteUserId","description":"The ID of the user account on the Enterprise Server installation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountConnection","description":"The connection type for EnterpriseServerUserAccount.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccount","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseServerUserAccount","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmail","description":"An email belonging to a user account on an Enterprise Server installation.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The email address.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrimary","description":"Indicates whether this is the primary email of the associated user account.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userAccount","description":"The user account to which the email belongs.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccount","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmailConnection","description":"The connection type for EnterpriseServerUserAccountEmail.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmailEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmail","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmailEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmail","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountEmailOrder","description":"Ordering options for Enterprise Server user account email connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order emails by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseServerUserAccountEmailOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseServerUserAccountEmailOrderField","description":"Properties by which Enterprise Server user account email connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"EMAIL","description":"Order emails by email","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountOrder","description":"Ordering options for Enterprise Server user account connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order user accounts by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseServerUserAccountOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseServerUserAccountOrderField","description":"Properties by which Enterprise Server user account connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LOGIN","description":"Order user accounts by login","isDeprecated":false,"deprecationReason":null},{"name":"REMOTE_CREATED_AT","description":"Order user accounts by creation time on the Enterprise Server installation","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUpload","description":"A user accounts upload from an Enterprise Server installation.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise to which this upload belongs.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Enterprise","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseServerInstallation","description":"The Enterprise Server installation for which this upload was generated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerInstallation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the file uploaded.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"syncState","description":"The synchronization state of the upload","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseServerUserAccountsUploadSyncState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUploadConnection","description":"The connection type for EnterpriseServerUserAccountsUpload.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUploadEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUpload","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUploadEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUpload","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"EnterpriseServerUserAccountsUploadOrder","description":"Ordering options for Enterprise Server user accounts upload connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order user accounts uploads by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseServerUserAccountsUploadOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseServerUserAccountsUploadOrderField","description":"Properties by which Enterprise Server user accounts upload connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order user accounts uploads by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseServerUserAccountsUploadSyncState","description":"Synchronization state of the Enterprise Server user accounts upload","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PENDING","description":"The synchronization of the upload is pending.","isDeprecated":false,"deprecationReason":null},{"name":"SUCCESS","description":"The synchronization of the upload succeeded.","isDeprecated":false,"deprecationReason":null},{"name":"FAILURE","description":"The synchronization of the upload failed.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseUserAccount","description":"An account for a user who is an admin of an enterprise or a member of an enterprise through one or more organizations.","fields":[{"name":"avatarUrl","description":"A URL pointing to the enterprise user account's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise in which this user account exists.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Enterprise","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"An identifier for the enterprise user account, a login or email address","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the enterprise user account","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizations","description":"A list of enterprise organizations this user is a member of.","args":[{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for organizations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"OrganizationOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"role","description":"The role of the user in the enterprise organization.","type":{"kind":"ENUM","name":"EnterpriseUserAccountMembershipRole","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseOrganizationMembershipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user within the enterprise.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Actor","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseUserAccountConnection","description":"The connection type for EnterpriseUserAccount.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseUserAccountEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"EnterpriseUserAccount","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"EnterpriseUserAccountEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseUserAccount","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseUserAccountMembershipRole","description":"The possible roles for enterprise membership.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MEMBER","description":"The user is a member of the enterprise membership.","isDeprecated":false,"deprecationReason":null},{"name":"OWNER","description":"The user is an owner of the enterprise membership.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"EnterpriseUserDeployment","description":"The possible GitHub Enterprise deployments where this user can exist.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CLOUD","description":"The user is part of a GitHub Enterprise Cloud deployment.","isDeprecated":false,"deprecationReason":null},{"name":"SERVER","description":"The user is part of a GitHub Enterprise Server deployment.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ExternalIdentity","description":"An external identity provisioned by SAML SSO or SCIM.","fields":[{"name":"guid","description":"The GUID for this identity","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationInvitation","description":"Organization invitation for this SCIM-provisioned external identity","args":[],"type":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"samlIdentity","description":"SAML Identity attributes","args":[],"type":{"kind":"OBJECT","name":"ExternalIdentitySamlAttributes","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"scimIdentity","description":"SCIM Identity attributes","args":[],"type":{"kind":"OBJECT","name":"ExternalIdentityScimAttributes","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"User linked to this external identity. Will be NULL if this identity has not been claimed by an organization member.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ExternalIdentityConnection","description":"The connection type for ExternalIdentity.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ExternalIdentityEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ExternalIdentity","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ExternalIdentityEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ExternalIdentity","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ExternalIdentitySamlAttributes","description":"SAML attributes for the External Identity","fields":[{"name":"emails","description":"The emails associated with the SAML identity","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserEmailMetadata","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"familyName","description":"Family name of the SAML identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"givenName","description":"Given name of the SAML identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"groups","description":"The groups linked to this identity in IDP","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"nameId","description":"The NameID of the SAML identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"username","description":"The userName of the SAML identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ExternalIdentityScimAttributes","description":"SCIM attributes for the External Identity","fields":[{"name":"emails","description":"The emails associated with the SCIM identity","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserEmailMetadata","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"familyName","description":"Family name of the SCIM identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"givenName","description":"Given name of the SCIM identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"groups","description":"The groups linked to this identity in IDP","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"username","description":"The userName of the SCIM identity","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"FileViewedState","description":"The possible viewed states of a file .","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"DISMISSED","description":"The file has new changes since last viewed.","isDeprecated":false,"deprecationReason":null},{"name":"VIEWED","description":"The file has been marked as viewed.","isDeprecated":false,"deprecationReason":null},{"name":"UNVIEWED","description":"The file has not been marked as viewed.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"SCALAR","name":"Float","description":"Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"FollowUserInput","description":"Autogenerated input type of FollowUser","fields":null,"inputFields":[{"name":"userId","description":"ID of the user to follow.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"FollowUserPayload","description":"Autogenerated return type of FollowUser","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user that was followed.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"FollowerConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"FollowingConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"FundingLink","description":"A funding platform link for a repository.","fields":[{"name":"platform","description":"The funding platform this link is for.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"FundingPlatform","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The configured URL for this funding link.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"FundingPlatform","description":"The possible funding platforms for repository funding links.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"GITHUB","description":"GitHub funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"PATREON","description":"Patreon funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"OPEN_COLLECTIVE","description":"Open Collective funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"KO_FI","description":"Ko-fi funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"TIDELIFT","description":"Tidelift funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"COMMUNITY_BRIDGE","description":"Community Bridge funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"LIBERAPAY","description":"Liberapay funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"ISSUEHUNT","description":"IssueHunt funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"OTECHIE","description":"Otechie funding platform.","isDeprecated":false,"deprecationReason":null},{"name":"CUSTOM","description":"Custom funding platform.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"GenericHovercardContext","description":"A generic hovercard context with a message and icon","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"HovercardContext","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Gist","description":"A Gist.","fields":[{"name":"comments","description":"A list of comments associated with the gist","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GistCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The gist description.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"files","description":"The files in this gist.","args":[{"name":"limit","description":"The maximum number of files to return.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"10"},{"name":"oid","description":"The oid of the files to return","type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"defaultValue":null}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GistFile","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"forks","description":"A list of forks associated with the gist","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for gists returned from the connection","type":{"kind":"INPUT_OBJECT","name":"GistOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GistConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isFork","description":"Identifies if the gist is a fork.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPublic","description":"Whether the gist is public or not.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The gist name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The gist owner.","args":[],"type":{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pushedAt","description":"Identifies when the gist was last pushed to.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTML path to this resource.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazerCount","description":"Returns a count of how many stargazers there are on this object\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazers","description":"A list of users who have starred this starrable.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"StarOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StargazerConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this Gist.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasStarred","description":"Returns a boolean indicating whether the viewing user has starred this starrable.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Starrable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistComment","description":"Represents a comment on an Gist.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the gist.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"Identifies the comment body.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"gist","description":"The associated gist.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Gist","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMinimized","description":"Returns whether or not a comment has been minimized.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedReason","description":"Returns why the comment was minimized.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanMinimize","description":"Check if the current viewer can minimize this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Minimizable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistCommentConnection","description":"The connection type for GistComment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GistCommentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GistComment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistCommentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"GistComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistConnection","description":"The connection type for Gist.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GistEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Gist","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Gist","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GistFile","description":"A file in a gist.","fields":[{"name":"encodedName","description":"The file name encoded to remove characters that are invalid in URL paths.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"encoding","description":"The gist file encoding.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"extension","description":"The file extension from the file name.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isImage","description":"Indicates if this file is an image.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isTruncated","description":"Whether the file's contents were truncated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"language","description":"The programming language this file is written in.","args":[],"type":{"kind":"OBJECT","name":"Language","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The gist file name.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"size","description":"The gist file size in bytes.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"text","description":"UTF8 text data or null if the file is binary","args":[{"name":"truncate","description":"Optionally truncate the returned file to this length.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"GistOrder","description":"Ordering options for gist connections","fields":null,"inputFields":[{"name":"field","description":"The field to order repositories by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"GistOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"GistOrderField","description":"Properties by which gist connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order gists by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order gists by update time","isDeprecated":false,"deprecationReason":null},{"name":"PUSHED_AT","description":"Order gists by push time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"GistPrivacy","description":"The privacy of a Gist","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PUBLIC","description":"Public","isDeprecated":false,"deprecationReason":null},{"name":"SECRET","description":"Secret","isDeprecated":false,"deprecationReason":null},{"name":"ALL","description":"Gists that are public and secret","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"GitActor","description":"Represents an actor in a Git commit (ie. an author or committer).","fields":[{"name":"avatarUrl","description":"A URL pointing to the author's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"date","description":"The timestamp of the Git action (authoring or committing).","args":[],"type":{"kind":"SCALAR","name":"GitTimestamp","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The email in the Git commit.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name in the Git commit.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The GitHub user corresponding to the email field. Null if no such user exists.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GitActorConnection","description":"The connection type for GitActor.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GitActorEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"GitActor","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GitActorEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"GitActor","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GitHubMetadata","description":"Represents information about the GitHub instance.","fields":[{"name":"gitHubServicesSha","description":"Returns a String that's a SHA of `github-services`","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"gitIpAddresses","description":"IP addresses that users connect to for git operations","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"hookIpAddresses","description":"IP addresses that service hooks are sent from","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"importerIpAddresses","description":"IP addresses that the importer connects from","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"isPasswordAuthenticationVerifiable","description":"Whether or not users are verified","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pagesIpAddresses","description":"IP addresses for GitHub Pages' A records","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"GitObject","description":"Represents a Git object.","fields":[{"name":"abbreviatedOid","description":"An abbreviated version of the Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitResourcePath","description":"The HTTP path for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitUrl","description":"The HTTP URL for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"The Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the Git object belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Blob","ofType":null},{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"Tag","ofType":null},{"kind":"OBJECT","name":"Tree","ofType":null}]},{"kind":"SCALAR","name":"GitObjectID","description":"A Git object ID.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"GitSSHRemote","description":"Git SSH string","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"GitSignature","description":"Information about a signature (GPG or S/MIME) on a Commit or Tag.","fields":[{"name":"email","description":"Email used to sign this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isValid","description":"True if the signature is valid and verified by GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"Payload for GPG signing object. Raw ODB object without the signature header.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signature","description":"ASCII-armored signature header from object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signer","description":"GitHub user corresponding to the email signing this commit.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"GitSignatureState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"wasSignedByGitHub","description":"True if the signature was made with GitHub's signing key.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"GpgSignature","ofType":null},{"kind":"OBJECT","name":"SmimeSignature","ofType":null},{"kind":"OBJECT","name":"UnknownSignature","ofType":null}]},{"kind":"ENUM","name":"GitSignatureState","description":"The state of a Git signature.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"VALID","description":"Valid signature and verified by GitHub","isDeprecated":false,"deprecationReason":null},{"name":"INVALID","description":"Invalid signature","isDeprecated":false,"deprecationReason":null},{"name":"MALFORMED_SIG","description":"Malformed signature","isDeprecated":false,"deprecationReason":null},{"name":"UNKNOWN_KEY","description":"Key used for signing not known to GitHub","isDeprecated":false,"deprecationReason":null},{"name":"BAD_EMAIL","description":"Invalid email used for signing","isDeprecated":false,"deprecationReason":null},{"name":"UNVERIFIED_EMAIL","description":"Email used for signing unverified on GitHub","isDeprecated":false,"deprecationReason":null},{"name":"NO_USER","description":"Email used for signing not known to GitHub","isDeprecated":false,"deprecationReason":null},{"name":"UNKNOWN_SIG_TYPE","description":"Unknown signature type","isDeprecated":false,"deprecationReason":null},{"name":"UNSIGNED","description":"Unsigned","isDeprecated":false,"deprecationReason":null},{"name":"GPGVERIFY_UNAVAILABLE","description":"Internal error - the GPG verification service is unavailable at the moment","isDeprecated":false,"deprecationReason":null},{"name":"GPGVERIFY_ERROR","description":"Internal error - the GPG verification service misbehaved","isDeprecated":false,"deprecationReason":null},{"name":"NOT_SIGNING_KEY","description":"The usage flags for the key that signed this don't allow signing","isDeprecated":false,"deprecationReason":null},{"name":"EXPIRED_KEY","description":"Signing key expired","isDeprecated":false,"deprecationReason":null},{"name":"OCSP_PENDING","description":"Valid signature, pending certificate revocation checking","isDeprecated":false,"deprecationReason":null},{"name":"OCSP_ERROR","description":"Valid signature, though certificate revocation check failed","isDeprecated":false,"deprecationReason":null},{"name":"BAD_CERT","description":"The signing certificate or its chain could not be verified","isDeprecated":false,"deprecationReason":null},{"name":"OCSP_REVOKED","description":"One or more certificates in chain has been revoked","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"SCALAR","name":"GitTimestamp","description":"An ISO-8601 encoded date string. Unlike the DateTime type, GitTimestamp is not converted in UTC.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"GpgSignature","description":"Represents a GPG signature on a Commit or Tag.","fields":[{"name":"email","description":"Email used to sign this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isValid","description":"True if the signature is valid and verified by GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"keyId","description":"Hex-encoded ID of the key that signed this object.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"Payload for GPG signing object. Raw ODB object without the signature header.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signature","description":"ASCII-armored signature header from object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signer","description":"GitHub user corresponding to the email signing this commit.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"GitSignatureState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"wasSignedByGitHub","description":"True if the signature was made with GitHub's signing key.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"GitSignature","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"HTML","description":"A string containing HTML code.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"HeadRefDeletedEvent","description":"Represents a 'head_ref_deleted' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"headRef","description":"Identifies the Ref associated with the `head_ref_deleted` event.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"headRefName","description":"Identifies the name of the Ref associated with the `head_ref_deleted` event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"HeadRefForcePushedEvent","description":"Represents a 'head_ref_force_pushed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"afterCommit","description":"Identifies the after commit SHA for the 'head_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"beforeCommit","description":"Identifies the before commit SHA for the 'head_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"Identifies the fully qualified ref name for the 'head_ref_force_pushed' event.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"HeadRefRestoredEvent","description":"Represents a 'head_ref_restored' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Hovercard","description":"Detail needed to display a hovercard for a user","fields":[{"name":"contexts","description":"Each of the contexts for this hovercard","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"HovercardContext","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"HovercardContext","description":"An individual line of a hovercard","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"GenericHovercardContext","ofType":null},{"kind":"OBJECT","name":"OrganizationTeamsHovercardContext","ofType":null},{"kind":"OBJECT","name":"OrganizationsHovercardContext","ofType":null},{"kind":"OBJECT","name":"ReviewStatusHovercardContext","ofType":null},{"kind":"OBJECT","name":"ViewerHovercardContext","ofType":null}]},{"kind":"SCALAR","name":"ID","description":"Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IdentityProviderConfigurationState","description":"The possible states in which authentication can be configured with an identity provider.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENFORCED","description":"Authentication with an identity provider is configured and enforced.","isDeprecated":false,"deprecationReason":null},{"name":"CONFIGURED","description":"Authentication with an identity provider is configured but not enforced.","isDeprecated":false,"deprecationReason":null},{"name":"UNCONFIGURED","description":"Authentication with an identity provider is not configured.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"SCALAR","name":"Int","description":"Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"InviteEnterpriseAdminInput","description":"Autogenerated input type of InviteEnterpriseAdmin","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise to which you want to invite an administrator.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"invitee","description":"The login of a user to invite as an administrator.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"email","description":"The email of the person to invite as an administrator.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"role","description":"The role of the administrator.","type":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"InviteEnterpriseAdminPayload","description":"Autogenerated return type of InviteEnterpriseAdmin","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"invitation","description":"The created enterprise administrator invitation.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IpAllowListEnabledSettingValue","description":"The possible values for the IP allow list enabled setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENABLED","description":"The setting is enabled for the owner.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"The setting is disabled for the owner.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"IpAllowListEntry","description":"An IP address or range of addresses that is allowed to access an owner's resources.","fields":[{"name":"allowListValue","description":"A single IP address or range of IP addresses in CIDR notation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isActive","description":"Whether the entry is currently active.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the IP allow list entry.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The owner of the IP allow list entry.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"IpAllowListOwner","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IpAllowListEntryConnection","description":"The connection type for IpAllowListEntry.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IpAllowListEntryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IpAllowListEntryEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"IpAllowListEntryOrder","description":"Ordering options for IP allow list entry connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order IP allow list entries by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IpAllowListEntryOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IpAllowListEntryOrderField","description":"Properties by which IP allow list entry connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order IP allow list entries by creation time.","isDeprecated":false,"deprecationReason":null},{"name":"ALLOW_LIST_VALUE","description":"Order IP allow list entries by the allow list value.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"UNION","name":"IpAllowListOwner","description":"Types that can own an IP allow list.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Enterprise","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null}]},{"kind":"OBJECT","name":"Issue","description":"An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.","fields":[{"name":"activeLockReason","description":"Reason that the conversation was locked.","args":[],"type":{"kind":"ENUM","name":"LockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"assignees","description":"A list of Users assigned to this object.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"Identifies the body of the issue.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyResourcePath","description":"The http path for this issue body","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"Identifies the body of the issue rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyUrl","description":"The http URL for this issue body","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closed","description":"`true` if the object is closed (definition of closed may depend on type)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closedAt","description":"Identifies the date and time when the object was closed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"comments","description":"A list of comments associated with the Issue.","args":[{"name":"orderBy","description":"Ordering options for issue comments returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueCommentOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"hovercard","description":"The hovercard information for this issue","args":[{"name":"includeNotificationContexts","description":"Whether or not to include notification contexts","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"true"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Hovercard","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPinned","description":"Indicates whether or not this issue is currently pinned to the repository issues list","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isReadByViewer","description":"Is this issue read by the viewer","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labels","description":"A list of labels associated with the object.","args":[{"name":"orderBy","description":"Ordering options for labels returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"LabelOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LabelConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locked","description":"`true` if the object is locked","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"milestone","description":"Identifies the milestone associated with the issue.","args":[],"type":{"kind":"OBJECT","name":"Milestone","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"Identifies the issue number.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"participants","description":"A list of Users that are participating in the Issue conversation.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectCards","description":"List of project cards associated with this issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"archivedStates","description":"A list of archived states to filter the cards by","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"ProjectCardArchivedState","ofType":null}},"defaultValue":"[ARCHIVED, NOT_ARCHIVED]"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCardConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this issue","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the state of the issue.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"timeline","description":"A list of events, comments, commits, etc. associated with the issue.","args":[{"name":"since","description":"Allows filtering timeline events by a `since` timestamp.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueTimelineConnection","ofType":null}},"isDeprecated":true,"deprecationReason":"`timeline` will be removed Use Issue.timelineItems instead. Removal on 2020-10-01 UTC."},{"name":"timelineItems","description":"A list of events, comments, commits, etc. associated with the issue.","args":[{"name":"since","description":"Filter timeline items by a `since` timestamp.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"skip","description":"Skips the first _n_ elements in the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"itemTypes","description":"Filter timeline items by type.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueTimelineItemsItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueTimelineItemsConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"Identifies the issue title.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this issue","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Assignable","ofType":null},{"kind":"INTERFACE","name":"Closable","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Labelable","ofType":null},{"kind":"INTERFACE","name":"Lockable","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueComment","description":"Represents a comment on an Issue.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The body as Markdown.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMinimized","description":"Returns whether or not a comment has been minimized.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"Identifies the issue associated with the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedReason","description":"Returns why the comment was minimized.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"Returns the pull request associated with the comment, if this comment was made on a\npull request.\n","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this issue comment","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this issue comment","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanMinimize","description":"Check if the current viewer can minimize this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Minimizable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueCommentConnection","description":"The connection type for IssueComment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IssueCommentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IssueComment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueCommentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"IssueComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"IssueCommentOrder","description":"Ways in which lists of issue comments can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order issue comments by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueCommentOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order issue comments by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IssueCommentOrderField","description":"Properties by which issue comment connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"UPDATED_AT","description":"Order issue comments by update time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"IssueConnection","description":"The connection type for Issue.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IssueEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueContributionsByRepository","description":"This aggregates issues opened by a user within one repository.","fields":[{"name":"contributions","description":"The issue contributions.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedIssueContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository in which the issues were opened.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"IssueFilters","description":"Ways in which to filter lists of issues.","fields":null,"inputFields":[{"name":"assignee","description":"List issues assigned to given name. Pass in `null` for issues with no assigned user, and `*` for issues assigned to any user.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"createdBy","description":"List issues created by given name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"labels","description":"List issues where the list of label names exist on the issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"mentioned","description":"List issues where the given name is mentioned in the issue.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"milestone","description":"List issues by given milestone argument. If an string representation of an integer is passed, it should refer to a milestone by its number field. Pass in `null` for issues with no milestone, and `*` for issues that are assigned to any milestone.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"since","description":"List issues that have been updated at or after the given date.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"states","description":"List issues filtered by the list of states given.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}}},"defaultValue":null},{"name":"viewerSubscribed","description":"List issues subscribed to by viewer.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"IssueOrPullRequest","description":"Used for return value of Repository.issueOrPullRequest.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"INPUT_OBJECT","name":"IssueOrder","description":"Ways in which lists of issues can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order issues by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order issues by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IssueOrderField","description":"Properties by which issue connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order issues by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order issues by update time","isDeprecated":false,"deprecationReason":null},{"name":"COMMENTS","description":"Order issues by comment count","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"IssueState","description":"The possible states of an issue.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OPEN","description":"An issue that is still open","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED","description":"An issue that has been closed","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"IssueTemplate","description":"A repository issue template.","fields":[{"name":"about","description":"The template purpose.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The suggested issue body.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The template name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"The suggested issue title.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueTimelineConnection","description":"The connection type for IssueTimelineItem.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IssueTimelineItemEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"IssueTimelineItem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"IssueTimelineItem","description":"An item in an issue timeline","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"AssignedEvent","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"DemilestonedEvent","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"LabeledEvent","ofType":null},{"kind":"OBJECT","name":"LockedEvent","ofType":null},{"kind":"OBJECT","name":"MilestonedEvent","ofType":null},{"kind":"OBJECT","name":"ReferencedEvent","ofType":null},{"kind":"OBJECT","name":"RenamedTitleEvent","ofType":null},{"kind":"OBJECT","name":"ReopenedEvent","ofType":null},{"kind":"OBJECT","name":"SubscribedEvent","ofType":null},{"kind":"OBJECT","name":"TransferredEvent","ofType":null},{"kind":"OBJECT","name":"UnassignedEvent","ofType":null},{"kind":"OBJECT","name":"UnlabeledEvent","ofType":null},{"kind":"OBJECT","name":"UnlockedEvent","ofType":null},{"kind":"OBJECT","name":"UnsubscribedEvent","ofType":null},{"kind":"OBJECT","name":"UserBlockedEvent","ofType":null}]},{"kind":"OBJECT","name":"IssueTimelineItemEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"IssueTimelineItem","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"IssueTimelineItems","description":"An item in an issue timeline","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"AddedToProjectEvent","ofType":null},{"kind":"OBJECT","name":"AssignedEvent","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"CommentDeletedEvent","ofType":null},{"kind":"OBJECT","name":"ConnectedEvent","ofType":null},{"kind":"OBJECT","name":"ConvertedNoteToIssueEvent","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"DemilestonedEvent","ofType":null},{"kind":"OBJECT","name":"DisconnectedEvent","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"LabeledEvent","ofType":null},{"kind":"OBJECT","name":"LockedEvent","ofType":null},{"kind":"OBJECT","name":"MarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"MentionedEvent","ofType":null},{"kind":"OBJECT","name":"MilestonedEvent","ofType":null},{"kind":"OBJECT","name":"MovedColumnsInProjectEvent","ofType":null},{"kind":"OBJECT","name":"PinnedEvent","ofType":null},{"kind":"OBJECT","name":"ReferencedEvent","ofType":null},{"kind":"OBJECT","name":"RemovedFromProjectEvent","ofType":null},{"kind":"OBJECT","name":"RenamedTitleEvent","ofType":null},{"kind":"OBJECT","name":"ReopenedEvent","ofType":null},{"kind":"OBJECT","name":"SubscribedEvent","ofType":null},{"kind":"OBJECT","name":"TransferredEvent","ofType":null},{"kind":"OBJECT","name":"UnassignedEvent","ofType":null},{"kind":"OBJECT","name":"UnlabeledEvent","ofType":null},{"kind":"OBJECT","name":"UnlockedEvent","ofType":null},{"kind":"OBJECT","name":"UnmarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"UnpinnedEvent","ofType":null},{"kind":"OBJECT","name":"UnsubscribedEvent","ofType":null},{"kind":"OBJECT","name":"UserBlockedEvent","ofType":null}]},{"kind":"OBJECT","name":"IssueTimelineItemsConnection","description":"The connection type for IssueTimelineItems.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"IssueTimelineItemsEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"filteredCount","description":"Identifies the count of items after applying `before` and `after` filters.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"IssueTimelineItems","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageCount","description":"Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the timeline was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"IssueTimelineItemsEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"IssueTimelineItems","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"IssueTimelineItemsItemType","description":"The possible item types found in a timeline.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ISSUE_COMMENT","description":"Represents a comment on an Issue.","isDeprecated":false,"deprecationReason":null},{"name":"CROSS_REFERENCED_EVENT","description":"Represents a mention made by one issue or pull request to another.","isDeprecated":false,"deprecationReason":null},{"name":"ADDED_TO_PROJECT_EVENT","description":"Represents a 'added_to_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"ASSIGNED_EVENT","description":"Represents an 'assigned' event on any assignable object.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED_EVENT","description":"Represents a 'closed' event on any `Closable`.","isDeprecated":false,"deprecationReason":null},{"name":"COMMENT_DELETED_EVENT","description":"Represents a 'comment_deleted' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"CONNECTED_EVENT","description":"Represents a 'connected' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"CONVERTED_NOTE_TO_ISSUE_EVENT","description":"Represents a 'converted_note_to_issue' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DEMILESTONED_EVENT","description":"Represents a 'demilestoned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DISCONNECTED_EVENT","description":"Represents a 'disconnected' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"LABELED_EVENT","description":"Represents a 'labeled' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"LOCKED_EVENT","description":"Represents a 'locked' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MARKED_AS_DUPLICATE_EVENT","description":"Represents a 'marked_as_duplicate' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MENTIONED_EVENT","description":"Represents a 'mentioned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MILESTONED_EVENT","description":"Represents a 'milestoned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MOVED_COLUMNS_IN_PROJECT_EVENT","description":"Represents a 'moved_columns_in_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PINNED_EVENT","description":"Represents a 'pinned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"REFERENCED_EVENT","description":"Represents a 'referenced' event on a given `ReferencedSubject`.","isDeprecated":false,"deprecationReason":null},{"name":"REMOVED_FROM_PROJECT_EVENT","description":"Represents a 'removed_from_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"RENAMED_TITLE_EVENT","description":"Represents a 'renamed' event on a given issue or pull request","isDeprecated":false,"deprecationReason":null},{"name":"REOPENED_EVENT","description":"Represents a 'reopened' event on any `Closable`.","isDeprecated":false,"deprecationReason":null},{"name":"SUBSCRIBED_EVENT","description":"Represents a 'subscribed' event on a given `Subscribable`.","isDeprecated":false,"deprecationReason":null},{"name":"TRANSFERRED_EVENT","description":"Represents a 'transferred' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNASSIGNED_EVENT","description":"Represents an 'unassigned' event on any assignable object.","isDeprecated":false,"deprecationReason":null},{"name":"UNLABELED_EVENT","description":"Represents an 'unlabeled' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNLOCKED_EVENT","description":"Represents an 'unlocked' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"USER_BLOCKED_EVENT","description":"Represents a 'user_blocked' event on a given user.","isDeprecated":false,"deprecationReason":null},{"name":"UNMARKED_AS_DUPLICATE_EVENT","description":"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNPINNED_EVENT","description":"Represents an 'unpinned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNSUBSCRIBED_EVENT","description":"Represents an 'unsubscribed' event on a given `Subscribable`.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"JoinedGitHubContribution","description":"Represents a user signing up for a GitHub account.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Label","description":"A label for categorizing Issues or Milestones with a given Repository.","fields":[{"name":"color","description":"Identifies the label color.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the label was created.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"A brief description of this label.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDefault","description":"Indicates whether or not this is a default label.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issues","description":"A list of issues associated with this label.","args":[{"name":"orderBy","description":"Ordering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"states","description":"A list of states to filter the issues by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}}},"defaultValue":null},{"name":"filterBy","description":"Filtering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueFilters","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Identifies the label name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequests","description":"A list of pull requests associated with this label.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this label.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this label.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the label was last updated.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this label.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LabelConnection","description":"The connection type for Label.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"LabelEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Label","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LabelEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Label","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"LabelOrder","description":"Ways in which lists of labels can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order labels by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"LabelOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order labels by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"LabelOrderField","description":"Properties by which label connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NAME","description":"Order labels by name ","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order labels by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"Labelable","description":"An object that can have labels assigned to it.","fields":[{"name":"labels","description":"A list of labels associated with the object.","args":[{"name":"orderBy","description":"Ordering options for labels returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"LabelOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LabelConnection","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"OBJECT","name":"LabeledEvent","description":"Represents a 'labeled' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"label","description":"Identifies the label associated with the 'labeled' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Label","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"labelable","description":"Identifies the `Labelable` associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Labelable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Language","description":"Represents a given language found in repositories.","fields":[{"name":"color","description":"The color defined for the current language.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the current language.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LanguageConnection","description":"A list of languages associated with the parent.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"LanguageEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Language","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalSize","description":"The total size in bytes of files written in that language.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LanguageEdge","description":"Represents the language of a repository.","fields":[{"name":"cursor","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Language","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"size","description":"The number of bytes of code written in the language.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"LanguageOrder","description":"Ordering options for language connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order languages by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"LanguageOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"LanguageOrderField","description":"Properties by which language connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SIZE","description":"Order languages by the size of all files containing the language","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"License","description":"A repository's open source license","fields":[{"name":"body","description":"The full text of the license","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"conditions","description":"The conditions set by the license","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"LicenseRule","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"A human-readable description of the license","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"featured","description":"Whether the license should be featured","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hidden","description":"Whether the license should be displayed in license pickers","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"implementation","description":"Instructions on how to implement the license","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"The lowercased SPDX ID of the license","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"limitations","description":"The limitations set by the license","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"LicenseRule","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The license full name specified by <https://spdx.org/licenses>","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nickname","description":"Customary short name if applicable (e.g, GPLv3)","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permissions","description":"The permissions set by the license","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"LicenseRule","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"pseudoLicense","description":"Whether the license is a pseudo-license placeholder (e.g., other, no-license)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"spdxId","description":"Short identifier specified by <https://spdx.org/licenses>","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"URL to the license on <https://choosealicense.com>","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LicenseRule","description":"Describes a License's conditions, permissions, and limitations","fields":[{"name":"description","description":"A description of the rule","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"The machine-readable rule key","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"label","description":"The human-readable rule label","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"LinkRepositoryToProjectInput","description":"Autogenerated input type of LinkRepositoryToProject","fields":null,"inputFields":[{"name":"projectId","description":"The ID of the Project to link to a Repository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"The ID of the Repository to link to a Project.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LinkRepositoryToProjectPayload","description":"Autogenerated return type of LinkRepositoryToProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The linked Project.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The linked Repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"LockLockableInput","description":"Autogenerated input type of LockLockable","fields":null,"inputFields":[{"name":"lockableId","description":"ID of the item to be locked.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"lockReason","description":"A reason for why the item will be locked.","type":{"kind":"ENUM","name":"LockReason","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"LockLockablePayload","description":"Autogenerated return type of LockLockable","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lockedRecord","description":"The item that was locked.","args":[],"type":{"kind":"INTERFACE","name":"Lockable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"LockReason","description":"The possible reasons that an issue or pull request was locked.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OFF_TOPIC","description":"The issue or pull request was locked because the conversation was off-topic.","isDeprecated":false,"deprecationReason":null},{"name":"TOO_HEATED","description":"The issue or pull request was locked because the conversation was too heated.","isDeprecated":false,"deprecationReason":null},{"name":"RESOLVED","description":"The issue or pull request was locked because the conversation was resolved.","isDeprecated":false,"deprecationReason":null},{"name":"SPAM","description":"The issue or pull request was locked because the conversation was spam.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"Lockable","description":"An object that can be locked.","fields":[{"name":"activeLockReason","description":"Reason that the conversation was locked.","args":[],"type":{"kind":"ENUM","name":"LockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locked","description":"`true` if the object is locked","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"OBJECT","name":"LockedEvent","description":"Represents a 'locked' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lockReason","description":"Reason that the conversation was locked (optional).","args":[],"type":{"kind":"ENUM","name":"LockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lockable","description":"Object that was locked.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Lockable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Mannequin","description":"A placeholder user for attribution of imported data on GitHub.","fields":[{"name":"avatarUrl","description":"A URL pointing to the GitHub App's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The mannequin's email on the source instance.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username of the actor.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTML path to this resource.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The URL to this resource.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Actor","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MarkFileAsViewedInput","description":"Autogenerated input type of MarkFileAsViewed","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Node ID of the pull request.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"path","description":"The path of the file to mark as viewed","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarkFileAsViewedPayload","description":"Autogenerated return type of MarkFileAsViewed","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The updated pull request.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MarkPullRequestReadyForReviewInput","description":"Autogenerated input type of MarkPullRequestReadyForReview","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to be marked as ready for review.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarkPullRequestReadyForReviewPayload","description":"Autogenerated return type of MarkPullRequestReadyForReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that is ready for review.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarkedAsDuplicateEvent","description":"Represents a 'marked_as_duplicate' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"canonical","description":"The authoritative issue or pull request which has been duplicated by another.","args":[],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"duplicate","description":"The issue or pull request which has been marked as a duplicate of another.","args":[],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Canonical and duplicate belong to different repositories.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarketplaceCategory","description":"A public description of a Marketplace category.","fields":[{"name":"description","description":"The category's description.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"howItWorks","description":"The technical description of how apps listed in this category work with GitHub.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The category's name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"primaryListingCount","description":"How many Marketplace listings have this as their primary category.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this Marketplace category.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"secondaryListingCount","description":"How many Marketplace listings have this as their secondary category.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"The short name of the category used in its URL.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this Marketplace category.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarketplaceListing","description":"A listing in the GitHub integration marketplace.","fields":[{"name":"app","description":"The GitHub App this listing represents.","args":[],"type":{"kind":"OBJECT","name":"App","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"companyUrl","description":"URL to the listing owner's company site.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"configurationResourcePath","description":"The HTTP path for configuring access to the listing's integration or OAuth app","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"configurationUrl","description":"The HTTP URL for configuring access to the listing's integration or OAuth app","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"documentationUrl","description":"URL to the listing's documentation.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"extendedDescription","description":"The listing's detailed description.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"extendedDescriptionHTML","description":"The listing's detailed description rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fullDescription","description":"The listing's introductory description.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fullDescriptionHTML","description":"The listing's introductory description rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasPublishedFreeTrialPlans","description":"Does this listing have any plans with a free trial?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasTermsOfService","description":"Does this listing have a terms of service link?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasVerifiedOwner","description":"Whether the creator of the app is a verified org","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"howItWorks","description":"A technical description of how this app works with GitHub.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"howItWorksHTML","description":"The listing's technical description rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"installationUrl","description":"URL to install the product to the viewer's account or organization.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"installedForViewer","description":"Whether this listing's app has been installed for the current viewer","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isArchived","description":"Whether this listing has been removed from the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDraft","description":"Whether this listing is still an editable draft that has not been submitted for review and is not publicly visible in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPaid","description":"Whether the product this listing represents is available as part of a paid plan.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPublic","description":"Whether this listing has been approved for display in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRejected","description":"Whether this listing has been rejected by GitHub for display in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnverified","description":"Whether this listing has been approved for unverified display in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUnverifiedPending","description":"Whether this draft listing has been submitted for review for approval to be unverified in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isVerificationPendingFromDraft","description":"Whether this draft listing has been submitted for review from GitHub for approval to be verified in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isVerificationPendingFromUnverified","description":"Whether this unverified listing has been submitted for review from GitHub for approval to be verified in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isVerified","description":"Whether this listing has been approved for verified display in the Marketplace.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"logoBackgroundColor","description":"The hex color code, without the leading '#', for the logo background.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"logoUrl","description":"URL for the listing's logo image.","args":[{"name":"size","description":"The size in pixels of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"400"}],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The listing's full name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"normalizedShortDescription","description":"The listing's very short description without a trailing period or ampersands.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pricingUrl","description":"URL to the listing's detailed pricing.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"primaryCategory","description":"The category that best describes the listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"MarketplaceCategory","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"privacyPolicyUrl","description":"URL to the listing's privacy policy, may return an empty string for listings that do not require a privacy policy URL.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for the Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"screenshotUrls","description":"The URLs for the listing's screenshots.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"secondaryCategory","description":"An alternate category that describes the listing.","args":[],"type":{"kind":"OBJECT","name":"MarketplaceCategory","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"shortDescription","description":"The listing's very short description.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"The short name of the listing used in its URL.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"statusUrl","description":"URL to the listing's status page.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"supportEmail","description":"An email address for support for this listing's app.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"supportUrl","description":"Either a URL or an email address for support for this listing's app, may return an empty string for listings that do not require a support URL.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"termsOfServiceUrl","description":"URL to the listing's terms of service.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for the Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanAddPlans","description":"Can the current viewer add plans for this Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanApprove","description":"Can the current viewer approve this Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelist","description":"Can the current viewer delist this Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanEdit","description":"Can the current viewer edit this Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanEditCategories","description":"Can the current viewer edit the primary and secondary category of this\nMarketplace listing.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanEditPlans","description":"Can the current viewer edit the plans for this Marketplace listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanRedraft","description":"Can the current viewer return this Marketplace listing to draft state\nso it becomes editable again.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReject","description":"Can the current viewer reject this Marketplace listing by returning it to\nan editable draft state or rejecting it entirely.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanRequestApproval","description":"Can the current viewer request this listing be reviewed for display in\nthe Marketplace as verified.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasPurchased","description":"Indicates whether the current user has an active subscription to this Marketplace listing.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasPurchasedForAllOrganizations","description":"Indicates if the current user has purchased a subscription to this Marketplace listing\nfor all of the organizations the user owns.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsListingAdmin","description":"Does the current viewer role allow them to administer this Marketplace listing.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarketplaceListingConnection","description":"Look up Marketplace Listings","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"MarketplaceListingEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"MarketplaceListing","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MarketplaceListingEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"MarketplaceListing","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"MemberStatusable","description":"Entities that have members who can set status messages.","fields":[{"name":"memberStatuses","description":"Get the status messages members of this entity have set that are either public or visible only to the organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for user statuses returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"UserStatusOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserStatusConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null}]},{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","description":"Audit log entry for a members_can_delete_repos.clear event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","description":"Audit log entry for a members_can_delete_repos.disable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","description":"Audit log entry for a members_can_delete_repos.enable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MentionedEvent","description":"Represents a 'mentioned' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MergeBranchInput","description":"Autogenerated input type of MergeBranch","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the Repository containing the base branch that will be modified.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"base","description":"The name of the base branch that the provided head will be merged into.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"head","description":"The head to merge into the base branch. This can be a branch name or a commit GitObjectID.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"commitMessage","description":"Message to use for the merge commit. If omitted, a default will be used.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"authorEmail","description":"The email address to associate with this commit.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MergeBranchPayload","description":"Autogenerated return type of MergeBranch","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeCommit","description":"The resulting merge Commit.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MergePullRequestInput","description":"Autogenerated input type of MergePullRequest","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to be merged.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"commitHeadline","description":"Commit headline to use for the merge commit; if omitted, a default message will be used.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"commitBody","description":"Commit body to use for the merge commit; if omitted, a default message will be used","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"expectedHeadOid","description":"OID that the pull request head ref must match to allow merge; if omitted, no check is performed.","type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"defaultValue":null},{"name":"mergeMethod","description":"The merge method to use. If omitted, defaults to 'MERGE'","type":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null},"defaultValue":"MERGE"},{"name":"authorEmail","description":"The email address to associate with this merge.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MergePullRequestPayload","description":"Autogenerated return type of MergePullRequest","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that was merged.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"MergeableState","description":"Whether or not a PullRequest can be merged.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MERGEABLE","description":"The pull request can be merged.","isDeprecated":false,"deprecationReason":null},{"name":"CONFLICTING","description":"The pull request cannot be merged due to merge conflicts.","isDeprecated":false,"deprecationReason":null},{"name":"UNKNOWN","description":"The mergeability of the pull request is still being calculated.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"MergedEvent","description":"Represents a 'merged' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the commit associated with the `merge` event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mergeRef","description":"Identifies the Ref associated with the `merge` event.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeRefName","description":"Identifies the name of the Ref associated with the `merge` event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this merged event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this merged event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Milestone","description":"Represents a Milestone object on a given repository.","fields":[{"name":"closed","description":"`true` if the object is closed (definition of closed may depend on type)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closedAt","description":"Identifies the date and time when the object was closed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"Identifies the actor who created the milestone.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"Identifies the description of the milestone.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dueOn","description":"Identifies the due date of the milestone.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issues","description":"A list of issues associated with the milestone.","args":[{"name":"orderBy","description":"Ordering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"states","description":"A list of states to filter the issues by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}}},"defaultValue":null},{"name":"filterBy","description":"Filtering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueFilters","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"Identifies the number of the milestone.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"progressPercentage","description":"Identifies the percentage complete for the milestone","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequests","description":"A list of pull requests associated with the milestone.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this milestone.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this milestone","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the state of the milestone.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"MilestoneState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"Identifies the title of the milestone.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this milestone","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Closable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MilestoneConnection","description":"The connection type for Milestone.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"MilestoneEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Milestone","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MilestoneEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Milestone","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"MilestoneItem","description":"Types that can be inside a Milestone.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"INPUT_OBJECT","name":"MilestoneOrder","description":"Ordering options for milestone connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order milestones by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"MilestoneOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"MilestoneOrderField","description":"Properties by which milestone connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"DUE_DATE","description":"Order milestones by when they are due.","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order milestones by when they were created.","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order milestones by when they were last updated.","isDeprecated":false,"deprecationReason":null},{"name":"NUMBER","description":"Order milestones by their number.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"MilestoneState","description":"The possible states of a milestone.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OPEN","description":"A milestone that is still open.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED","description":"A milestone that has been closed.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"MilestonedEvent","description":"Represents a 'milestoned' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"milestoneTitle","description":"Identifies the milestone title associated with the 'milestoned' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Object referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"MilestoneItem","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Minimizable","description":"Entities that can be minimized.","fields":[{"name":"isMinimized","description":"Returns whether or not a comment has been minimized.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedReason","description":"Returns why the comment was minimized.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanMinimize","description":"Check if the current viewer can minimize this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null}]},{"kind":"INPUT_OBJECT","name":"MinimizeCommentInput","description":"Autogenerated input type of MinimizeComment","fields":null,"inputFields":[{"name":"subjectId","description":"The Node ID of the subject to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"classifier","description":"The classification of comment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReportedContentClassifiers","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MinimizeCommentPayload","description":"Autogenerated return type of MinimizeComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedComment","description":"The comment that was minimized.","args":[],"type":{"kind":"INTERFACE","name":"Minimizable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MoveProjectCardInput","description":"Autogenerated input type of MoveProjectCard","fields":null,"inputFields":[{"name":"cardId","description":"The id of the card to move.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"columnId","description":"The id of the column to move it into.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"afterCardId","description":"Place the new card after the card with this id. Pass null to place it at the top.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MoveProjectCardPayload","description":"Autogenerated return type of MoveProjectCard","fields":[{"name":"cardEdge","description":"The new edge of the moved card.","args":[],"type":{"kind":"OBJECT","name":"ProjectCardEdge","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"MoveProjectColumnInput","description":"Autogenerated input type of MoveProjectColumn","fields":null,"inputFields":[{"name":"columnId","description":"The id of the column to move.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"afterColumnId","description":"Place the new column after the column with this id. Pass null to place it at the front.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MoveProjectColumnPayload","description":"Autogenerated return type of MoveProjectColumn","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"columnEdge","description":"The new edge of the moved column.","args":[],"type":{"kind":"OBJECT","name":"ProjectColumnEdge","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"MovedColumnsInProjectEvent","description":"Represents a 'moved_columns_in_project' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Mutation","description":"The root query for implementing GraphQL mutations.","fields":[{"name":"acceptEnterpriseAdministratorInvitation","description":"Accepts a pending invitation for a user to become an administrator of an enterprise.","args":[{"name":"input","description":"Parameters for AcceptEnterpriseAdministratorInvitation","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AcceptEnterpriseAdministratorInvitationInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AcceptEnterpriseAdministratorInvitationPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"acceptTopicSuggestion","description":"Applies a suggested topic to the repository.","args":[{"name":"input","description":"Parameters for AcceptTopicSuggestion","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AcceptTopicSuggestionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AcceptTopicSuggestionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addAssigneesToAssignable","description":"Adds assignees to an assignable object.","args":[{"name":"input","description":"Parameters for AddAssigneesToAssignable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddAssigneesToAssignableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddAssigneesToAssignablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addComment","description":"Adds a comment to an Issue or Pull Request.","args":[{"name":"input","description":"Parameters for AddComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addEnterpriseSupportEntitlement","description":"Adds a support entitlement to an enterprise member.","args":[{"name":"input","description":"Parameters for AddEnterpriseSupportEntitlement","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddEnterpriseSupportEntitlementInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddEnterpriseSupportEntitlementPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addLabelsToLabelable","description":"Adds labels to a labelable object.","args":[{"name":"input","description":"Parameters for AddLabelsToLabelable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddLabelsToLabelableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddLabelsToLabelablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addProjectCard","description":"Adds a card to a ProjectColumn. Either `contentId` or `note` must be provided but **not** both.","args":[{"name":"input","description":"Parameters for AddProjectCard","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddProjectCardInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddProjectCardPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addProjectColumn","description":"Adds a column to a Project.","args":[{"name":"input","description":"Parameters for AddProjectColumn","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddProjectColumnInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddProjectColumnPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addPullRequestReview","description":"Adds a review to a Pull Request.","args":[{"name":"input","description":"Parameters for AddPullRequestReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddPullRequestReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addPullRequestReviewComment","description":"Adds a comment to a review.","args":[{"name":"input","description":"Parameters for AddPullRequestReviewComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddPullRequestReviewCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addPullRequestReviewThread","description":"Adds a new thread to a pending Pull Request Review.","args":[{"name":"input","description":"Parameters for AddPullRequestReviewThread","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddPullRequestReviewThreadInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddPullRequestReviewThreadPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addReaction","description":"Adds a reaction to a subject.","args":[{"name":"input","description":"Parameters for AddReaction","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddReactionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddReactionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addStar","description":"Adds a star to a Starrable.","args":[{"name":"input","description":"Parameters for AddStar","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddStarInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddStarPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"addVerifiableDomain","description":"Adds a verifiable domain to an owning account.","args":[{"name":"input","description":"Parameters for AddVerifiableDomain","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"AddVerifiableDomainInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"AddVerifiableDomainPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"approveVerifiableDomain","description":"Approve a verifiable domain for notification delivery.","args":[{"name":"input","description":"Parameters for ApproveVerifiableDomain","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ApproveVerifiableDomainInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ApproveVerifiableDomainPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"archiveRepository","description":"Marks a repository as archived.","args":[{"name":"input","description":"Parameters for ArchiveRepository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ArchiveRepositoryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ArchiveRepositoryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"cancelEnterpriseAdminInvitation","description":"Cancels a pending invitation for an administrator to join an enterprise.","args":[{"name":"input","description":"Parameters for CancelEnterpriseAdminInvitation","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CancelEnterpriseAdminInvitationInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CancelEnterpriseAdminInvitationPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"changeUserStatus","description":"Update your status on GitHub.","args":[{"name":"input","description":"Parameters for ChangeUserStatus","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ChangeUserStatusInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ChangeUserStatusPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clearLabelsFromLabelable","description":"Clears all labels from a labelable object.","args":[{"name":"input","description":"Parameters for ClearLabelsFromLabelable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ClearLabelsFromLabelableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ClearLabelsFromLabelablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"cloneProject","description":"Creates a new project by cloning configuration from an existing project.","args":[{"name":"input","description":"Parameters for CloneProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CloneProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CloneProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"cloneTemplateRepository","description":"Create a new repository with the same files and directory structure as a template repository.","args":[{"name":"input","description":"Parameters for CloneTemplateRepository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CloneTemplateRepositoryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CloneTemplateRepositoryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"closeIssue","description":"Close an issue.","args":[{"name":"input","description":"Parameters for CloseIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CloseIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CloseIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"closePullRequest","description":"Close a pull request.","args":[{"name":"input","description":"Parameters for ClosePullRequest","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ClosePullRequestInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ClosePullRequestPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"convertProjectCardNoteToIssue","description":"Convert a project note card to one associated with a newly created issue.","args":[{"name":"input","description":"Parameters for ConvertProjectCardNoteToIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ConvertProjectCardNoteToIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ConvertProjectCardNoteToIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createBranchProtectionRule","description":"Create a new branch protection rule","args":[{"name":"input","description":"Parameters for CreateBranchProtectionRule","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateBranchProtectionRuleInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateBranchProtectionRulePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createCheckRun","description":"Create a check run.","args":[{"name":"input","description":"Parameters for CreateCheckRun","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateCheckRunInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateCheckRunPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createCheckSuite","description":"Create a check suite","args":[{"name":"input","description":"Parameters for CreateCheckSuite","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateCheckSuiteInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateCheckSuitePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createEnterpriseOrganization","description":"Creates an organization as part of an enterprise account.","args":[{"name":"input","description":"Parameters for CreateEnterpriseOrganization","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateEnterpriseOrganizationInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateEnterpriseOrganizationPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createIpAllowListEntry","description":"Creates a new IP allow list entry.","args":[{"name":"input","description":"Parameters for CreateIpAllowListEntry","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateIpAllowListEntryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateIpAllowListEntryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createIssue","description":"Creates a new issue.","args":[{"name":"input","description":"Parameters for CreateIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createProject","description":"Creates a new project.","args":[{"name":"input","description":"Parameters for CreateProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createPullRequest","description":"Create a new pull request","args":[{"name":"input","description":"Parameters for CreatePullRequest","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreatePullRequestInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreatePullRequestPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createRef","description":"Create a new Git Ref.","args":[{"name":"input","description":"Parameters for CreateRef","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateRefInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateRefPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createRepository","description":"Create a new repository.","args":[{"name":"input","description":"Parameters for CreateRepository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateRepositoryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateRepositoryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createTeamDiscussion","description":"Creates a new team discussion.","args":[{"name":"input","description":"Parameters for CreateTeamDiscussion","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateTeamDiscussionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateTeamDiscussionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createTeamDiscussionComment","description":"Creates a new team discussion comment.","args":[{"name":"input","description":"Parameters for CreateTeamDiscussionComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CreateTeamDiscussionCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CreateTeamDiscussionCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"declineTopicSuggestion","description":"Rejects a suggested topic for the repository.","args":[{"name":"input","description":"Parameters for DeclineTopicSuggestion","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeclineTopicSuggestionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeclineTopicSuggestionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteBranchProtectionRule","description":"Delete a branch protection rule","args":[{"name":"input","description":"Parameters for DeleteBranchProtectionRule","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteBranchProtectionRuleInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteBranchProtectionRulePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteDeployment","description":"Deletes a deployment.","args":[{"name":"input","description":"Parameters for DeleteDeployment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteDeploymentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteDeploymentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteIpAllowListEntry","description":"Deletes an IP allow list entry.","args":[{"name":"input","description":"Parameters for DeleteIpAllowListEntry","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteIpAllowListEntryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteIpAllowListEntryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteIssue","description":"Deletes an Issue object.","args":[{"name":"input","description":"Parameters for DeleteIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteIssueComment","description":"Deletes an IssueComment object.","args":[{"name":"input","description":"Parameters for DeleteIssueComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteIssueCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteIssueCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteProject","description":"Deletes a project.","args":[{"name":"input","description":"Parameters for DeleteProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteProjectCard","description":"Deletes a project card.","args":[{"name":"input","description":"Parameters for DeleteProjectCard","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteProjectCardInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteProjectCardPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteProjectColumn","description":"Deletes a project column.","args":[{"name":"input","description":"Parameters for DeleteProjectColumn","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteProjectColumnInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteProjectColumnPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletePullRequestReview","description":"Deletes a pull request review.","args":[{"name":"input","description":"Parameters for DeletePullRequestReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeletePullRequestReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeletePullRequestReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletePullRequestReviewComment","description":"Deletes a pull request review comment.","args":[{"name":"input","description":"Parameters for DeletePullRequestReviewComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeletePullRequestReviewCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeletePullRequestReviewCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteRef","description":"Delete a Git Ref.","args":[{"name":"input","description":"Parameters for DeleteRef","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteRefInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteRefPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteTeamDiscussion","description":"Deletes a team discussion.","args":[{"name":"input","description":"Parameters for DeleteTeamDiscussion","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteTeamDiscussionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteTeamDiscussionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteTeamDiscussionComment","description":"Deletes a team discussion comment.","args":[{"name":"input","description":"Parameters for DeleteTeamDiscussionComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteTeamDiscussionCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteTeamDiscussionCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteVerifiableDomain","description":"Deletes a verifiable domain.","args":[{"name":"input","description":"Parameters for DeleteVerifiableDomain","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DeleteVerifiableDomainInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DeleteVerifiableDomainPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"disablePullRequestAutoMerge","description":"Disable auto merge on the given pull request","args":[{"name":"input","description":"Parameters for DisablePullRequestAutoMerge","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DisablePullRequestAutoMergeInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DisablePullRequestAutoMergePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismissPullRequestReview","description":"Dismisses an approved or rejected pull request review.","args":[{"name":"input","description":"Parameters for DismissPullRequestReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"DismissPullRequestReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"DismissPullRequestReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enablePullRequestAutoMerge","description":"Enable the default auto-merge on a pull request.","args":[{"name":"input","description":"Parameters for EnablePullRequestAutoMerge","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"EnablePullRequestAutoMergeInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"EnablePullRequestAutoMergePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"followUser","description":"Follow a user.","args":[{"name":"input","description":"Parameters for FollowUser","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"FollowUserInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"FollowUserPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"inviteEnterpriseAdmin","description":"Invite someone to become an administrator of the enterprise.","args":[{"name":"input","description":"Parameters for InviteEnterpriseAdmin","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"InviteEnterpriseAdminInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"InviteEnterpriseAdminPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"linkRepositoryToProject","description":"Creates a repository link for a project.","args":[{"name":"input","description":"Parameters for LinkRepositoryToProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"LinkRepositoryToProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LinkRepositoryToProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lockLockable","description":"Lock a lockable object","args":[{"name":"input","description":"Parameters for LockLockable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"LockLockableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LockLockablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"markFileAsViewed","description":"Mark a pull request file as viewed","args":[{"name":"input","description":"Parameters for MarkFileAsViewed","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MarkFileAsViewedInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MarkFileAsViewedPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"markPullRequestReadyForReview","description":"Marks a pull request ready for review.","args":[{"name":"input","description":"Parameters for MarkPullRequestReadyForReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MarkPullRequestReadyForReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MarkPullRequestReadyForReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeBranch","description":"Merge a head into a branch.","args":[{"name":"input","description":"Parameters for MergeBranch","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MergeBranchInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MergeBranchPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergePullRequest","description":"Merge a pull request.","args":[{"name":"input","description":"Parameters for MergePullRequest","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MergePullRequestInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MergePullRequestPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizeComment","description":"Minimizes a comment on an Issue, Commit, Pull Request, or Gist","args":[{"name":"input","description":"Parameters for MinimizeComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MinimizeCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MinimizeCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"moveProjectCard","description":"Moves a project card to another place.","args":[{"name":"input","description":"Parameters for MoveProjectCard","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MoveProjectCardInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MoveProjectCardPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"moveProjectColumn","description":"Moves a project column to another place.","args":[{"name":"input","description":"Parameters for MoveProjectColumn","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"MoveProjectColumnInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MoveProjectColumnPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pinIssue","description":"Pin an issue to a repository","args":[{"name":"input","description":"Parameters for PinIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"PinIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PinIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"regenerateEnterpriseIdentityProviderRecoveryCodes","description":"Regenerates the identity provider recovery codes for an enterprise","args":[{"name":"input","description":"Parameters for RegenerateEnterpriseIdentityProviderRecoveryCodes","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RegenerateEnterpriseIdentityProviderRecoveryCodesInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RegenerateEnterpriseIdentityProviderRecoveryCodesPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"regenerateVerifiableDomainToken","description":"Regenerates a verifiable domain's verification token.","args":[{"name":"input","description":"Parameters for RegenerateVerifiableDomainToken","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RegenerateVerifiableDomainTokenInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RegenerateVerifiableDomainTokenPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeAssigneesFromAssignable","description":"Removes assignees from an assignable object.","args":[{"name":"input","description":"Parameters for RemoveAssigneesFromAssignable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveAssigneesFromAssignableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveAssigneesFromAssignablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeEnterpriseAdmin","description":"Removes an administrator from the enterprise.","args":[{"name":"input","description":"Parameters for RemoveEnterpriseAdmin","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseAdminInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveEnterpriseAdminPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeEnterpriseIdentityProvider","description":"Removes the identity provider from an enterprise","args":[{"name":"input","description":"Parameters for RemoveEnterpriseIdentityProvider","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseIdentityProviderInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveEnterpriseIdentityProviderPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeEnterpriseOrganization","description":"Removes an organization from the enterprise","args":[{"name":"input","description":"Parameters for RemoveEnterpriseOrganization","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseOrganizationInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveEnterpriseOrganizationPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeEnterpriseSupportEntitlement","description":"Removes a support entitlement from an enterprise member.","args":[{"name":"input","description":"Parameters for RemoveEnterpriseSupportEntitlement","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseSupportEntitlementInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveEnterpriseSupportEntitlementPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeLabelsFromLabelable","description":"Removes labels from a Labelable object.","args":[{"name":"input","description":"Parameters for RemoveLabelsFromLabelable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveLabelsFromLabelableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveLabelsFromLabelablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeOutsideCollaborator","description":"Removes outside collaborator from all repositories in an organization.","args":[{"name":"input","description":"Parameters for RemoveOutsideCollaborator","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveOutsideCollaboratorInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveOutsideCollaboratorPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeReaction","description":"Removes a reaction from a subject.","args":[{"name":"input","description":"Parameters for RemoveReaction","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveReactionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveReactionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removeStar","description":"Removes a star from a Starrable.","args":[{"name":"input","description":"Parameters for RemoveStar","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RemoveStarInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RemoveStarPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reopenIssue","description":"Reopen a issue.","args":[{"name":"input","description":"Parameters for ReopenIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ReopenIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ReopenIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reopenPullRequest","description":"Reopen a pull request.","args":[{"name":"input","description":"Parameters for ReopenPullRequest","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ReopenPullRequestInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ReopenPullRequestPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requestReviews","description":"Set review requests on a pull request.","args":[{"name":"input","description":"Parameters for RequestReviews","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RequestReviewsInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RequestReviewsPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"rerequestCheckSuite","description":"Rerequests an existing check suite.","args":[{"name":"input","description":"Parameters for RerequestCheckSuite","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RerequestCheckSuiteInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RerequestCheckSuitePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resolveReviewThread","description":"Marks a review thread as resolved.","args":[{"name":"input","description":"Parameters for ResolveReviewThread","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"ResolveReviewThreadInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ResolveReviewThreadPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"setEnterpriseIdentityProvider","description":"Creates or updates the identity provider for an enterprise.","args":[{"name":"input","description":"Parameters for SetEnterpriseIdentityProvider","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"SetEnterpriseIdentityProviderInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SetEnterpriseIdentityProviderPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"setOrganizationInteractionLimit","description":"Set an organization level interaction limit for an organization's public repositories.","args":[{"name":"input","description":"Parameters for SetOrganizationInteractionLimit","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"SetOrganizationInteractionLimitInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SetOrganizationInteractionLimitPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"setRepositoryInteractionLimit","description":"Sets an interaction limit setting for a repository.","args":[{"name":"input","description":"Parameters for SetRepositoryInteractionLimit","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"SetRepositoryInteractionLimitInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SetRepositoryInteractionLimitPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"setUserInteractionLimit","description":"Set a user level interaction limit for an user's public repositories.","args":[{"name":"input","description":"Parameters for SetUserInteractionLimit","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"SetUserInteractionLimitInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SetUserInteractionLimitPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"submitPullRequestReview","description":"Submits a pending pull request review.","args":[{"name":"input","description":"Parameters for SubmitPullRequestReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"SubmitPullRequestReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SubmitPullRequestReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"transferIssue","description":"Transfer an issue to a different repository","args":[{"name":"input","description":"Parameters for TransferIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"TransferIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"TransferIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unarchiveRepository","description":"Unarchives a repository.","args":[{"name":"input","description":"Parameters for UnarchiveRepository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnarchiveRepositoryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnarchiveRepositoryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unfollowUser","description":"Unfollow a user.","args":[{"name":"input","description":"Parameters for UnfollowUser","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnfollowUserInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnfollowUserPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unlinkRepositoryFromProject","description":"Deletes a repository link from a project.","args":[{"name":"input","description":"Parameters for UnlinkRepositoryFromProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnlinkRepositoryFromProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnlinkRepositoryFromProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unlockLockable","description":"Unlock a lockable object","args":[{"name":"input","description":"Parameters for UnlockLockable","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnlockLockableInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnlockLockablePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unmarkFileAsViewed","description":"Unmark a pull request file as viewed","args":[{"name":"input","description":"Parameters for UnmarkFileAsViewed","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnmarkFileAsViewedInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnmarkFileAsViewedPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unmarkIssueAsDuplicate","description":"Unmark an issue as a duplicate of another issue.","args":[{"name":"input","description":"Parameters for UnmarkIssueAsDuplicate","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnmarkIssueAsDuplicateInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnmarkIssueAsDuplicatePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unminimizeComment","description":"Unminimizes a comment on an Issue, Commit, Pull Request, or Gist","args":[{"name":"input","description":"Parameters for UnminimizeComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnminimizeCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnminimizeCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unpinIssue","description":"Unpin a pinned issue from a repository","args":[{"name":"input","description":"Parameters for UnpinIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnpinIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnpinIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unresolveReviewThread","description":"Marks a review thread as unresolved.","args":[{"name":"input","description":"Parameters for UnresolveReviewThread","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UnresolveReviewThreadInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UnresolveReviewThreadPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateBranchProtectionRule","description":"Create a new branch protection rule","args":[{"name":"input","description":"Parameters for UpdateBranchProtectionRule","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateBranchProtectionRuleInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateBranchProtectionRulePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateCheckRun","description":"Update a check run","args":[{"name":"input","description":"Parameters for UpdateCheckRun","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateCheckRunInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateCheckRunPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateCheckSuitePreferences","description":"Modifies the settings of an existing check suite","args":[{"name":"input","description":"Parameters for UpdateCheckSuitePreferences","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateCheckSuitePreferencesInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateCheckSuitePreferencesPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseAdministratorRole","description":"Updates the role of an enterprise administrator.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseAdministratorRole","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseAdministratorRoleInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseAdministratorRolePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseAllowPrivateRepositoryForkingSetting","description":"Sets whether private repository forks are enabled for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseAllowPrivateRepositoryForkingSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseDefaultRepositoryPermissionSetting","description":"Sets the default repository permission for organizations in an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseDefaultRepositoryPermissionSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseDefaultRepositoryPermissionSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseDefaultRepositoryPermissionSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanChangeRepositoryVisibilitySetting","description":"Sets whether organization members with admin permissions on a repository can change repository visibility.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanCreateRepositoriesSetting","description":"Sets the members can create repositories setting for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanCreateRepositoriesSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanCreateRepositoriesSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanDeleteIssuesSetting","description":"Sets the members can delete issues setting for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanDeleteIssuesSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanDeleteIssuesSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanDeleteIssuesSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanDeleteRepositoriesSetting","description":"Sets the members can delete repositories setting for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanDeleteRepositoriesSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanInviteCollaboratorsSetting","description":"Sets whether members can invite collaborators are enabled for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanInviteCollaboratorsSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanMakePurchasesSetting","description":"Sets whether or not an organization admin can make purchases.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanMakePurchasesSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanMakePurchasesSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanMakePurchasesSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanUpdateProtectedBranchesSetting","description":"Sets the members can update protected branches setting for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseMembersCanViewDependencyInsightsSetting","description":"Sets the members can view dependency insights for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseMembersCanViewDependencyInsightsSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseOrganizationProjectsSetting","description":"Sets whether organization projects are enabled for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseOrganizationProjectsSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseOrganizationProjectsSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseOrganizationProjectsSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseProfile","description":"Updates an enterprise's profile.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseProfile","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseProfileInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseProfilePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseRepositoryProjectsSetting","description":"Sets whether repository projects are enabled for a enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseRepositoryProjectsSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseRepositoryProjectsSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseRepositoryProjectsSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseTeamDiscussionsSetting","description":"Sets whether team discussions are enabled for an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseTeamDiscussionsSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseTeamDiscussionsSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseTeamDiscussionsSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateEnterpriseTwoFactorAuthenticationRequiredSetting","description":"Sets whether two factor authentication is required for all users in an enterprise.","args":[{"name":"input","description":"Parameters for UpdateEnterpriseTwoFactorAuthenticationRequiredSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateIpAllowListEnabledSetting","description":"Sets whether an IP allow list is enabled on an owner.","args":[{"name":"input","description":"Parameters for UpdateIpAllowListEnabledSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateIpAllowListEnabledSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateIpAllowListEnabledSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateIpAllowListEntry","description":"Updates an IP allow list entry.","args":[{"name":"input","description":"Parameters for UpdateIpAllowListEntry","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateIpAllowListEntryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateIpAllowListEntryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateIssue","description":"Updates an Issue.","args":[{"name":"input","description":"Parameters for UpdateIssue","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateIssueInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateIssuePayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateIssueComment","description":"Updates an IssueComment object.","args":[{"name":"input","description":"Parameters for UpdateIssueComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateIssueCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateIssueCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateNotificationRestrictionSetting","description":"Update the setting to restrict notifications to only verified domains available to an owner.","args":[{"name":"input","description":"Parameters for UpdateNotificationRestrictionSetting","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateNotificationRestrictionSettingInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateNotificationRestrictionSettingPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateProject","description":"Updates an existing project.","args":[{"name":"input","description":"Parameters for UpdateProject","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateProjectInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateProjectPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateProjectCard","description":"Updates an existing project card.","args":[{"name":"input","description":"Parameters for UpdateProjectCard","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateProjectCardInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateProjectCardPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateProjectColumn","description":"Updates an existing project column.","args":[{"name":"input","description":"Parameters for UpdateProjectColumn","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateProjectColumnInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateProjectColumnPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatePullRequest","description":"Update a pull request","args":[{"name":"input","description":"Parameters for UpdatePullRequest","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdatePullRequestInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdatePullRequestPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatePullRequestReview","description":"Updates the body of a pull request review.","args":[{"name":"input","description":"Parameters for UpdatePullRequestReview","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdatePullRequestReviewInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdatePullRequestReviewPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatePullRequestReviewComment","description":"Updates a pull request review comment.","args":[{"name":"input","description":"Parameters for UpdatePullRequestReviewComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdatePullRequestReviewCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdatePullRequestReviewCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateRef","description":"Update a Git Ref.","args":[{"name":"input","description":"Parameters for UpdateRef","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateRefInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateRefPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateRepository","description":"Update information about a repository.","args":[{"name":"input","description":"Parameters for UpdateRepository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateRepositoryInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateRepositoryPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateSubscription","description":"Updates the state for subscribable subjects.","args":[{"name":"input","description":"Parameters for UpdateSubscription","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateSubscriptionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateSubscriptionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateTeamDiscussion","description":"Updates a team discussion.","args":[{"name":"input","description":"Parameters for UpdateTeamDiscussion","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateTeamDiscussionInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateTeamDiscussionPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateTeamDiscussionComment","description":"Updates a discussion comment.","args":[{"name":"input","description":"Parameters for UpdateTeamDiscussionComment","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateTeamDiscussionCommentInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateTeamDiscussionCommentPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updateTopics","description":"Replaces the repository's topics with the given topics.","args":[{"name":"input","description":"Parameters for UpdateTopics","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"UpdateTopicsInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UpdateTopicsPayload","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"verifyVerifiableDomain","description":"Verify that a verifiable domain has the expected DNS record.","args":[{"name":"input","description":"Parameters for VerifyVerifiableDomain","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"VerifyVerifiableDomainInput","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"VerifyVerifiableDomainPayload","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Node","description":"An object with an ID.","fields":[{"name":"id","description":"ID of the object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"AddedToProjectEvent","ofType":null},{"kind":"OBJECT","name":"App","ofType":null},{"kind":"OBJECT","name":"AssignedEvent","ofType":null},{"kind":"OBJECT","name":"AutoMergeDisabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoMergeEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoRebaseEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoSquashEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutomaticBaseChangeFailedEvent","ofType":null},{"kind":"OBJECT","name":"AutomaticBaseChangeSucceededEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefChangedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"Blob","ofType":null},{"kind":"OBJECT","name":"Bot","ofType":null},{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},{"kind":"OBJECT","name":"CWE","ofType":null},{"kind":"OBJECT","name":"CheckRun","ofType":null},{"kind":"OBJECT","name":"CheckSuite","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"CodeOfConduct","ofType":null},{"kind":"OBJECT","name":"CommentDeletedEvent","ofType":null},{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"CommitCommentThread","ofType":null},{"kind":"OBJECT","name":"ConnectedEvent","ofType":null},{"kind":"OBJECT","name":"ConvertToDraftEvent","ofType":null},{"kind":"OBJECT","name":"ConvertedNoteToIssueEvent","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"DemilestonedEvent","ofType":null},{"kind":"OBJECT","name":"DeployKey","ofType":null},{"kind":"OBJECT","name":"DeployedEvent","ofType":null},{"kind":"OBJECT","name":"Deployment","ofType":null},{"kind":"OBJECT","name":"DeploymentEnvironmentChangedEvent","ofType":null},{"kind":"OBJECT","name":"DeploymentStatus","ofType":null},{"kind":"OBJECT","name":"DisconnectedEvent","ofType":null},{"kind":"OBJECT","name":"Enterprise","ofType":null},{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},{"kind":"OBJECT","name":"EnterpriseIdentityProvider","ofType":null},{"kind":"OBJECT","name":"EnterpriseRepositoryInfo","ofType":null},{"kind":"OBJECT","name":"EnterpriseServerInstallation","ofType":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccount","ofType":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountEmail","ofType":null},{"kind":"OBJECT","name":"EnterpriseServerUserAccountsUpload","ofType":null},{"kind":"OBJECT","name":"EnterpriseUserAccount","ofType":null},{"kind":"OBJECT","name":"ExternalIdentity","ofType":null},{"kind":"OBJECT","name":"Gist","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"HeadRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefRestoredEvent","ofType":null},{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"Label","ofType":null},{"kind":"OBJECT","name":"LabeledEvent","ofType":null},{"kind":"OBJECT","name":"Language","ofType":null},{"kind":"OBJECT","name":"License","ofType":null},{"kind":"OBJECT","name":"LockedEvent","ofType":null},{"kind":"OBJECT","name":"Mannequin","ofType":null},{"kind":"OBJECT","name":"MarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"MarketplaceCategory","ofType":null},{"kind":"OBJECT","name":"MarketplaceListing","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MentionedEvent","ofType":null},{"kind":"OBJECT","name":"MergedEvent","ofType":null},{"kind":"OBJECT","name":"Milestone","ofType":null},{"kind":"OBJECT","name":"MilestonedEvent","ofType":null},{"kind":"OBJECT","name":"MovedColumnsInProjectEvent","ofType":null},{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgBlockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveOutsideCollaboratorAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUnblockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateDefaultRepositoryPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"OrganizationIdentityProvider","ofType":null},{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null},{"kind":"OBJECT","name":"Package","ofType":null},{"kind":"OBJECT","name":"PackageFile","ofType":null},{"kind":"OBJECT","name":"PackageTag","ofType":null},{"kind":"OBJECT","name":"PackageVersion","ofType":null},{"kind":"OBJECT","name":"PinnedEvent","ofType":null},{"kind":"OBJECT","name":"PinnedIssue","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"Project","ofType":null},{"kind":"OBJECT","name":"ProjectCard","ofType":null},{"kind":"OBJECT","name":"ProjectColumn","ofType":null},{"kind":"OBJECT","name":"PublicKey","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestCommit","ofType":null},{"kind":"OBJECT","name":"PullRequestCommitCommentThread","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},{"kind":"OBJECT","name":"Push","ofType":null},{"kind":"OBJECT","name":"PushAllowance","ofType":null},{"kind":"OBJECT","name":"Reaction","ofType":null},{"kind":"OBJECT","name":"ReadyForReviewEvent","ofType":null},{"kind":"OBJECT","name":"Ref","ofType":null},{"kind":"OBJECT","name":"ReferencedEvent","ofType":null},{"kind":"OBJECT","name":"Release","ofType":null},{"kind":"OBJECT","name":"ReleaseAsset","ofType":null},{"kind":"OBJECT","name":"RemovedFromProjectEvent","ofType":null},{"kind":"OBJECT","name":"RenamedTitleEvent","ofType":null},{"kind":"OBJECT","name":"ReopenedEvent","ofType":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"RepositoryInvitation","ofType":null},{"kind":"OBJECT","name":"RepositoryTopic","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVulnerabilityAlert","ofType":null},{"kind":"OBJECT","name":"ReviewDismissalAllowance","ofType":null},{"kind":"OBJECT","name":"ReviewDismissedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequest","ofType":null},{"kind":"OBJECT","name":"ReviewRequestRemovedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequestedEvent","ofType":null},{"kind":"OBJECT","name":"SavedReply","ofType":null},{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null},{"kind":"OBJECT","name":"SponsorsListing","ofType":null},{"kind":"OBJECT","name":"SponsorsTier","ofType":null},{"kind":"OBJECT","name":"Sponsorship","ofType":null},{"kind":"OBJECT","name":"Status","ofType":null},{"kind":"OBJECT","name":"StatusCheckRollup","ofType":null},{"kind":"OBJECT","name":"StatusContext","ofType":null},{"kind":"OBJECT","name":"SubscribedEvent","ofType":null},{"kind":"OBJECT","name":"Tag","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"Topic","ofType":null},{"kind":"OBJECT","name":"TransferredEvent","ofType":null},{"kind":"OBJECT","name":"Tree","ofType":null},{"kind":"OBJECT","name":"UnassignedEvent","ofType":null},{"kind":"OBJECT","name":"UnlabeledEvent","ofType":null},{"kind":"OBJECT","name":"UnlockedEvent","ofType":null},{"kind":"OBJECT","name":"UnmarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"UnpinnedEvent","ofType":null},{"kind":"OBJECT","name":"UnsubscribedEvent","ofType":null},{"kind":"OBJECT","name":"User","ofType":null},{"kind":"OBJECT","name":"UserBlockedEvent","ofType":null},{"kind":"OBJECT","name":"UserContentEdit","ofType":null},{"kind":"OBJECT","name":"UserStatus","ofType":null},{"kind":"OBJECT","name":"VerifiableDomain","ofType":null}]},{"kind":"ENUM","name":"NotificationRestrictionSettingValue","description":"The possible values for the notification restriction setting.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ENABLED","description":"The setting is enabled for the owner.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"The setting is disabled for the owner.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"OauthApplicationAuditEntryData","description":"Metadata for an audit entry with action oauth_application.*","fields":[{"name":"oauthApplicationName","description":"The name of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationResourcePath","description":"The HTTP path for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationUrl","description":"The HTTP URL for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","description":"Audit log entry for a oauth_application.create event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"applicationUrl","description":"The application URL of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"callbackUrl","description":"The callback URL of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationName","description":"The name of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationResourcePath","description":"The HTTP path for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationUrl","description":"The HTTP URL for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"rateLimit","description":"The rate limit of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of the OAuth Application.","args":[],"type":{"kind":"ENUM","name":"OauthApplicationCreateAuditEntryState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OauthApplicationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OauthApplicationCreateAuditEntryState","description":"The state of an OAuth Application when it was created.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ACTIVE","description":"The OAuth Application was active and allowed to have OAuth Accesses.","isDeprecated":false,"deprecationReason":null},{"name":"SUSPENDED","description":"The OAuth Application was suspended from generating OAuth Accesses due to abuse or security concerns.","isDeprecated":false,"deprecationReason":null},{"name":"PENDING_DELETION","description":"The OAuth Application was in the process of being deleted.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OperationType","description":"The corresponding operation type for the action","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ACCESS","description":"An existing resource was accessed","isDeprecated":false,"deprecationReason":null},{"name":"AUTHENTICATION","description":"A resource performed an authentication event","isDeprecated":false,"deprecationReason":null},{"name":"CREATE","description":"A new resource was created","isDeprecated":false,"deprecationReason":null},{"name":"MODIFY","description":"An existing resource was modified","isDeprecated":false,"deprecationReason":null},{"name":"REMOVE","description":"An existing resource was removed","isDeprecated":false,"deprecationReason":null},{"name":"RESTORE","description":"An existing resource was restored","isDeprecated":false,"deprecationReason":null},{"name":"TRANSFER","description":"An existing resource was transferred between multiple resources","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OrderDirection","description":"Possible directions in which to order a list of items when provided an `orderBy` argument.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ASC","description":"Specifies an ascending order for a given `orderBy` argument.","isDeprecated":false,"deprecationReason":null},{"name":"DESC","description":"Specifies a descending order for a given `orderBy` argument.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgAddBillingManagerAuditEntry","description":"Audit log entry for a org.add_billing_manager","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitationEmail","description":"The email address used to invite a billing manager for the organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgAddMemberAuditEntry","description":"Audit log entry for a org.add_member","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The permission level of the member added to the organization.","args":[],"type":{"kind":"ENUM","name":"OrgAddMemberAuditEntryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgAddMemberAuditEntryPermission","description":"The permissions available to members on an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"READ","description":"Can read and clone repositories.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Can read, clone, push, and add collaborators to repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgBlockUserAuditEntry","description":"Audit log entry for a org.block_user","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUser","description":"The blocked user.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserName","description":"The username of the blocked user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserResourcePath","description":"The HTTP path for the blocked user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserUrl","description":"The HTTP URL for the blocked user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgConfigDisableCollaboratorsOnlyAuditEntry","description":"Audit log entry for a org.config.disable_collaborators_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgConfigEnableCollaboratorsOnlyAuditEntry","description":"Audit log entry for a org.config.enable_collaborators_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgCreateAuditEntry","description":"Audit log entry for a org.create event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"billingPlan","description":"The billing plan for the Organization.","args":[],"type":{"kind":"ENUM","name":"OrgCreateAuditEntryBillingPlan","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgCreateAuditEntryBillingPlan","description":"The billing plans available for organizations.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"FREE","description":"Free Plan","isDeprecated":false,"deprecationReason":null},{"name":"BUSINESS","description":"Team Plan","isDeprecated":false,"deprecationReason":null},{"name":"BUSINESS_PLUS","description":"Enterprise Cloud Plan","isDeprecated":false,"deprecationReason":null},{"name":"UNLIMITED","description":"Legacy Unlimited Plan","isDeprecated":false,"deprecationReason":null},{"name":"TIERED_PER_SEAT","description":"Tiered Per Seat Plan","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgDisableOauthAppRestrictionsAuditEntry","description":"Audit log entry for a org.disable_oauth_app_restrictions event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgDisableSamlAuditEntry","description":"Audit log entry for a org.disable_saml event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"digestMethodUrl","description":"The SAML provider's digest algorithm URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issuerUrl","description":"The SAML provider's issuer URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"signatureMethodUrl","description":"The SAML provider's signature algorithm URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"singleSignOnUrl","description":"The SAML provider's single sign-on URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgDisableTwoFactorRequirementAuditEntry","description":"Audit log entry for a org.disable_two_factor_requirement event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgEnableOauthAppRestrictionsAuditEntry","description":"Audit log entry for a org.enable_oauth_app_restrictions event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgEnableSamlAuditEntry","description":"Audit log entry for a org.enable_saml event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"digestMethodUrl","description":"The SAML provider's digest algorithm URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issuerUrl","description":"The SAML provider's issuer URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"signatureMethodUrl","description":"The SAML provider's signature algorithm URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"singleSignOnUrl","description":"The SAML provider's single sign-on URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgEnableTwoFactorRequirementAuditEntry","description":"Audit log entry for a org.enable_two_factor_requirement event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgInviteMemberAuditEntry","description":"Audit log entry for a org.invite_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The email address of the organization invitation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationInvitation","description":"The organization invitation.","args":[],"type":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","description":"Audit log entry for a org.invite_to_business event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","description":"Audit log entry for a org.oauth_app_access_approved event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationName","description":"The name of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationResourcePath","description":"The HTTP path for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationUrl","description":"The HTTP URL for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OauthApplicationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","description":"Audit log entry for a org.oauth_app_access_denied event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationName","description":"The name of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationResourcePath","description":"The HTTP path for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationUrl","description":"The HTTP URL for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OauthApplicationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","description":"Audit log entry for a org.oauth_app_access_requested event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationName","description":"The name of the OAuth Application.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationResourcePath","description":"The HTTP path for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oauthApplicationUrl","description":"The HTTP URL for the OAuth Application","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OauthApplicationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgRemoveBillingManagerAuditEntry","description":"Audit log entry for a org.remove_billing_manager event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reason","description":"The reason for the billing manager being removed.","args":[],"type":{"kind":"ENUM","name":"OrgRemoveBillingManagerAuditEntryReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgRemoveBillingManagerAuditEntryReason","description":"The reason a billing manager was removed from an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE","description":"The organization required 2FA of its billing managers and this user did not have 2FA enabled.","isDeprecated":false,"deprecationReason":null},{"name":"SAML_EXTERNAL_IDENTITY_MISSING","description":"SAML external identity missing","isDeprecated":false,"deprecationReason":null},{"name":"SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY","description":"SAML SSO enforcement requires an external identity","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgRemoveMemberAuditEntry","description":"Audit log entry for a org.remove_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membershipTypes","description":"The types of membership the member has with the organization.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrgRemoveMemberAuditEntryMembershipType","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reason","description":"The reason for the member being removed.","args":[],"type":{"kind":"ENUM","name":"OrgRemoveMemberAuditEntryReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgRemoveMemberAuditEntryMembershipType","description":"The type of membership a user has with an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"DIRECT_MEMBER","description":"A direct member is a user that is a member of the Organization.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Organization administrators have full access and can change several settings, including the names of repositories that belong to the Organization and Owners team membership. In addition, organization admins can delete the organization and all of its repositories.","isDeprecated":false,"deprecationReason":null},{"name":"BILLING_MANAGER","description":"A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.","isDeprecated":false,"deprecationReason":null},{"name":"UNAFFILIATED","description":"An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the Organization.","isDeprecated":false,"deprecationReason":null},{"name":"OUTSIDE_COLLABORATOR","description":"An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OrgRemoveMemberAuditEntryReason","description":"The reason a member was removed from an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE","description":"The organization required 2FA of its billing managers and this user did not have 2FA enabled.","isDeprecated":false,"deprecationReason":null},{"name":"SAML_EXTERNAL_IDENTITY_MISSING","description":"SAML external identity missing","isDeprecated":false,"deprecationReason":null},{"name":"SAML_SSO_ENFORCEMENT_REQUIRES_EXTERNAL_IDENTITY","description":"SAML SSO enforcement requires an external identity","isDeprecated":false,"deprecationReason":null},{"name":"USER_ACCOUNT_DELETED","description":"User account has been deleted","isDeprecated":false,"deprecationReason":null},{"name":"TWO_FACTOR_ACCOUNT_RECOVERY","description":"User was removed from organization during account recovery","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgRemoveOutsideCollaboratorAuditEntry","description":"Audit log entry for a org.remove_outside_collaborator event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membershipTypes","description":"The types of membership the outside collaborator has with the organization.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrgRemoveOutsideCollaboratorAuditEntryMembershipType","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reason","description":"The reason for the outside collaborator being removed from the Organization.","args":[],"type":{"kind":"ENUM","name":"OrgRemoveOutsideCollaboratorAuditEntryReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgRemoveOutsideCollaboratorAuditEntryMembershipType","description":"The type of membership a user has with an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OUTSIDE_COLLABORATOR","description":"An outside collaborator is a person who isn't explicitly a member of the Organization, but who has Read, Write, or Admin permissions to one or more repositories in the organization.","isDeprecated":false,"deprecationReason":null},{"name":"UNAFFILIATED","description":"An unaffiliated collaborator is a person who is not a member of the Organization and does not have access to any repositories in the organization.","isDeprecated":false,"deprecationReason":null},{"name":"BILLING_MANAGER","description":"A billing manager is a user who manages the billing settings for the Organization, such as updating payment information.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OrgRemoveOutsideCollaboratorAuditEntryReason","description":"The reason an outside collaborator was removed from an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TWO_FACTOR_REQUIREMENT_NON_COMPLIANCE","description":"The organization required 2FA of its billing managers and this user did not have 2FA enabled.","isDeprecated":false,"deprecationReason":null},{"name":"SAML_EXTERNAL_IDENTITY_MISSING","description":"SAML external identity missing","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgRestoreMemberAuditEntry","description":"Audit log entry for a org.restore_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredCustomEmailRoutingsCount","description":"The number of custom email routings for the restored member.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredIssueAssignmentsCount","description":"The number of issue assignments for the restored member.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredMemberships","description":"Restored organization membership objects.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"OrgRestoreMemberAuditEntryMembership","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"restoredMembershipsCount","description":"The number of restored memberships.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredRepositoriesCount","description":"The number of repositories of the restored member.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredRepositoryStarsCount","description":"The number of starred repositories for the restored member.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"restoredRepositoryWatchesCount","description":"The number of watched repositories for the restored member.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"OrgRestoreMemberAuditEntryMembership","description":"Types of memberships that can be restored for an Organization member.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"OrgRestoreMemberMembershipOrganizationAuditEntryData","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipRepositoryAuditEntryData","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipTeamAuditEntryData","ofType":null}]},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipOrganizationAuditEntryData","description":"Metadata for an organization membership for org.restore_member actions","fields":[{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipRepositoryAuditEntryData","description":"Metadata for a repository membership for org.restore_member actions","fields":[{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipTeamAuditEntryData","description":"Metadata for a team membership for org.restore_member actions","fields":[{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgUnblockUserAuditEntry","description":"Audit log entry for a org.unblock_user","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUser","description":"The user being unblocked by the organization.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserName","description":"The username of the blocked user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserResourcePath","description":"The HTTP path for the blocked user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockedUserUrl","description":"The HTTP URL for the blocked user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrgUpdateDefaultRepositoryPermissionAuditEntry","description":"Audit log entry for a org.update_default_repository_permission","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The new default repository permission level for the organization.","args":[],"type":{"kind":"ENUM","name":"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permissionWas","description":"The former default repository permission level for the organization.","args":[],"type":{"kind":"ENUM","name":"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgUpdateDefaultRepositoryPermissionAuditEntryPermission","description":"The default permission a repository can have in an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"READ","description":"Can read and clone repositories.","isDeprecated":false,"deprecationReason":null},{"name":"WRITE","description":"Can read, clone and push to repositories.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Can read, clone, push, and add collaborators to repositories.","isDeprecated":false,"deprecationReason":null},{"name":"NONE","description":"No default permission value.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgUpdateMemberAuditEntry","description":"Audit log entry for a org.update_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The new member permission level for the organization.","args":[],"type":{"kind":"ENUM","name":"OrgUpdateMemberAuditEntryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permissionWas","description":"The former member permission level for the organization.","args":[],"type":{"kind":"ENUM","name":"OrgUpdateMemberAuditEntryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgUpdateMemberAuditEntryPermission","description":"The permissions available to members on an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"READ","description":"Can read and clone repositories.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"Can read, clone, push, and add collaborators to repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntry","description":"Audit log entry for a org.update_member_repository_creation_permission event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"canCreateRepositories","description":"Can members create repositories in the organization.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The permission for visibility level of repositories for this organization.","args":[],"type":{"kind":"ENUM","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntryVisibility","description":"The permissions available for repository creation on an Organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ALL","description":"All organization members are restricted from creating any repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"All organization members are restricted from creating public repositories.","isDeprecated":false,"deprecationReason":null},{"name":"NONE","description":"All organization members are allowed to create any repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"All organization members are restricted from creating private repositories.","isDeprecated":false,"deprecationReason":null},{"name":"INTERNAL","description":"All organization members are restricted from creating internal repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC_INTERNAL","description":"All organization members are restricted from creating public or internal repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE_INTERNAL","description":"All organization members are restricted from creating private or internal repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC_PRIVATE","description":"All organization members are restricted from creating public or private repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry","description":"Audit log entry for a org.update_member_repository_invitation_permission event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"canInviteOutsideCollaboratorsToRepositories","description":"Can outside collaborators be invited to repositories in the organization.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Organization","description":"An account on GitHub, with one or more owners, that has repositories, members and teams.","fields":[{"name":"anyPinnableItems","description":"Determine if this repository owner has any items that can be pinned to their profile.","args":[{"name":"type","description":"Filter to only a particular kind of pinnable item.","type":{"kind":"ENUM","name":"PinnableItemType","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"auditLog","description":"Audit log entries of the organization","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"query","description":"The query string to filter audit entries","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for the returned audit log entries.","type":{"kind":"INPUT_OBJECT","name":"AuditLogOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationAuditEntryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"avatarUrl","description":"A URL pointing to the organization's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The organization's public profile description.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The organization's public profile description rendered to HTML.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"domains","description":"A list of domains owned by the organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isVerified","description":"Filter by if the domain is verified.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"null"},{"name":"orderBy","description":"Ordering options for verifiable domains returned.","type":{"kind":"INPUT_OBJECT","name":"VerifiableDomainOrder","ofType":null},"defaultValue":"{field: DOMAIN, direction: ASC}"}],"type":{"kind":"OBJECT","name":"VerifiableDomainConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The organization's public email.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"hasSponsorsListing","description":"True if this user/organization has a GitHub Sponsors listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"interactionAbility","description":"The interaction ability settings for this organization.","args":[],"type":{"kind":"OBJECT","name":"RepositoryInteractionAbility","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEnabledSetting","description":"The setting value for whether the organization has an IP allow list enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IpAllowListEnabledSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEntries","description":"The IP addresses that are allowed to access resources owned by the organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for IP allow list entries returned.","type":{"kind":"INPUT_OBJECT","name":"IpAllowListEntryOrder","ofType":null},"defaultValue":"{field: ALLOW_LIST_VALUE, direction: ASC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IpAllowListEntryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoredBy","description":"Check if the given account is sponsoring this user/organization.","args":[{"name":"accountLogin","description":"The target account's login.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoringViewer","description":"True if the viewer is sponsored by this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isVerified","description":"Whether the organization has verified its profile email and website.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"itemShowcase","description":"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProfileItemShowcase","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"location","description":"The organization's public profile location.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The organization's login name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"memberStatuses","description":"Get the status messages members of this entity have set that are either public or visible only to the organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for user statuses returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"UserStatusOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserStatusConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersWithRole","description":"A list of users who are members of this organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationMemberConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The organization's public profile name.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"newTeamResourcePath","description":"The HTTP path creating a new team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"newTeamUrl","description":"The HTTP URL creating a new team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"notificationDeliveryRestrictionEnabledSetting","description":"Indicates if email notification delivery for this organization is restricted to verified domains.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"NotificationRestrictionSettingValue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organizationBillingEmail","description":"The billing email for the organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"packages","description":"A list of packages under the owner.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"names","description":"Find packages by their names.","type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"Find packages in a repository by ID.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"packageType","description":"Filter registry package by type.","type":{"kind":"ENUM","name":"PackageType","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering of the returned packages.","type":{"kind":"INPUT_OBJECT","name":"PackageOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pendingMembers","description":"A list of users who have been invited to join this organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnableItems","description":"A list of repositories and gists this profile owner can pin to their profile.","args":[{"name":"types","description":"Filter the types of pinnable items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItems","description":"A list of repositories and gists this profile owner has pinned to their profile","args":[{"name":"types","description":"Filter the types of pinned items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItemsRemaining","description":"Returns how many more items this profile owner can pin to their profile.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"Find project by number.","args":[{"name":"number","description":"The project number to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projects","description":"A list of projects under the owner.","args":[{"name":"orderBy","description":"Ordering options for projects returned from the connection","type":{"kind":"INPUT_OBJECT","name":"ProjectOrder","ofType":null},"defaultValue":null},{"name":"search","description":"Query to search projects by, currently only searching by name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"states","description":"A list of states to filter the projects by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectState","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsResourcePath","description":"The HTTP path listing organization's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsUrl","description":"The HTTP URL listing organization's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"A list of repositories that the user owns.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"affiliations","description":"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":null},{"name":"ownerAffiliations","description":"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":"[OWNER, COLLABORATOR]"},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isFork","description":"If non-null, filters repositories according to whether they are forks of another repository","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Find Repository.","args":[{"name":"name","description":"Name of Repository to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requiresTwoFactorAuthentication","description":"When true the organization requires all members, billing managers, and outside collaborators to enable two-factor authentication.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"samlIdentityProvider","description":"The Organization's SAML identity providers","args":[],"type":{"kind":"OBJECT","name":"OrganizationIdentityProvider","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorsListing","description":"The GitHub Sponsors listing for this user or organization.","args":[],"type":{"kind":"OBJECT","name":"SponsorsListing","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipForViewerAsSponsor","description":"The viewer's sponsorship of this entity.","args":[],"type":{"kind":"OBJECT","name":"Sponsorship","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsMaintainer","description":"This object's sponsorships as the maintainer.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"includePrivate","description":"Whether or not to include private sponsorships in the result set","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsSponsor","description":"This object's sponsorships as the sponsor.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"Find an organization's team by its slug.","args":[{"name":"slug","description":"The name or slug of the team to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teams","description":"A list of teams in this organization.","args":[{"name":"privacy","description":"If non-null, filters teams according to privacy","type":{"kind":"ENUM","name":"TeamPrivacy","ofType":null},"defaultValue":null},{"name":"role","description":"If non-null, filters teams according to whether the viewer is an admin or member on team","type":{"kind":"ENUM","name":"TeamRole","ofType":null},"defaultValue":null},{"name":"query","description":"If non-null, filters teams with query on team name and team slug","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"userLogins","description":"User logins to filter by","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for teams returned from the connection","type":{"kind":"INPUT_OBJECT","name":"TeamOrder","ofType":null},"defaultValue":null},{"name":"ldapMapped","description":"If true, filters teams that are mapped to an LDAP Group (Enterprise only)","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"rootTeamsOnly","description":"If true, restrict to only root teams","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsResourcePath","description":"The HTTP path listing organization's teams","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsUrl","description":"The HTTP URL listing organization's teams","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"twitterUsername","description":"The organization's Twitter username.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanAdminister","description":"Organization is adminable by the viewer.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanChangePinnedItems","description":"Can the viewer pin repositories and gists to the profile?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateProjects","description":"Can the current viewer create new projects on this owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateRepositories","description":"Viewer can create repositories on this organization","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateTeams","description":"Viewer can create teams on this organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSponsor","description":"Whether or not the viewer is able to sponsor this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsAMember","description":"Viewer is an active member of this organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsSponsoring","description":"True if the viewer is sponsoring this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"websiteUrl","description":"The organization's public profile URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Actor","ofType":null},{"kind":"INTERFACE","name":"PackageOwner","ofType":null},{"kind":"INTERFACE","name":"ProjectOwner","ofType":null},{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"MemberStatusable","ofType":null},{"kind":"INTERFACE","name":"ProfileOwner","ofType":null},{"kind":"INTERFACE","name":"Sponsorable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"OrganizationAuditEntry","description":"An audit entry in an organization audit log.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgBlockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveOutsideCollaboratorAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUnblockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateDefaultRepositoryPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"OrganizationAuditEntryConnection","description":"The connection type for OrganizationAuditEntry.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationAuditEntryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"OrganizationAuditEntry","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","description":"Metadata for an audit entry with action org.*","fields":[{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"MembersCanDeleteReposClearAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"MembersCanDeleteReposEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"OauthApplicationCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgBlockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgDisableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableOauthAppRestrictionsAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableSamlAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgEnableTwoFactorRequirementAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgInviteToBusinessAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessApprovedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessDeniedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgOauthAppAccessRequestedAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveBillingManagerAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRemoveOutsideCollaboratorAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgRestoreMemberMembershipOrganizationAuditEntryData","ofType":null},{"kind":"OBJECT","name":"OrgUnblockUserAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateDefaultRepositoryPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryCreationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"OrgUpdateMemberRepositoryInvitationPermissionAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"OrganizationAuditEntryEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"OrganizationAuditEntry","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationConnection","description":"The connection type for Organization.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Organization","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationIdentityProvider","description":"An Identity Provider configured to provision SAML and SCIM identities for Organizations","fields":[{"name":"digestMethod","description":"The digest algorithm used to sign SAML requests for the Identity Provider.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"externalIdentities","description":"External Identities provisioned by this Identity Provider","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ExternalIdentityConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"idpCertificate","description":"The x509 certificate used by the Identity Provider to sign assertions and responses.","args":[],"type":{"kind":"SCALAR","name":"X509Certificate","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issuer","description":"The Issuer Entity ID for the SAML Identity Provider","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"Organization this Identity Provider belongs to","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"signatureMethod","description":"The signature algorithm used to sign SAML requests for the Identity Provider.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ssoUrl","description":"The URL endpoint for the Identity Provider's SAML SSO.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationInvitation","description":"An Invitation for a user to an organization.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The email address of the user invited to the organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitationType","description":"The type of invitation that was sent (e.g. email, user).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrganizationInvitationType","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitee","description":"The user who was invited to the organization.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"inviter","description":"The user who created the invitation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization the invite is for","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Organization","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The user's pending role in the organization (e.g. member, owner).","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrganizationInvitationRole","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationInvitationConnection","description":"The connection type for OrganizationInvitation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationInvitationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationInvitationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"OrganizationInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrganizationInvitationRole","description":"The possible organization invitation roles.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"DIRECT_MEMBER","description":"The user is invited to be a direct member of the organization.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"The user is invited to be an admin of the organization.","isDeprecated":false,"deprecationReason":null},{"name":"BILLING_MANAGER","description":"The user is invited to be a billing manager of the organization.","isDeprecated":false,"deprecationReason":null},{"name":"REINSTATE","description":"The user's previous role will be reinstated.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OrganizationInvitationType","description":"The possible organization invitation types.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"USER","description":"The invitation was to an existing user.","isDeprecated":false,"deprecationReason":null},{"name":"EMAIL","description":"The invitation was to an email address.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationMemberConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationMemberEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationMemberEdge","description":"Represents a user within an organization.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasTwoFactorEnabled","description":"Whether the organization member has two factor enabled or not. Returns null if information is not available to viewer.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The role this user has in the organization.","args":[],"type":{"kind":"ENUM","name":"OrganizationMemberRole","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrganizationMemberRole","description":"The possible roles within an organization for its members.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MEMBER","description":"The user is a member of the organization.","isDeprecated":false,"deprecationReason":null},{"name":"ADMIN","description":"The user is an administrator of the organization.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"OrganizationMembersCanCreateRepositoriesSettingValue","description":"The possible values for the members can create repositories setting on an organization.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ALL","description":"Members will be able to create public and private repositories.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"Members will be able to create only private repositories.","isDeprecated":false,"deprecationReason":null},{"name":"DISABLED","description":"Members will not be able to create public or private repositories.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"OrganizationOrder","description":"Ordering options for organization connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order organizations by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrganizationOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"OrganizationOrderField","description":"Properties by which organization connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order organizations by creation time","isDeprecated":false,"deprecationReason":null},{"name":"LOGIN","description":"Order organizations by login","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationTeamsHovercardContext","description":"An organization teams hovercard context","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"relevantTeams","description":"Teams in this organization the user is a member of that are relevant","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsResourcePath","description":"The path for the full team list for this user","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsUrl","description":"The URL for the full team list for this user","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalTeamCount","description":"The total number of teams the user is on in the organization","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"HovercardContext","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"OrganizationsHovercardContext","description":"An organization list hovercard context","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"relevantOrganizations","description":"Organizations this user is a member of that are relevant","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalOrganizationCount","description":"The total number of organizations this user is in","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"HovercardContext","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Package","description":"Information for an uploaded package.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"latestVersion","description":"Find the latest version for the package.","args":[],"type":{"kind":"OBJECT","name":"PackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Identifies the name of the package.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"packageType","description":"Identifies the type of the package.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PackageType","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository this package belongs to.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"statistics","description":"Statistics about package activity.","args":[],"type":{"kind":"OBJECT","name":"PackageStatistics","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"version","description":"Find package version by version string.","args":[{"name":"version","description":"The package version.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"versions","description":"list of versions for this package","args":[{"name":"orderBy","description":"Ordering of the returned packages.","type":{"kind":"INPUT_OBJECT","name":"PackageVersionOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageVersionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageConnection","description":"The connection type for Package.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PackageEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Package","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Package","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageFile","description":"A file in a package version.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"md5","description":"MD5 hash of the file.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Name of the file.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"packageVersion","description":"The package version this file belongs to.","args":[],"type":{"kind":"OBJECT","name":"PackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sha1","description":"SHA1 hash of the file.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sha256","description":"SHA256 hash of the file.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"size","description":"Size of the file in bytes.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"URL to download the asset.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageFileConnection","description":"The connection type for PackageFile.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PackageFileEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PackageFile","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageFileEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PackageFile","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"PackageFileOrder","description":"Ways in which lists of package files can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order package files by.","type":{"kind":"ENUM","name":"PackageFileOrderField","ofType":null},"defaultValue":null},{"name":"direction","description":"The direction in which to order package files by the specified field.","type":{"kind":"ENUM","name":"OrderDirection","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PackageFileOrderField","description":"Properties by which package file connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order package files by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"PackageOrder","description":"Ways in which lists of packages can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order packages by.","type":{"kind":"ENUM","name":"PackageOrderField","ofType":null},"defaultValue":null},{"name":"direction","description":"The direction in which to order packages by the specified field.","type":{"kind":"ENUM","name":"OrderDirection","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PackageOrderField","description":"Properties by which package connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order packages by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"PackageOwner","description":"Represents an owner of a package.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"packages","description":"A list of packages under the owner.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"names","description":"Find packages by their names.","type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"Find packages in a repository by ID.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"packageType","description":"Filter registry package by type.","type":{"kind":"ENUM","name":"PackageType","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering of the returned packages.","type":{"kind":"INPUT_OBJECT","name":"PackageOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"PackageStatistics","description":"Represents a object that contains package activity statistics such as downloads.","fields":[{"name":"downloadsTotalCount","description":"Number of times the package was downloaded since it was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageTag","description":"A version tag contains the mapping between a tag name and a version.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Identifies the tag name of the version.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"version","description":"Version that the tag is associated with.","args":[],"type":{"kind":"OBJECT","name":"PackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PackageType","description":"The possible types of a package.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NPM","description":"An npm package.","isDeprecated":false,"deprecationReason":null},{"name":"RUBYGEMS","description":"A rubygems package.","isDeprecated":false,"deprecationReason":null},{"name":"MAVEN","description":"A maven package.","isDeprecated":false,"deprecationReason":null},{"name":"DOCKER","description":"A docker image.","isDeprecated":false,"deprecationReason":null},{"name":"DEBIAN","description":"A debian package.","isDeprecated":false,"deprecationReason":null},{"name":"NUGET","description":"A nuget package.","isDeprecated":false,"deprecationReason":null},{"name":"PYPI","description":"A python package.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PackageVersion","description":"Information about a specific package version.","fields":[{"name":"files","description":"List of files associated with this package version","args":[{"name":"orderBy","description":"Ordering of the returned package files.","type":{"kind":"INPUT_OBJECT","name":"PackageFileOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageFileConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"package","description":"The package associated with this version.","args":[],"type":{"kind":"OBJECT","name":"Package","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"platform","description":"The platform this version was built for.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"preRelease","description":"Whether or not this version is a pre-release.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"readme","description":"The README of this package version.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"release","description":"The release associated with this package version.","args":[],"type":{"kind":"OBJECT","name":"Release","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"statistics","description":"Statistics about package activity.","args":[],"type":{"kind":"OBJECT","name":"PackageVersionStatistics","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"summary","description":"The package version summary.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"version","description":"The version string.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageVersionConnection","description":"The connection type for PackageVersion.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PackageVersionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PackageVersion","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PackageVersionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"PackageVersionOrder","description":"Ways in which lists of package versions can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order package versions by.","type":{"kind":"ENUM","name":"PackageVersionOrderField","ofType":null},"defaultValue":null},{"name":"direction","description":"The direction in which to order package versions by the specified field.","type":{"kind":"ENUM","name":"OrderDirection","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PackageVersionOrderField","description":"Properties by which package version connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order package versions by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PackageVersionStatistics","description":"Represents a object that contains package version activity statistics such as downloads.","fields":[{"name":"downloadsTotalCount","description":"Number of times the package was downloaded since it was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PageInfo","description":"Information about pagination in a connection.","fields":[{"name":"endCursor","description":"When paginating forwards, the cursor to continue.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"hasNextPage","description":"When paginating forwards, are there more items?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasPreviousPage","description":"When paginating backwards, are there more items?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"startCursor","description":"When paginating backwards, the cursor to continue.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"PermissionGranter","description":"Types that can grant permissions on a repository to a user","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null}]},{"kind":"OBJECT","name":"PermissionSource","description":"A level of permission and source for a user's access to a repository.","fields":[{"name":"organization","description":"The organization the repository belongs to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Organization","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The level of access this source has granted to the user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"DefaultRepositoryPermissionField","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"source","description":"The source of this permission.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"PermissionGranter","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"PinIssueInput","description":"Autogenerated input type of PinIssue","fields":null,"inputFields":[{"name":"issueId","description":"The ID of the issue to be pinned","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PinIssuePayload","description":"Autogenerated return type of PinIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was pinned","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"PinnableItem","description":"Types that can be pinned to a profile page.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Gist","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null}]},{"kind":"OBJECT","name":"PinnableItemConnection","description":"The connection type for PinnableItem.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"PinnableItem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PinnableItemEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"PinnableItem","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PinnableItemType","description":"Represents items that can be pinned to a profile page or dashboard.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"REPOSITORY","description":"A repository.","isDeprecated":false,"deprecationReason":null},{"name":"GIST","description":"A gist.","isDeprecated":false,"deprecationReason":null},{"name":"ISSUE","description":"An issue.","isDeprecated":false,"deprecationReason":null},{"name":"PROJECT","description":"A project.","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST","description":"A pull request.","isDeprecated":false,"deprecationReason":null},{"name":"USER","description":"A user.","isDeprecated":false,"deprecationReason":null},{"name":"ORGANIZATION","description":"An organization.","isDeprecated":false,"deprecationReason":null},{"name":"TEAM","description":"A team.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PinnedEvent","description":"Represents a 'pinned' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"Identifies the issue associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PinnedIssue","description":"A Pinned Issue is a issue pinned to a repository's index page.","fields":[{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was pinned.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedBy","description":"The actor that pinned this issue.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Actor","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that this issue was pinned to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PinnedIssueConnection","description":"The connection type for PinnedIssue.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PinnedIssueEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PinnedIssue","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PinnedIssueEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PinnedIssue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"PreciseDateTime","description":"An ISO-8601 encoded UTC date string with millisecond precision.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","description":"Audit log entry for a private_repository_forking.disable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","description":"Audit log entry for a private_repository_forking.enable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProfileItemShowcase","description":"A curatable list of repositories relating to a repository owner, which defaults to showing the most popular repositories they own.","fields":[{"name":"hasPinnedItems","description":"Whether or not the owner has pinned any repositories or gists.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"items","description":"The repositories and gists in the showcase. If the profile owner has any pinned items, those will be returned. Otherwise, the profile owner's popular repositories will be returned.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"ProfileOwner","description":"Represents any entity on GitHub that has a profile page.","fields":[{"name":"anyPinnableItems","description":"Determine if this repository owner has any items that can be pinned to their profile.","args":[{"name":"type","description":"Filter to only a particular kind of pinnable item.","type":{"kind":"ENUM","name":"PinnableItemType","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The public profile email.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"itemShowcase","description":"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProfileItemShowcase","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"location","description":"The public profile location.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username used to login.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The public profile name.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pinnableItems","description":"A list of repositories and gists this profile owner can pin to their profile.","args":[{"name":"types","description":"Filter the types of pinnable items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItems","description":"A list of repositories and gists this profile owner has pinned to their profile","args":[{"name":"types","description":"Filter the types of pinned items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItemsRemaining","description":"Returns how many more items this profile owner can pin to their profile.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanChangePinnedItems","description":"Can the viewer pin repositories and gists to the profile?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"websiteUrl","description":"The public profile website URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"Project","description":"Projects manage issues, pull requests and notes within a project owner.","fields":[{"name":"body","description":"The project's description body.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The projects description body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closed","description":"`true` if the object is closed (definition of closed may depend on type)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closedAt","description":"Identifies the date and time when the object was closed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"columns","description":"List of columns in the project","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectColumnConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"The actor who originally created the project.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The project's name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"The project's number.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The project's owner. Currently limited to repositories, organizations, and users.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"ProjectOwner","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pendingCards","description":"List of pending cards in this project","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"archivedStates","description":"A list of archived states to filter the cards by","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"ProjectCardArchivedState","ofType":null}},"defaultValue":"[ARCHIVED, NOT_ARCHIVED]"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCardConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"progress","description":"Project progress details.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectProgress","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this project","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Whether the project is open or closed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this project","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Closable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProjectCard","description":"A card in a project.","fields":[{"name":"column","description":"The project column this card is associated under. A card may only belong to one\nproject column at a time. The column field will be null if the card is created\nin a pending state and has yet to be associated with a column. Once cards are\nassociated with a column, they will not become pending in the future.\n","args":[],"type":{"kind":"OBJECT","name":"ProjectColumn","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"content","description":"The card content item","args":[],"type":{"kind":"UNION","name":"ProjectCardItem","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"The actor who created this card","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isArchived","description":"Whether the card is archived","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"note","description":"The card note","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The project that contains this card.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Project","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this card","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of ProjectCard","args":[],"type":{"kind":"ENUM","name":"ProjectCardState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this card","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ProjectCardArchivedState","description":"The possible archived states of a project card.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ARCHIVED","description":"A project card that is archived","isDeprecated":false,"deprecationReason":null},{"name":"NOT_ARCHIVED","description":"A project card that is not archived","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ProjectCardConnection","description":"The connection type for ProjectCard.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCardEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCard","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProjectCardEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ProjectCard","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"ProjectCardItem","description":"Types that can be inside Project Cards.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"ENUM","name":"ProjectCardState","description":"Various content states of a ProjectCard","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CONTENT_ONLY","description":"The card has content only.","isDeprecated":false,"deprecationReason":null},{"name":"NOTE_ONLY","description":"The card has a note only.","isDeprecated":false,"deprecationReason":null},{"name":"REDACTED","description":"The card is redacted.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ProjectColumn","description":"A column inside a project.","fields":[{"name":"cards","description":"List of cards in the column","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"archivedStates","description":"A list of archived states to filter the cards by","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"ProjectCardArchivedState","ofType":null}},"defaultValue":"[ARCHIVED, NOT_ARCHIVED]"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCardConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The project column's name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The project that contains this column.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Project","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"purpose","description":"The semantic purpose of the column","args":[],"type":{"kind":"ENUM","name":"ProjectColumnPurpose","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this project column","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this project column","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProjectColumnConnection","description":"The connection type for ProjectColumn.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ProjectColumnEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ProjectColumn","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProjectColumnEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ProjectColumn","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ProjectColumnPurpose","description":"The semantic purpose of the column - todo, in progress, or done.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TODO","description":"The column contains cards still to be worked on","isDeprecated":false,"deprecationReason":null},{"name":"IN_PROGRESS","description":"The column contains cards which are currently being worked on","isDeprecated":false,"deprecationReason":null},{"name":"DONE","description":"The column contains cards which are complete","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ProjectConnection","description":"A list of projects associated with the owner.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ProjectEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Project","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ProjectEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ProjectOrder","description":"Ways in which lists of projects can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order projects by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order projects by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ProjectOrderField","description":"Properties by which project connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order projects by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order projects by update time","isDeprecated":false,"deprecationReason":null},{"name":"NAME","description":"Order projects by name","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"ProjectOwner","description":"Represents an owner of a Project.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"Find project by number.","args":[{"name":"number","description":"The project number to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projects","description":"A list of projects under the owner.","args":[{"name":"orderBy","description":"Ordering options for projects returned from the connection","type":{"kind":"INPUT_OBJECT","name":"ProjectOrder","ofType":null},"defaultValue":null},{"name":"search","description":"Query to search projects by, currently only searching by name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"states","description":"A list of states to filter the projects by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectState","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsResourcePath","description":"The HTTP path listing owners projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsUrl","description":"The HTTP URL listing owners projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateProjects","description":"Can the current viewer create new projects on this owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"ProjectProgress","description":"Project progress stats.","fields":[{"name":"doneCount","description":"The number of done cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"donePercentage","description":"The percentage of done cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enabled","description":"Whether progress tracking is enabled and cards with purpose exist for this project","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"inProgressCount","description":"The number of in-progress cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"inProgressPercentage","description":"The percentage of in-progress cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"todoCount","description":"The number of to do cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"todoPercentage","description":"The percentage of to do cards.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Float","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ProjectState","description":"State of the project; either 'open' or 'closed'","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OPEN","description":"The project is open.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED","description":"The project is closed.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"ProjectTemplate","description":"GitHub-provided templates for Projects","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"BASIC_KANBAN","description":"Create a board with columns for To do, In progress and Done.","isDeprecated":false,"deprecationReason":null},{"name":"AUTOMATED_KANBAN_V2","description":"Create a board with v2 triggers to automatically move cards across To do, In progress and Done columns.","isDeprecated":false,"deprecationReason":null},{"name":"AUTOMATED_REVIEWS_KANBAN","description":"Create a board with triggers to automatically move cards across columns with review automation.","isDeprecated":false,"deprecationReason":null},{"name":"BUG_TRIAGE","description":"Create a board to triage and prioritize bugs with To do, priority, and Done columns.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PublicKey","description":"A user's public key.","fields":[{"name":"accessedAt","description":"The last time this authorization was used to perform an action. Values will be null for keys not owned by the user.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the key was created. Keys created before March 5th, 2014 have inaccurate values. Values will be null for keys not owned by the user.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"fingerprint","description":"The fingerprint for this PublicKey.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isReadOnly","description":"Whether this PublicKey is read-only or not. Values will be null for keys not owned by the user.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"key","description":"The public key string.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the key was updated. Keys created before March 5th, 2014 may have inaccurate values. Values will be null for keys not owned by the user.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PublicKeyConnection","description":"The connection type for PublicKey.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PublicKeyEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PublicKey","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PublicKeyEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PublicKey","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequest","description":"A repository pull request.","fields":[{"name":"activeLockReason","description":"Reason that the conversation was locked.","args":[],"type":{"kind":"ENUM","name":"LockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"additions","description":"The number of additions in this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"assignees","description":"A list of Users assigned to this object.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"autoMergeRequest","description":"Returns the auto-merge request object if one exists for this pull request.","args":[],"type":{"kind":"OBJECT","name":"AutoMergeRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"baseRef","description":"Identifies the base Ref associated with the pull request.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"baseRefName","description":"Identifies the name of the base Ref associated with the pull request, even if the ref has been deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"baseRefOid","description":"Identifies the oid of the base ref associated with the pull request, even if the ref has been deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"baseRepository","description":"The repository associated with this pull request's base Ref.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The body as Markdown.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"changedFiles","description":"The number of changed files in this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"checksResourcePath","description":"The HTTP path for the checks of this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"checksUrl","description":"The HTTP URL for the checks of this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closed","description":"`true` if the pull request is closed","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"closedAt","description":"Identifies the date and time when the object was closed.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"comments","description":"A list of comments associated with the pull request.","args":[{"name":"orderBy","description":"Ordering options for issue comments returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueCommentOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commits","description":"A list of commits present in this pull request's head branch not present in the base branch.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestCommitConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletions","description":"The number of deletions in this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited this pull request's body.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"files","description":"Lists the files changed within this pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PullRequestChangedFileConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"headRef","description":"Identifies the head Ref associated with the pull request.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"headRefName","description":"Identifies the name of the head Ref associated with the pull request, even if the ref has been deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"headRefOid","description":"Identifies the oid of the head ref associated with the pull request, even if the ref has been deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"headRepository","description":"The repository associated with this pull request's head Ref.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"headRepositoryOwner","description":"The owner of the repository associated with this pull request's head Ref.","args":[],"type":{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"hovercard","description":"The hovercard information for this issue","args":[{"name":"includeNotificationContexts","description":"Whether or not to include notification contexts","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"true"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Hovercard","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"The head and base repositories are different.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDraft","description":"Identifies if the pull request is a draft.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isReadByViewer","description":"Is this pull request read by the viewer","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labels","description":"A list of labels associated with the object.","args":[{"name":"orderBy","description":"Ordering options for labels returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"LabelOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LabelConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"latestOpinionatedReviews","description":"A list of latest reviews per user associated with the pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"writersOnly","description":"Only return reviews from user who have write access to the repository","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"OBJECT","name":"PullRequestReviewConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"latestReviews","description":"A list of latest reviews per user associated with the pull request that are not also pending review.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PullRequestReviewConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locked","description":"`true` if the pull request is locked","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"maintainerCanModify","description":"Indicates whether maintainers can modify the pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mergeCommit","description":"The commit that was created when this pull request was merged.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeable","description":"Whether or not the pull request can be merged based on the existence of merge conflicts.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"MergeableState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"merged","description":"Whether or not the pull request was merged.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mergedAt","description":"The date and time that the pull request was merged.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergedBy","description":"The actor who merged the pull request.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"milestone","description":"Identifies the milestone associated with the pull request.","args":[],"type":{"kind":"OBJECT","name":"Milestone","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"Identifies the pull request number.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"participants","description":"A list of Users that are participating in the Pull Request conversation.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permalink","description":"The permalink to the pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"potentialMergeCommit","description":"The commit that GitHub automatically generated to test if this pull request could be merged. This field will not return a value if the pull request is merged, or if the test merge commit is still being generated. See the `mergeable` field for more details on the mergeability of the pull request.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projectCards","description":"List of project cards associated with this pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"archivedStates","description":"A list of archived states to filter the cards by","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"ProjectCardArchivedState","ofType":null}},"defaultValue":"[ARCHIVED, NOT_ARCHIVED]"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectCardConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"revertResourcePath","description":"The HTTP path for reverting this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"revertUrl","description":"The HTTP URL for reverting this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reviewDecision","description":"The current status of this pull request with respect to code review.","args":[],"type":{"kind":"ENUM","name":"PullRequestReviewDecision","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reviewRequests","description":"A list of review requests associated with the pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"ReviewRequestConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reviewThreads","description":"The list of all review threads for this pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewThreadConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reviews","description":"A list of reviews associated with the pull request.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"states","description":"A list of states to filter the reviews.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestReviewState","ofType":null}}},"defaultValue":null},{"name":"author","description":"Filter by author of the review.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PullRequestReviewConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the state of the pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"suggestedReviewers","description":"A list of reviewer suggestions based on commit history and past review comments.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SuggestedReviewer","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"timeline","description":"A list of events, comments, commits, etc. associated with the pull request.","args":[{"name":"since","description":"Allows filtering timeline events by a `since` timestamp.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestTimelineConnection","ofType":null}},"isDeprecated":true,"deprecationReason":"`timeline` will be removed Use PullRequest.timelineItems instead. Removal on 2020-10-01 UTC."},{"name":"timelineItems","description":"A list of events, comments, commits, etc. associated with the pull request.","args":[{"name":"since","description":"Filter timeline items by a `since` timestamp.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"skip","description":"Skips the first _n_ elements in the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"itemTypes","description":"Filter timeline items by type.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestTimelineItemsItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestTimelineItemsConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"Identifies the pull request title.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanApplySuggestion","description":"Whether or not the viewer can apply suggestion.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDeleteHeadRef","description":"Check if the viewer can restore the deleted head ref.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDisableAutoMerge","description":"Whether or not the viewer can disable auto-merge","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanEnableAutoMerge","description":"Whether or not the viewer can enable auto-merge","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerLatestReview","description":"The latest review given from the viewer.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerLatestReviewRequest","description":"The person who has requested the viewer for review on this pull request.","args":[],"type":{"kind":"OBJECT","name":"ReviewRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerMergeBodyText","description":"The merge body text for the viewer and method.","args":[{"name":"mergeType","description":"The merge method for the message.","type":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerMergeHeadlineText","description":"The merge headline text for the viewer and method.","args":[{"name":"mergeType","description":"The merge method for the message.","type":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Assignable","ofType":null},{"kind":"INTERFACE","name":"Closable","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Labelable","ofType":null},{"kind":"INTERFACE","name":"Lockable","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestChangedFile","description":"A file changed in a pull request.","fields":[{"name":"additions","description":"The number of additions to the file.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deletions","description":"The number of deletions to the file.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The path of the file.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerViewedState","description":"The state of the file for the viewer.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"FileViewedState","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestChangedFileConnection","description":"The connection type for PullRequestChangedFile.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestChangedFileEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestChangedFile","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestChangedFileEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequestChangedFile","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestCommit","description":"Represents a Git commit part of a pull request.","fields":[{"name":"commit","description":"The Git commit object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request this commit belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this pull request commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this pull request commit","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestCommitCommentThread","description":"Represents a commit comment thread part of a pull request.","fields":[{"name":"comments","description":"The comments that exist in this thread.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"The commit the comments were made on.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The file the comments were made on.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"The position in the diff for the commit that the comment was made on.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request this commit comment thread belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestCommitConnection","description":"The connection type for PullRequestCommit.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestCommitEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestCommit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestCommitEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequestCommit","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestConnection","description":"The connection type for PullRequest.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestContributionsByRepository","description":"This aggregates pull requests opened by a user within one repository.","fields":[{"name":"contributions","description":"The pull request contributions.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository in which the pull requests were opened.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestMergeMethod","description":"Represents available types of methods to use when merging a pull request.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MERGE","description":"Add all commits from the head branch to the base branch with a merge commit.","isDeprecated":false,"deprecationReason":null},{"name":"SQUASH","description":"Combine all commits from the head branch into a single commit in the base branch.","isDeprecated":false,"deprecationReason":null},{"name":"REBASE","description":"Add all commits from the head branch onto the base branch individually.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"PullRequestOrder","description":"Ways in which lists of issues can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order pull requests by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order pull requests by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestOrderField","description":"Properties by which pull_requests connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order pull_requests by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order pull_requests by update time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReview","description":"A review object for a given pull request.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"authorCanPushToRepository","description":"Indicates whether the author of this review has push access to the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"Identifies the pull request review body.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body of this review rendered as plain text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"comments","description":"A list of review comments for the current pull request review.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the commit associated with this pull request review.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"onBehalfOf","description":"A list of teams that this review was made on behalf of.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"Identifies the pull request associated with this pull request review.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path permalink for this PullRequestReview.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the current state of the pull request review.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestReviewState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"submittedAt","description":"Identifies when the Pull Request Review was submitted","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL permalink for this PullRequestReview.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewComment","description":"A review comment associated with a given repository pull request.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the subject of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The comment body of this review comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The comment body of this review comment rendered as plain text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the commit associated with the comment.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies when the comment was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"diffHunk","description":"The diff hunk to which the comment applies.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"draftedAt","description":"Identifies when the comment was created in a draft state.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMinimized","description":"Returns whether or not a comment has been minimized.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"minimizedReason","description":"Returns why the comment was minimized.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"originalCommit","description":"Identifies the original commit associated with the comment.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"originalPosition","description":"The original line index in the diff to which the comment applies.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"outdated","description":"Identifies when the comment body is outdated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The path to which the comment applies.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"position","description":"The line index in the diff to which the comment applies.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request associated with this review comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The pull request review associated with this review comment.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"replyTo","description":"The comment this is a reply to.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path permalink for this review comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"Identifies the state of the comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestReviewCommentState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies when the comment was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL permalink for this review comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanMinimize","description":"Check if the current viewer can minimize this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Minimizable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewCommentConnection","description":"The connection type for PullRequestReviewComment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewCommentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewCommentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestReviewCommentState","description":"The possible states of a pull request review comment.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PENDING","description":"A comment that is part of a pending review","isDeprecated":false,"deprecationReason":null},{"name":"SUBMITTED","description":"A comment that is part of a submitted review","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewConnection","description":"The connection type for PullRequestReview.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReview","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewContributionsByRepository","description":"This aggregates pull request reviews made by a user within one repository.","fields":[{"name":"contributions","description":"The pull request review contributions.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for contributions returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"ContributionOrder","ofType":null},"defaultValue":"{direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CreatedPullRequestReviewContributionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository in which the pull request reviews were made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestReviewDecision","description":"The review status of a pull request.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CHANGES_REQUESTED","description":"Changes have been requested on the pull request.","isDeprecated":false,"deprecationReason":null},{"name":"APPROVED","description":"The pull request has received an approving review.","isDeprecated":false,"deprecationReason":null},{"name":"REVIEW_REQUIRED","description":"A review is required before the pull request can be merged.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestReviewEvent","description":"The possible events to perform on a pull request review.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"COMMENT","description":"Submit general feedback without explicit approval.","isDeprecated":false,"deprecationReason":null},{"name":"APPROVE","description":"Submit feedback and approve merging these changes.","isDeprecated":false,"deprecationReason":null},{"name":"REQUEST_CHANGES","description":"Submit feedback that must be addressed before merging.","isDeprecated":false,"deprecationReason":null},{"name":"DISMISS","description":"Dismiss review so it now longer effects merging.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"PullRequestReviewState","description":"The possible states of a pull request review.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PENDING","description":"A review that has not yet been submitted.","isDeprecated":false,"deprecationReason":null},{"name":"COMMENTED","description":"An informational review.","isDeprecated":false,"deprecationReason":null},{"name":"APPROVED","description":"A review allowing the pull request to merge.","isDeprecated":false,"deprecationReason":null},{"name":"CHANGES_REQUESTED","description":"A review blocking the pull request from merging.","isDeprecated":false,"deprecationReason":null},{"name":"DISMISSED","description":"A review that has been dismissed.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewThread","description":"A threaded list of comments for a given pull request.","fields":[{"name":"comments","description":"A list of pull request comments associated with the thread.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"skip","description":"Skips the first _n_ elements in the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"diffSide","description":"The side of the diff on which this thread was placed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"DiffSide","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCollapsed","description":"Whether or not the thread has been collapsed (outdated or resolved)","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isOutdated","description":"Indicates whether this thread was outdated by newer changes.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isResolved","description":"Whether this thread has been resolved","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"line","description":"The line in the file to which this thread refers","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"originalLine","description":"The original line in the file to which this thread refers.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"originalStartLine","description":"The original start line in the file to which this thread refers (multi-line only).","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"Identifies the file path of this thread.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"Identifies the pull request associated with this thread.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Identifies the repository associated with this thread.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resolvedBy","description":"The user who resolved this thread","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"startDiffSide","description":"The side of the diff that the first line of the thread starts on (multi-line only)","args":[],"type":{"kind":"ENUM","name":"DiffSide","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"startLine","description":"The start line in the file to which this thread refers (multi-line only)","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReply","description":"Indicates whether the current viewer can reply to this thread.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanResolve","description":"Whether or not the viewer can resolve this thread","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUnresolve","description":"Whether or not the viewer can unresolve this thread","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewThreadConnection","description":"Review comment threads for a pull request review.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewThreadEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestReviewThreadEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestRevisionMarker","description":"Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastSeenCommit","description":"The last commit the viewer has seen.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Commit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request to which the marker belongs.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestState","description":"The possible states of a pull request.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OPEN","description":"A pull request that is still open.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED","description":"A pull request that has been closed without being merged.","isDeprecated":false,"deprecationReason":null},{"name":"MERGED","description":"A pull request that has been closed by being merged.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestTimelineConnection","description":"The connection type for PullRequestTimelineItem.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestTimelineItemEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"PullRequestTimelineItem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"PullRequestTimelineItem","description":"An item in a pull request timeline","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"AssignedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"CommitCommentThread","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"DemilestonedEvent","ofType":null},{"kind":"OBJECT","name":"DeployedEvent","ofType":null},{"kind":"OBJECT","name":"DeploymentEnvironmentChangedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefRestoredEvent","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"LabeledEvent","ofType":null},{"kind":"OBJECT","name":"LockedEvent","ofType":null},{"kind":"OBJECT","name":"MergedEvent","ofType":null},{"kind":"OBJECT","name":"MilestonedEvent","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},{"kind":"OBJECT","name":"ReferencedEvent","ofType":null},{"kind":"OBJECT","name":"RenamedTitleEvent","ofType":null},{"kind":"OBJECT","name":"ReopenedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewDismissedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequestRemovedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequestedEvent","ofType":null},{"kind":"OBJECT","name":"SubscribedEvent","ofType":null},{"kind":"OBJECT","name":"UnassignedEvent","ofType":null},{"kind":"OBJECT","name":"UnlabeledEvent","ofType":null},{"kind":"OBJECT","name":"UnlockedEvent","ofType":null},{"kind":"OBJECT","name":"UnsubscribedEvent","ofType":null},{"kind":"OBJECT","name":"UserBlockedEvent","ofType":null}]},{"kind":"OBJECT","name":"PullRequestTimelineItemEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"PullRequestTimelineItem","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"PullRequestTimelineItems","description":"An item in a pull request timeline","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"AddedToProjectEvent","ofType":null},{"kind":"OBJECT","name":"AssignedEvent","ofType":null},{"kind":"OBJECT","name":"AutoMergeDisabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoMergeEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoRebaseEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutoSquashEnabledEvent","ofType":null},{"kind":"OBJECT","name":"AutomaticBaseChangeFailedEvent","ofType":null},{"kind":"OBJECT","name":"AutomaticBaseChangeSucceededEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefChangedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"BaseRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"CommentDeletedEvent","ofType":null},{"kind":"OBJECT","name":"ConnectedEvent","ofType":null},{"kind":"OBJECT","name":"ConvertToDraftEvent","ofType":null},{"kind":"OBJECT","name":"ConvertedNoteToIssueEvent","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"DemilestonedEvent","ofType":null},{"kind":"OBJECT","name":"DeployedEvent","ofType":null},{"kind":"OBJECT","name":"DeploymentEnvironmentChangedEvent","ofType":null},{"kind":"OBJECT","name":"DisconnectedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefDeletedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefForcePushedEvent","ofType":null},{"kind":"OBJECT","name":"HeadRefRestoredEvent","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"LabeledEvent","ofType":null},{"kind":"OBJECT","name":"LockedEvent","ofType":null},{"kind":"OBJECT","name":"MarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"MentionedEvent","ofType":null},{"kind":"OBJECT","name":"MergedEvent","ofType":null},{"kind":"OBJECT","name":"MilestonedEvent","ofType":null},{"kind":"OBJECT","name":"MovedColumnsInProjectEvent","ofType":null},{"kind":"OBJECT","name":"PinnedEvent","ofType":null},{"kind":"OBJECT","name":"PullRequestCommit","ofType":null},{"kind":"OBJECT","name":"PullRequestCommitCommentThread","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},{"kind":"OBJECT","name":"PullRequestRevisionMarker","ofType":null},{"kind":"OBJECT","name":"ReadyForReviewEvent","ofType":null},{"kind":"OBJECT","name":"ReferencedEvent","ofType":null},{"kind":"OBJECT","name":"RemovedFromProjectEvent","ofType":null},{"kind":"OBJECT","name":"RenamedTitleEvent","ofType":null},{"kind":"OBJECT","name":"ReopenedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewDismissedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequestRemovedEvent","ofType":null},{"kind":"OBJECT","name":"ReviewRequestedEvent","ofType":null},{"kind":"OBJECT","name":"SubscribedEvent","ofType":null},{"kind":"OBJECT","name":"TransferredEvent","ofType":null},{"kind":"OBJECT","name":"UnassignedEvent","ofType":null},{"kind":"OBJECT","name":"UnlabeledEvent","ofType":null},{"kind":"OBJECT","name":"UnlockedEvent","ofType":null},{"kind":"OBJECT","name":"UnmarkedAsDuplicateEvent","ofType":null},{"kind":"OBJECT","name":"UnpinnedEvent","ofType":null},{"kind":"OBJECT","name":"UnsubscribedEvent","ofType":null},{"kind":"OBJECT","name":"UserBlockedEvent","ofType":null}]},{"kind":"OBJECT","name":"PullRequestTimelineItemsConnection","description":"The connection type for PullRequestTimelineItems.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestTimelineItemsEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"filteredCount","description":"Identifies the count of items after applying `before` and `after` filters.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"PullRequestTimelineItems","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageCount","description":"Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the timeline was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PullRequestTimelineItemsEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"PullRequestTimelineItems","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"PullRequestTimelineItemsItemType","description":"The possible item types found in a timeline.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PULL_REQUEST_COMMIT","description":"Represents a Git commit part of a pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST_COMMIT_COMMENT_THREAD","description":"Represents a commit comment thread part of a pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST_REVIEW","description":"A review object for a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST_REVIEW_THREAD","description":"A threaded list of comments for a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST_REVISION_MARKER","description":"Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.","isDeprecated":false,"deprecationReason":null},{"name":"AUTOMATIC_BASE_CHANGE_FAILED_EVENT","description":"Represents a 'automatic_base_change_failed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"AUTOMATIC_BASE_CHANGE_SUCCEEDED_EVENT","description":"Represents a 'automatic_base_change_succeeded' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"AUTO_MERGE_DISABLED_EVENT","description":"Represents a 'auto_merge_disabled' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"AUTO_MERGE_ENABLED_EVENT","description":"Represents a 'auto_merge_enabled' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"AUTO_REBASE_ENABLED_EVENT","description":"Represents a 'auto_rebase_enabled' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"AUTO_SQUASH_ENABLED_EVENT","description":"Represents a 'auto_squash_enabled' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"BASE_REF_CHANGED_EVENT","description":"Represents a 'base_ref_changed' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"BASE_REF_FORCE_PUSHED_EVENT","description":"Represents a 'base_ref_force_pushed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"BASE_REF_DELETED_EVENT","description":"Represents a 'base_ref_deleted' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DEPLOYED_EVENT","description":"Represents a 'deployed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT","description":"Represents a 'deployment_environment_changed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"HEAD_REF_DELETED_EVENT","description":"Represents a 'head_ref_deleted' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"HEAD_REF_FORCE_PUSHED_EVENT","description":"Represents a 'head_ref_force_pushed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"HEAD_REF_RESTORED_EVENT","description":"Represents a 'head_ref_restored' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MERGED_EVENT","description":"Represents a 'merged' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"REVIEW_DISMISSED_EVENT","description":"Represents a 'review_dismissed' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"REVIEW_REQUESTED_EVENT","description":"Represents an 'review_requested' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"REVIEW_REQUEST_REMOVED_EVENT","description":"Represents an 'review_request_removed' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"READY_FOR_REVIEW_EVENT","description":"Represents a 'ready_for_review' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"CONVERT_TO_DRAFT_EVENT","description":"Represents a 'convert_to_draft' event on a given pull request.","isDeprecated":false,"deprecationReason":null},{"name":"ISSUE_COMMENT","description":"Represents a comment on an Issue.","isDeprecated":false,"deprecationReason":null},{"name":"CROSS_REFERENCED_EVENT","description":"Represents a mention made by one issue or pull request to another.","isDeprecated":false,"deprecationReason":null},{"name":"ADDED_TO_PROJECT_EVENT","description":"Represents a 'added_to_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"ASSIGNED_EVENT","description":"Represents an 'assigned' event on any assignable object.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED_EVENT","description":"Represents a 'closed' event on any `Closable`.","isDeprecated":false,"deprecationReason":null},{"name":"COMMENT_DELETED_EVENT","description":"Represents a 'comment_deleted' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"CONNECTED_EVENT","description":"Represents a 'connected' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"CONVERTED_NOTE_TO_ISSUE_EVENT","description":"Represents a 'converted_note_to_issue' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DEMILESTONED_EVENT","description":"Represents a 'demilestoned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"DISCONNECTED_EVENT","description":"Represents a 'disconnected' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"LABELED_EVENT","description":"Represents a 'labeled' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"LOCKED_EVENT","description":"Represents a 'locked' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MARKED_AS_DUPLICATE_EVENT","description":"Represents a 'marked_as_duplicate' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MENTIONED_EVENT","description":"Represents a 'mentioned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MILESTONED_EVENT","description":"Represents a 'milestoned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"MOVED_COLUMNS_IN_PROJECT_EVENT","description":"Represents a 'moved_columns_in_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"PINNED_EVENT","description":"Represents a 'pinned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"REFERENCED_EVENT","description":"Represents a 'referenced' event on a given `ReferencedSubject`.","isDeprecated":false,"deprecationReason":null},{"name":"REMOVED_FROM_PROJECT_EVENT","description":"Represents a 'removed_from_project' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"RENAMED_TITLE_EVENT","description":"Represents a 'renamed' event on a given issue or pull request","isDeprecated":false,"deprecationReason":null},{"name":"REOPENED_EVENT","description":"Represents a 'reopened' event on any `Closable`.","isDeprecated":false,"deprecationReason":null},{"name":"SUBSCRIBED_EVENT","description":"Represents a 'subscribed' event on a given `Subscribable`.","isDeprecated":false,"deprecationReason":null},{"name":"TRANSFERRED_EVENT","description":"Represents a 'transferred' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNASSIGNED_EVENT","description":"Represents an 'unassigned' event on any assignable object.","isDeprecated":false,"deprecationReason":null},{"name":"UNLABELED_EVENT","description":"Represents an 'unlabeled' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNLOCKED_EVENT","description":"Represents an 'unlocked' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"USER_BLOCKED_EVENT","description":"Represents a 'user_blocked' event on a given user.","isDeprecated":false,"deprecationReason":null},{"name":"UNMARKED_AS_DUPLICATE_EVENT","description":"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNPINNED_EVENT","description":"Represents an 'unpinned' event on a given issue or pull request.","isDeprecated":false,"deprecationReason":null},{"name":"UNSUBSCRIBED_EVENT","description":"Represents an 'unsubscribed' event on a given `Subscribable`.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"PullRequestUpdateState","description":"The possible target states when updating a pull request.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OPEN","description":"A pull request that is still open.","isDeprecated":false,"deprecationReason":null},{"name":"CLOSED","description":"A pull request that has been closed without being merged.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"Push","description":"A Git push.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nextSha","description":"The SHA after the push","args":[],"type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"permalink","description":"The permalink for this push.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"previousSha","description":"The SHA before the push","args":[],"type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pusher","description":"The user who pushed","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that was pushed to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PushAllowance","description":"A team, user or app who has the ability to push to a protected branch.","fields":[{"name":"actor","description":"The actor that can push.","args":[],"type":{"kind":"UNION","name":"PushAllowanceActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"branchProtectionRule","description":"Identifies the branch protection rule associated with the allowed user or team.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"PushAllowanceActor","description":"Types that can be an actor.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"App","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"PushAllowanceConnection","description":"The connection type for PushAllowance.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PushAllowanceEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"PushAllowance","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"PushAllowanceEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"PushAllowance","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Query","description":"The query root of GitHub's GraphQL interface.","fields":[{"name":"codeOfConduct","description":"Look up a code of conduct by its key","args":[{"name":"key","description":"The code of conduct's key","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"CodeOfConduct","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"codesOfConduct","description":"Look up a code of conduct by its key","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"CodeOfConduct","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"Look up an enterprise by URL slug.","args":[{"name":"slug","description":"The enterprise URL slug.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"invitationToken","description":"The enterprise invitation token.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseAdministratorInvitation","description":"Look up a pending enterprise administrator invitation by invitee, enterprise and role.","args":[{"name":"userLogin","description":"The login of the user invited to join the business.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"enterpriseSlug","description":"The slug of the enterprise the user was invited to join.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"role","description":"The role for the business member invitation.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseAdministratorInvitationByToken","description":"Look up a pending enterprise administrator invitation by invitation token.","args":[{"name":"invitationToken","description":"The invitation token sent with the invitation email.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"EnterpriseAdministratorInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"license","description":"Look up an open source license by its key","args":[{"name":"key","description":"The license's downcased SPDX ID","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"License","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"licenses","description":"Return a list of known open source licenses","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"License","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"marketplaceCategories","description":"Get alphabetically sorted list of Marketplace categories","args":[{"name":"includeCategories","description":"Return only the specified categories.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"excludeEmpty","description":"Exclude categories with no listings.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"excludeSubcategories","description":"Returns top level categories only, excluding any subcategories.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"MarketplaceCategory","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"marketplaceCategory","description":"Look up a Marketplace category by its slug.","args":[{"name":"slug","description":"The URL slug of the category.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"useTopicAliases","description":"Also check topic aliases for the category slug","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MarketplaceCategory","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"marketplaceListing","description":"Look up a single Marketplace listing","args":[{"name":"slug","description":"Select the listing that matches this slug. It's the short name of the listing used in its URL.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MarketplaceListing","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"marketplaceListings","description":"Look up Marketplace listings","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"categorySlug","description":"Select only listings with the given category.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"useTopicAliases","description":"Also check topic aliases for the category slug","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"viewerCanAdmin","description":"Select listings to which user has admin access. If omitted, listings visible to the\nviewer are returned.\n","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"adminId","description":"Select listings that can be administered by the specified user.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"organizationId","description":"Select listings for products owned by the specified organization.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"allStates","description":"Select listings visible to the viewer even if they are not approved. If omitted or\nfalse, only approved listings will be returned.\n","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"slugs","description":"Select the listings with these slugs, if they are visible to the viewer.","type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"primaryCategoryOnly","description":"Select only listings where the primary category matches the given category slug.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"withFreeTrialsOnly","description":"Select only listings that offer a free trial.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"MarketplaceListingConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"meta","description":"Return information about the GitHub instance","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GitHubMetadata","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"Fetches an object given its ID.","args":[{"name":"id","description":"ID of the object.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null}],"type":{"kind":"INTERFACE","name":"Node","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"Lookup nodes by a list of IDs.","args":[{"name":"ids","description":"The list of node IDs.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"INTERFACE","name":"Node","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"Lookup a organization by login.","args":[{"name":"login","description":"The organization's login.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"rateLimit","description":"The client's rate limit information.","args":[{"name":"dryRun","description":"If true, calculate the cost for the query without evaluating it","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"OBJECT","name":"RateLimit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"relay","description":"Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Query","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Lookup a given repository by the owner and repository name.","args":[{"name":"owner","description":"The login field of a user or organization","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the repository","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryOwner","description":"Lookup a repository owner (ie. either a User or an Organization) by login.","args":[{"name":"login","description":"The username to lookup the owner by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resource","description":"Lookup resource by a URL.","args":[{"name":"url","description":"The URL.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"defaultValue":null}],"type":{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"search","description":"Perform a search across resources.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"query","description":"The search string to look for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"type","description":"The types of search items to search within.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SearchType","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SearchResultItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"securityAdvisories","description":"GitHub Security Advisories","args":[{"name":"orderBy","description":"Ordering options for the returned topics.","type":{"kind":"INPUT_OBJECT","name":"SecurityAdvisoryOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"},{"name":"identifier","description":"Filter advisories by identifier, e.g. GHSA or CVE.","type":{"kind":"INPUT_OBJECT","name":"SecurityAdvisoryIdentifierFilter","ofType":null},"defaultValue":null},{"name":"publishedSince","description":"Filter advisories to those published since a time in the past.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"updatedSince","description":"Filter advisories to those updated since a time in the past.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"securityAdvisory","description":"Fetch a Security Advisory by its GHSA ID","args":[{"name":"ghsaId","description":"GitHub Security Advisory ID.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"securityVulnerabilities","description":"Software Vulnerabilities documented by GitHub Security Advisories","args":[{"name":"orderBy","description":"Ordering options for the returned topics.","type":{"kind":"INPUT_OBJECT","name":"SecurityVulnerabilityOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"},{"name":"ecosystem","description":"An ecosystem to filter vulnerabilities by.","type":{"kind":"ENUM","name":"SecurityAdvisoryEcosystem","ofType":null},"defaultValue":null},{"name":"package","description":"A package name to filter vulnerabilities by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"severities","description":"A list of severities to filter vulnerabilities by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisorySeverity","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityVulnerabilityConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorables","description":"Users and organizations who can be sponsored via GitHub Sponsors.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for users and organizations returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"SponsorableOrder","ofType":null},"defaultValue":"{field: LOGIN, direction: ASC}"},{"name":"onlyDependencies","description":"Whether only sponsorables who own the viewer's dependencies will be returned. Must be authenticated to use. Can check an organization instead for their dependencies owned by sponsorables by passing orgLoginForDependencies.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orgLoginForDependencies","description":"Optional organization username for whose dependencies should be checked. Used when onlyDependencies = true. Omit to check your own dependencies. If you are not an administrator of the organization, only dependencies from its public repositories will be considered.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"dependencyEcosystem","description":"Optional filter for which dependencies should be checked for sponsorable owners. Only sponsorable owners of dependencies in this ecosystem will be included. Used when onlyDependencies = true.","type":{"kind":"ENUM","name":"SecurityAdvisoryEcosystem","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorsListing","description":"Look up a single Sponsors Listing","args":[{"name":"slug","description":"Select the Sponsors listing which matches this slug","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"SponsorsListing","ofType":null},"isDeprecated":true,"deprecationReason":"`Query.sponsorsListing` will be removed. Use `Sponsorable.sponsorsListing` instead. Removal on 2020-04-01 UTC."},{"name":"topic","description":"Look up a topic by name.","args":[{"name":"name","description":"The topic's name.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"Lookup a user by login.","args":[{"name":"login","description":"The user's login.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewer","description":"The currently authenticated user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RateLimit","description":"Represents the client's rate limit.","fields":[{"name":"cost","description":"The point cost for the current query counting against the rate limit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"limit","description":"The maximum number of points the client is permitted to consume in a 60 minute window.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodeCount","description":"The maximum number of nodes this query may return","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"remaining","description":"The number of points remaining in the current rate limit window.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resetAt","description":"The time at which the current rate limit window resets in UTC epoch seconds.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"used","description":"The number of points used in the current rate limit window.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Reactable","description":"Represents a subject that can be reacted on.","fields":[{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}]},{"kind":"OBJECT","name":"ReactingUserConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReactingUserEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReactingUserEdge","description":"Represents a user that's made a reaction.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reactedAt","description":"The moment when the user made the reaction.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Reaction","description":"An emoji reaction to a particular piece of content.","fields":[{"name":"content","description":"Identifies the emoji reaction.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReactionContent","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reactable","description":"The reactable piece of content","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Reactable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"Identifies the user who created this reaction.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReactionConnection","description":"A list of reactions that have been left on the subject.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReactionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Reaction","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasReacted","description":"Whether or not the authenticated user has left a reaction on the subject.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ReactionContent","description":"Emojis that can be attached to Issues, Pull Requests and Comments.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"THUMBS_UP","description":"Represents the `:+1:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"THUMBS_DOWN","description":"Represents the `:-1:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"LAUGH","description":"Represents the `:laugh:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"HOORAY","description":"Represents the `:hooray:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"CONFUSED","description":"Represents the `:confused:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"HEART","description":"Represents the `:heart:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"ROCKET","description":"Represents the `:rocket:` emoji.","isDeprecated":false,"deprecationReason":null},{"name":"EYES","description":"Represents the `:eyes:` emoji.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ReactionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Reaction","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReactionGroup","description":"A group of emoji reactions to a particular piece of content.","fields":[{"name":"content","description":"Identifies the emoji reaction.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReactionContent","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies when the reaction was created.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"The subject that was reacted to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Reactable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"users","description":"Users who have reacted to the reaction subject with the emotion represented by this reaction group","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactingUserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasReacted","description":"Whether or not the authenticated user has left a reaction on the subject.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ReactionOrder","description":"Ways in which lists of reactions can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order reactions by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReactionOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order reactions by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ReactionOrderField","description":"A list of fields that reactions can be ordered by.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Allows ordering a list of reactions by when they were created.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"ReadyForReviewEvent","description":"Represents a 'ready_for_review' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this ready for review event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this ready for review event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Ref","description":"Represents a Git reference.","fields":[{"name":"associatedPullRequests","description":"A list of pull requests with this ref as the head ref.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"branchProtectionRule","description":"Branch protection rules for this ref","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The ref name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"prefix","description":"The ref's prefix, such as `refs/heads/` or `refs/tags/`.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"refUpdateRule","description":"Branch protection rules that are viewable by non-admins","args":[],"type":{"kind":"OBJECT","name":"RefUpdateRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository the ref belongs to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"target","description":"The object the ref points to. Returns null when object does not exist.","args":[],"type":{"kind":"INTERFACE","name":"GitObject","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RefConnection","description":"The connection type for Ref.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RefEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Ref","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RefEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RefOrder","description":"Ways in which lists of git refs can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order refs by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RefOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order refs by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RefOrderField","description":"Properties by which ref connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TAG_COMMIT_DATE","description":"Order refs by underlying commit date if the ref prefix is refs/tags/","isDeprecated":false,"deprecationReason":null},{"name":"ALPHABETICAL","description":"Order refs by their alphanumeric name","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RefUpdateRule","description":"A ref update rules for a viewer.","fields":[{"name":"allowsDeletions","description":"Can this branch be deleted.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"allowsForcePushes","description":"Are force pushes allowed on this branch.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pattern","description":"Identifies the protection rule pattern.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiredApprovingReviewCount","description":"Number of approving reviews required to update matching branches.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requiredStatusCheckContexts","description":"List of required status check contexts that must pass for commits to be accepted to matching branches.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresCodeOwnerReviews","description":"Are reviews from code owners required to update matching branches.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresLinearHistory","description":"Are merge commits prohibited from being pushed to this branch.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requiresSignatures","description":"Are commits required to be signed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerAllowedToDismissReviews","description":"Is the viewer allowed to dismiss reviews.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanPush","description":"Can the viewer push to the branch","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReferencedEvent","description":"Represents a 'referenced' event on a given `ReferencedSubject`.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"Identifies the commit associated with the 'referenced' event.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commitRepository","description":"Identifies the repository associated with the 'referenced' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Reference originated in a different repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDirectReference","description":"Checks if the commit message itself references the subject. Can be false in the case of a commit comment reference.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Object referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"ReferencedSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"ReferencedSubject","description":"Any referencable object","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"INPUT_OBJECT","name":"RegenerateEnterpriseIdentityProviderRecoveryCodesInput","description":"Autogenerated input type of RegenerateEnterpriseIdentityProviderRecoveryCodes","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set an identity provider.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RegenerateEnterpriseIdentityProviderRecoveryCodesPayload","description":"Autogenerated return type of RegenerateEnterpriseIdentityProviderRecoveryCodes","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"identityProvider","description":"The identity provider for the enterprise.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseIdentityProvider","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RegenerateVerifiableDomainTokenInput","description":"Autogenerated input type of RegenerateVerifiableDomainToken","fields":null,"inputFields":[{"name":"id","description":"The ID of the verifiable domain to regenerate the verification token of.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RegenerateVerifiableDomainTokenPayload","description":"Autogenerated return type of RegenerateVerifiableDomainToken","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"verificationToken","description":"The verification token that was generated.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Release","description":"A release contains the content for a release.","fields":[{"name":"author","description":"The author of the release","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the release.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The description of this release rendered to HTML.","args":[],"type":{"kind":"SCALAR","name":"HTML","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDraft","description":"Whether or not the release is a draft","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLatest","description":"Whether or not the release is the latest releast","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrerelease","description":"Whether or not the release is a prerelease","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The title of the release.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies the date and time when the release was created.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"releaseAssets","description":"List of releases assets which are dependent on this release.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"name","description":"A list of names to filter the assets by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReleaseAssetConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that the release belongs to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this issue","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"shortDescriptionHTML","description":"A description of the release, rendered to HTML without any links in it.","args":[{"name":"limit","description":"How many characters to return.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"200"}],"type":{"kind":"SCALAR","name":"HTML","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"tag","description":"The Git tag the release points to","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"tagCommit","description":"The tag commit for this release.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"tagName","description":"The name of the release's Git tag","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this issue","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReleaseAsset","description":"A release asset contains the content for a release asset.","fields":[{"name":"contentType","description":"The asset's content-type","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"downloadCount","description":"The number of times this asset was downloaded","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"downloadUrl","description":"Identifies the URL where you can download the release asset via the browser.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Identifies the title of the release asset.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"release","description":"Release that the asset is associated with","args":[],"type":{"kind":"OBJECT","name":"Release","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"size","description":"The size (in bytes) of the asset","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"uploadedBy","description":"The user that performed the upload","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"Identifies the URL of the release asset.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReleaseAssetConnection","description":"The connection type for ReleaseAsset.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReleaseAssetEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReleaseAsset","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReleaseAssetEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ReleaseAsset","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReleaseConnection","description":"The connection type for Release.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReleaseEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Release","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReleaseEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Release","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ReleaseOrder","description":"Ways in which lists of releases can be ordered upon return.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order releases by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReleaseOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order releases by the specified field.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ReleaseOrderField","description":"Properties by which release connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order releases by creation time","isDeprecated":false,"deprecationReason":null},{"name":"NAME","description":"Order releases alphabetically by name","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveAssigneesFromAssignableInput","description":"Autogenerated input type of RemoveAssigneesFromAssignable","fields":null,"inputFields":[{"name":"assignableId","description":"The id of the assignable object to remove assignees from.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"assigneeIds","description":"The id of users to remove as assignees.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveAssigneesFromAssignablePayload","description":"Autogenerated return type of RemoveAssigneesFromAssignable","fields":[{"name":"assignable","description":"The item that was unassigned.","args":[],"type":{"kind":"INTERFACE","name":"Assignable","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseAdminInput","description":"Autogenerated input type of RemoveEnterpriseAdmin","fields":null,"inputFields":[{"name":"enterpriseId","description":"The Enterprise ID from which to remove the administrator.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"login","description":"The login of the user to remove as an administrator.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveEnterpriseAdminPayload","description":"Autogenerated return type of RemoveEnterpriseAdmin","fields":[{"name":"admin","description":"The user who was removed as an administrator.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The updated enterprise.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of removing an administrator.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewer","description":"The viewer performing the mutation.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseIdentityProviderInput","description":"Autogenerated input type of RemoveEnterpriseIdentityProvider","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise from which to remove the identity provider.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveEnterpriseIdentityProviderPayload","description":"Autogenerated return type of RemoveEnterpriseIdentityProvider","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"identityProvider","description":"The identity provider that was removed from the enterprise.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseIdentityProvider","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseOrganizationInput","description":"Autogenerated input type of RemoveEnterpriseOrganization","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise from which the organization should be removed.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"organizationId","description":"The ID of the organization to remove from the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveEnterpriseOrganizationPayload","description":"Autogenerated return type of RemoveEnterpriseOrganization","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The updated enterprise.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization that was removed from the enterprise.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewer","description":"The viewer performing the mutation.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveEnterpriseSupportEntitlementInput","description":"Autogenerated input type of RemoveEnterpriseSupportEntitlement","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the Enterprise which the admin belongs to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"login","description":"The login of a member who will lose the support entitlement.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveEnterpriseSupportEntitlementPayload","description":"Autogenerated return type of RemoveEnterpriseSupportEntitlement","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of removing the support entitlement.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveLabelsFromLabelableInput","description":"Autogenerated input type of RemoveLabelsFromLabelable","fields":null,"inputFields":[{"name":"labelableId","description":"The id of the Labelable to remove labels from.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"labelIds","description":"The ids of labels to remove.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveLabelsFromLabelablePayload","description":"Autogenerated return type of RemoveLabelsFromLabelable","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labelable","description":"The Labelable the labels were removed from.","args":[],"type":{"kind":"INTERFACE","name":"Labelable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveOutsideCollaboratorInput","description":"Autogenerated input type of RemoveOutsideCollaborator","fields":null,"inputFields":[{"name":"userId","description":"The ID of the outside collaborator to remove.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"organizationId","description":"The ID of the organization to remove the outside collaborator from.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveOutsideCollaboratorPayload","description":"Autogenerated return type of RemoveOutsideCollaborator","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"removedUser","description":"The user that was removed as an outside collaborator.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveReactionInput","description":"Autogenerated input type of RemoveReaction","fields":null,"inputFields":[{"name":"subjectId","description":"The Node ID of the subject to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"content","description":"The name of the emoji reaction to remove.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ReactionContent","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveReactionPayload","description":"Autogenerated return type of RemoveReaction","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reaction","description":"The reaction object.","args":[],"type":{"kind":"OBJECT","name":"Reaction","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"The reactable subject.","args":[],"type":{"kind":"INTERFACE","name":"Reactable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RemoveStarInput","description":"Autogenerated input type of RemoveStar","fields":null,"inputFields":[{"name":"starrableId","description":"The Starrable ID to unstar.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemoveStarPayload","description":"Autogenerated return type of RemoveStar","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"starrable","description":"The starrable.","args":[],"type":{"kind":"INTERFACE","name":"Starrable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RemovedFromProjectEvent","description":"Represents a 'removed_from_project' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RenamedTitleEvent","description":"Represents a 'renamed' event on a given issue or pull request","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"currentTitle","description":"Identifies the current title of the issue or pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"previousTitle","description":"Identifies the previous title of the issue or pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"Subject that was renamed.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"RenamedTitleSubject","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"RenamedTitleSubject","description":"An object which has a renamable title","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null}]},{"kind":"INPUT_OBJECT","name":"ReopenIssueInput","description":"Autogenerated input type of ReopenIssue","fields":null,"inputFields":[{"name":"issueId","description":"ID of the issue to be opened.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReopenIssuePayload","description":"Autogenerated return type of ReopenIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was opened.","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ReopenPullRequestInput","description":"Autogenerated input type of ReopenPullRequest","fields":null,"inputFields":[{"name":"pullRequestId","description":"ID of the pull request to be reopened.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReopenPullRequestPayload","description":"Autogenerated return type of ReopenPullRequest","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that was reopened.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReopenedEvent","description":"Represents a 'reopened' event on any `Closable`.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"closable","description":"Object that was reopened.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Closable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","description":"Audit log entry for a repo.access event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoAccessAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoAccessAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","description":"Audit log entry for a repo.add_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoAddMemberAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoAddMemberAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","description":"Audit log entry for a repo.add_topic event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topic","description":"The name of the topic added to the repository","args":[],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topicName","description":"The name of the topic added to the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TopicAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","description":"Audit log entry for a repo.archived event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoArchivedAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoArchivedAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","description":"Audit log entry for a repo.change_merge_setting event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isEnabled","description":"Whether the change was to enable (true) or disable (false) the merge type","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mergeType","description":"The merge method affected by the change","args":[],"type":{"kind":"ENUM","name":"RepoChangeMergeSettingAuditEntryMergeType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoChangeMergeSettingAuditEntryMergeType","description":"The merge options available for pull requests to this repository.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MERGE","description":"The pull request is added to the base branch in a merge commit.","isDeprecated":false,"deprecationReason":null},{"name":"REBASE","description":"Commits from the pull request are added onto the base branch individually without a merge commit.","isDeprecated":false,"deprecationReason":null},{"name":"SQUASH","description":"The pull request's commits are squashed into a single commit before they are merged to the base branch.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","description":"Audit log entry for a repo.config.disable_anonymous_git_access event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","description":"Audit log entry for a repo.config.disable_collaborators_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","description":"Audit log entry for a repo.config.disable_contributors_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","description":"Audit log entry for a repo.config.disable_sockpuppet_disallowed event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","description":"Audit log entry for a repo.config.enable_anonymous_git_access event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","description":"Audit log entry for a repo.config.enable_collaborators_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","description":"Audit log entry for a repo.config.enable_contributors_only event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","description":"Audit log entry for a repo.config.enable_sockpuppet_disallowed event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","description":"Audit log entry for a repo.config.lock_anonymous_git_access event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","description":"Audit log entry for a repo.config.unlock_anonymous_git_access event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","description":"Audit log entry for a repo.create event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"forkParentName","description":"The name of the parent repository for this forked repository.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"forkSourceName","description":"The name of the root repository for this network.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoCreateAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoCreateAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","description":"Audit log entry for a repo.destroy event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoDestroyAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoDestroyAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","description":"Audit log entry for a repo.remove_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"visibility","description":"The visibility of the repository","args":[],"type":{"kind":"ENUM","name":"RepoRemoveMemberAuditEntryVisibility","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepoRemoveMemberAuditEntryVisibility","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","description":"Audit log entry for a repo.remove_topic event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topic","description":"The name of the topic added to the repository","args":[],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topicName","description":"The name of the topic added to the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TopicAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"ReportedContentClassifiers","description":"The reasons a piece of content can be reported or minimized.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SPAM","description":"A spammy piece of content","isDeprecated":false,"deprecationReason":null},{"name":"ABUSE","description":"An abusive or harassing piece of content","isDeprecated":false,"deprecationReason":null},{"name":"OFF_TOPIC","description":"An irrelevant piece of content","isDeprecated":false,"deprecationReason":null},{"name":"OUTDATED","description":"An outdated piece of content","isDeprecated":false,"deprecationReason":null},{"name":"DUPLICATE","description":"A duplicated piece of content","isDeprecated":false,"deprecationReason":null},{"name":"RESOLVED","description":"The content has been resolved","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"Repository","description":"A repository contains the content for a project.","fields":[{"name":"assignableUsers","description":"A list of users that can be assigned to issues in this repository.","args":[{"name":"query","description":"Filters users with query on user name and login","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"branchProtectionRules","description":"A list of branch protection rules for this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"BranchProtectionRuleConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"codeOfConduct","description":"Returns the code of conduct for this repository","args":[],"type":{"kind":"OBJECT","name":"CodeOfConduct","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"collaborators","description":"A list of collaborators associated with the repository.","args":[{"name":"affiliation","description":"Collaborators affiliation level with a repository.","type":{"kind":"ENUM","name":"CollaboratorAffiliation","ofType":null},"defaultValue":null},{"name":"query","description":"Filters users with query on user name and login","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RepositoryCollaboratorConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commitComments","description":"A list of commit comments associated with the repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"contactLinks","description":"Returns a list of contact links associated to the repository","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryContactLink","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"defaultBranchRef","description":"The Ref associated with the repository's default branch.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deleteBranchOnMerge","description":"Whether or not branches are automatically deleted when merged in this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deployKeys","description":"A list of deploy keys that are on this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"DeployKeyConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deployments","description":"Deployments associated with the repository","args":[{"name":"environments","description":"Environments to list deployments for","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for deployments returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"DeploymentOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"DeploymentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the repository.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The description of the repository rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"diskUsage","description":"The number of kilobytes this repository occupies on disk.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"forkCount","description":"Returns how many forks there are of this repository in the whole network.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"forks","description":"A list of direct forked repositories.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"affiliations","description":"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":null},{"name":"ownerAffiliations","description":"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":"[OWNER, COLLABORATOR]"},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fundingLinks","description":"The funding links for this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"FundingLink","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"hasIssuesEnabled","description":"Indicates if the repository has issues feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasProjectsEnabled","description":"Indicates if the repository has the Projects feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasWikiEnabled","description":"Indicates if the repository has wiki feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"homepageUrl","description":"The repository's URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"interactionAbility","description":"The interaction ability settings for this repository.","args":[],"type":{"kind":"OBJECT","name":"RepositoryInteractionAbility","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isArchived","description":"Indicates if the repository is unmaintained.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isBlankIssuesEnabled","description":"Returns true if blank issue creation is allowed","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDisabled","description":"Returns whether or not this repository disabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isEmpty","description":"Returns whether or not this repository is empty.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isFork","description":"Identifies if the repository is a fork.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isInOrganization","description":"Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLocked","description":"Indicates if the repository has been locked or not.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMirror","description":"Identifies if the repository is a mirror.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrivate","description":"Identifies if the repository is private.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSecurityPolicyEnabled","description":"Returns true if this repository has a security policy","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isTemplate","description":"Identifies if the repository is a template that can be used to generate new repositories.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isUserConfigurationRepository","description":"Is this repository a user configuration repository?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"Returns a single issue from the current repository by number.","args":[{"name":"number","description":"The number for the issue to be returned.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issueOrPullRequest","description":"Returns a single issue-like object from the current repository by number.","args":[{"name":"number","description":"The number for the issue to be returned.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issueTemplates","description":"Returns a list of issue templates associated to the repository","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueTemplate","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"issues","description":"A list of issues that have been opened in the repository.","args":[{"name":"orderBy","description":"Ordering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"states","description":"A list of states to filter the issues by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}}},"defaultValue":null},{"name":"filterBy","description":"Filtering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueFilters","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"label","description":"Returns a single label by name","args":[{"name":"name","description":"Label name","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Label","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"labels","description":"A list of labels associated with the repository.","args":[{"name":"orderBy","description":"Ordering options for labels returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"LabelOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: ASC}"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"query","description":"If provided, searches labels by name and description.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LabelConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"languages","description":"A list containing a breakdown of the language composition of the repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"LanguageOrder","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"LanguageConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"latestRelease","description":"Get the latest release for the repository if one exists.","args":[],"type":{"kind":"OBJECT","name":"Release","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"licenseInfo","description":"The license associated with the repository","args":[],"type":{"kind":"OBJECT","name":"License","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lockReason","description":"The reason the repository has been locked.","args":[],"type":{"kind":"ENUM","name":"RepositoryLockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mentionableUsers","description":"A list of Users that can be mentioned in the context of the repository.","args":[{"name":"query","description":"Filters users with query on user name and login","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mergeCommitAllowed","description":"Whether or not PRs are merged with a merge commit on this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"milestone","description":"Returns a single milestone from the current repository by number.","args":[{"name":"number","description":"The number for the milestone to be returned.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Milestone","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"milestones","description":"A list of milestones associated with the repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"states","description":"Filter by the state of the milestones.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"MilestoneState","ofType":null}}},"defaultValue":null},{"name":"orderBy","description":"Ordering options for milestones.","type":{"kind":"INPUT_OBJECT","name":"MilestoneOrder","ofType":null},"defaultValue":null},{"name":"query","description":"Filters milestones with a query on the title","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"MilestoneConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mirrorUrl","description":"The repository's original mirror URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nameWithOwner","description":"The repository's name with owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"object","description":"A Git object in the repository","args":[{"name":"oid","description":"The Git object ID","type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"defaultValue":null},{"name":"expression","description":"A Git revision expression suitable for rev-parse","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"type":{"kind":"INTERFACE","name":"GitObject","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"openGraphImageUrl","description":"The image used to represent this repository in Open Graph data.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The User owner of the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"packages","description":"A list of packages under the owner.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"names","description":"Find packages by their names.","type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"Find packages in a repository by ID.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"packageType","description":"Filter registry package by type.","type":{"kind":"ENUM","name":"PackageType","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering of the returned packages.","type":{"kind":"INPUT_OBJECT","name":"PackageOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"parent","description":"The repository parent, if this is a fork.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedIssues","description":"A list of pinned issues for this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PinnedIssueConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"primaryLanguage","description":"The primary language of the repository's code.","args":[],"type":{"kind":"OBJECT","name":"Language","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"Find project by number.","args":[{"name":"number","description":"The project number to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projects","description":"A list of projects under the owner.","args":[{"name":"orderBy","description":"Ordering options for projects returned from the connection","type":{"kind":"INPUT_OBJECT","name":"ProjectOrder","ofType":null},"defaultValue":null},{"name":"search","description":"Query to search projects by, currently only searching by name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"states","description":"A list of states to filter the projects by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectState","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsResourcePath","description":"The HTTP path listing the repository's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsUrl","description":"The HTTP URL listing the repository's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"Returns a single pull request from the current repository by number.","args":[{"name":"number","description":"The number for the pull request to be returned.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequests","description":"A list of pull requests that have been opened in the repository.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pushedAt","description":"Identifies when the repository was last pushed to.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"rebaseMergeAllowed","description":"Whether or not rebase-merging is enabled on this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"Fetch a given ref from the repository","args":[{"name":"qualifiedName","description":"The ref to retrieve. Fully qualified matches are checked in order (`refs/heads/master`) before falling back onto checks for short name matches (`master`).","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"refs","description":"Fetch a list of refs from the repository","args":[{"name":"query","description":"Filters refs with query on name","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"refPrefix","description":"A ref name prefix like `refs/heads/`, `refs/tags/`, etc.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"direction","description":"DEPRECATED: use orderBy. The ordering direction.","type":{"kind":"ENUM","name":"OrderDirection","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for refs returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"RefOrder","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RefConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"release","description":"Lookup a single release given various criteria.","args":[{"name":"tagName","description":"The name of the Tag the Release was created from","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Release","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"releases","description":"List of releases which are dependent on this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"ReleaseOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReleaseConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryTopics","description":"A list of applied repository-topic associations for this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryTopicConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"securityPolicyUrl","description":"The security policy URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"shortDescriptionHTML","description":"A description of the repository, rendered to HTML without any links in it.","args":[{"name":"limit","description":"How many characters to return.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"200"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"squashMergeAllowed","description":"Whether or not squash-merging is enabled on this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sshUrl","description":"The SSH URL to clone this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitSSHRemote","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazerCount","description":"Returns a count of how many stargazers there are on this object\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazers","description":"A list of users who have starred this starrable.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"StarOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StargazerConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"submodules","description":"Returns a list of all submodules in this repository parsed from the .gitmodules file as of the default branch's HEAD commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SubmoduleConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tempCloneToken","description":"Temporary authentication token for cloning this repository.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"templateRepository","description":"The repository from which this repository was generated, if any.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"usesCustomOpenGraphImage","description":"Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanAdminister","description":"Indicates whether the viewer has admin permissions on this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateProjects","description":"Can the current viewer create new projects on this owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdateTopics","description":"Indicates whether the viewer can update the topics of this repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDefaultCommitEmail","description":"The last commit email for the viewer.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDefaultMergeMethod","description":"The last used merge method by the viewer or the default for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestMergeMethod","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasStarred","description":"Returns a boolean indicating whether the viewing user has starred this starrable.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerPermission","description":"The users permission level on the repository. Will return null if authenticated as an GitHub App.","args":[],"type":{"kind":"ENUM","name":"RepositoryPermission","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerPossibleCommitEmails","description":"A list of emails this viewer can commit with.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerabilityAlerts","description":"A list of vulnerability alerts that are on this repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"RepositoryVulnerabilityAlertConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"watchers","description":"A list of users watching the repository.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"ProjectOwner","ofType":null},{"kind":"INTERFACE","name":"PackageOwner","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"Starrable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"RepositoryInfo","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryAffiliation","description":"The affiliation of a user to a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"OWNER","description":"Repositories that are owned by the authenticated user.","isDeprecated":false,"deprecationReason":null},{"name":"COLLABORATOR","description":"Repositories that the user has been added to as a collaborator.","isDeprecated":false,"deprecationReason":null},{"name":"ORGANIZATION_MEMBER","description":"Repositories that the user has access to through being a member of an organization. This includes every repository on every team that the user is on.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","description":"Metadata for an audit entry with action repo.*","fields":[{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"OrgRestoreMemberMembershipRepositoryAuditEntryData","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingDisableAuditEntry","ofType":null},{"kind":"OBJECT","name":"PrivateRepositoryForkingEnableAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoArchivedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoChangeMergeSettingAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigDisableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableCollaboratorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableContributorsOnlyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigEnableSockpuppetDisallowedAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigLockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoConfigUnlockAnonymousGitAccessAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoCreateAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoDestroyAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"RepositoryCollaboratorConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryCollaboratorEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryCollaboratorEdge","description":"Represents a user who is a collaborator of a repository.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The permission the user has on the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryPermission","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permissionSources","description":"A list of sources for the user's access to the repository.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PermissionSource","ofType":null}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryConnection","description":"A list of repositories owned by the subject.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalDiskUsage","description":"The total size in kilobytes of all repositories in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryContactLink","description":"A repository contact link.","fields":[{"name":"about","description":"The contact link purpose.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The contact link name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The contact link URL.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryContributionType","description":"The reason a repository is listed as 'contributed'.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"COMMIT","description":"Created a commit","isDeprecated":false,"deprecationReason":null},{"name":"ISSUE","description":"Created an issue","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST","description":"Created a pull request","isDeprecated":false,"deprecationReason":null},{"name":"REPOSITORY","description":"Created the repository","isDeprecated":false,"deprecationReason":null},{"name":"PULL_REQUEST_REVIEW","description":"Reviewed a pull request","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"RepositoryInfo","description":"A subset of repository info.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the repository.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The description of the repository rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"forkCount","description":"Returns how many forks there are of this repository in the whole network.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasIssuesEnabled","description":"Indicates if the repository has issues feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasProjectsEnabled","description":"Indicates if the repository has the Projects feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasWikiEnabled","description":"Indicates if the repository has wiki feature enabled.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"homepageUrl","description":"The repository's URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isArchived","description":"Indicates if the repository is unmaintained.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isFork","description":"Identifies if the repository is a fork.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isInOrganization","description":"Indicates if a repository is either owned by an organization, or is a private fork of an organization repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLocked","description":"Indicates if the repository has been locked or not.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isMirror","description":"Identifies if the repository is a mirror.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrivate","description":"Identifies if the repository is private.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isTemplate","description":"Identifies if the repository is a template that can be used to generate new repositories.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"licenseInfo","description":"The license associated with the repository","args":[],"type":{"kind":"OBJECT","name":"License","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"lockReason","description":"The reason the repository has been locked.","args":[],"type":{"kind":"ENUM","name":"RepositoryLockReason","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"mirrorUrl","description":"The repository's original mirror URL.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nameWithOwner","description":"The repository's name with owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"openGraphImageUrl","description":"The image used to represent this repository in Open Graph data.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The User owner of the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pushedAt","description":"Identifies when the repository was last pushed to.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"shortDescriptionHTML","description":"A description of the repository, rendered to HTML without any links in it.","args":[{"name":"limit","description":"How many characters to return.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"200"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"usesCustomOpenGraphImage","description":"Whether this repository has a custom image to use with Open Graph as opposed to being represented by the owner's avatar.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Repository","ofType":null}]},{"kind":"OBJECT","name":"RepositoryInteractionAbility","description":"Repository interaction limit that applies to this object.","fields":[{"name":"expiresAt","description":"The time the currently active limit expires.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"limit","description":"The current limit that is enabled on this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInteractionLimit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"origin","description":"The origin of the currently active interaction limit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInteractionLimitOrigin","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryInteractionLimit","description":"A repository interaction limit.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"EXISTING_USERS","description":"Users that have recently created their account will be unable to interact with the repository.","isDeprecated":false,"deprecationReason":null},{"name":"CONTRIBUTORS_ONLY","description":"Users that have not previously committed to a repository’s default branch will be unable to interact with the repository.","isDeprecated":false,"deprecationReason":null},{"name":"COLLABORATORS_ONLY","description":"Users that are not collaborators will not be able to interact with the repository.","isDeprecated":false,"deprecationReason":null},{"name":"NO_LIMIT","description":"No interaction limits are enabled.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"RepositoryInteractionLimitExpiry","description":"The length for a repository interaction limit to be enabled for.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ONE_DAY","description":"The interaction limit will expire after 1 day.","isDeprecated":false,"deprecationReason":null},{"name":"THREE_DAYS","description":"The interaction limit will expire after 3 days.","isDeprecated":false,"deprecationReason":null},{"name":"ONE_WEEK","description":"The interaction limit will expire after 1 week.","isDeprecated":false,"deprecationReason":null},{"name":"ONE_MONTH","description":"The interaction limit will expire after 1 month.","isDeprecated":false,"deprecationReason":null},{"name":"SIX_MONTHS","description":"The interaction limit will expire after 6 months.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"RepositoryInteractionLimitOrigin","description":"Indicates where an interaction limit is configured.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"REPOSITORY","description":"A limit that is configured at the repository level.","isDeprecated":false,"deprecationReason":null},{"name":"ORGANIZATION","description":"A limit that is configured at the organization level.","isDeprecated":false,"deprecationReason":null},{"name":"USER","description":"A limit that is configured at the user-wide level.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryInvitation","description":"An invitation for a user to be added to a repository.","fields":[{"name":"email","description":"The email address that received the invitation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitee","description":"The user who received the invitation.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"inviter","description":"The user who created the invitation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permalink","description":"The permalink for this repository invitation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The permission granted on this repository by this invitation.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryPermission","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the user is invited to.","args":[],"type":{"kind":"INTERFACE","name":"RepositoryInfo","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryInvitationConnection","description":"The connection type for RepositoryInvitation.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryInvitationEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryInvitation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryInvitationEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"RepositoryInvitation","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RepositoryInvitationOrder","description":"Ordering options for repository invitation connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order repository invitations by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInvitationOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryInvitationOrderField","description":"Properties by which repository invitation connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order repository invitations by creation time","isDeprecated":false,"deprecationReason":null},{"name":"INVITEE_LOGIN","description":"Order repository invitations by invitee login","isDeprecated":true,"deprecationReason":"`INVITEE_LOGIN` is no longer a valid field value. Repository invitations can now be associated with an email, not only an invitee. Removal on 2020-10-01 UTC."}],"possibleTypes":null},{"kind":"ENUM","name":"RepositoryLockReason","description":"The possible reasons a given repository could be in a locked state.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MOVING","description":"The repository is locked due to a move.","isDeprecated":false,"deprecationReason":null},{"name":"BILLING","description":"The repository is locked due to a billing related reason.","isDeprecated":false,"deprecationReason":null},{"name":"RENAME","description":"The repository is locked due to a rename.","isDeprecated":false,"deprecationReason":null},{"name":"MIGRATING","description":"The repository is locked due to a migration.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"RepositoryNode","description":"Represents a object that belongs to a repository.","fields":[{"name":"repository","description":"The repository associated with this node.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"CommitCommentThread","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestCommitCommentThread","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"RepositoryVulnerabilityAlert","ofType":null}]},{"kind":"INPUT_OBJECT","name":"RepositoryOrder","description":"Ordering options for repository connections","fields":null,"inputFields":[{"name":"field","description":"The field to order repositories by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryOrderField","description":"Properties by which repository connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order repositories by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order repositories by update time","isDeprecated":false,"deprecationReason":null},{"name":"PUSHED_AT","description":"Order repositories by push time","isDeprecated":false,"deprecationReason":null},{"name":"NAME","description":"Order repositories by name","isDeprecated":false,"deprecationReason":null},{"name":"STARGAZERS","description":"Order repositories by number of stargazers","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INTERFACE","name":"RepositoryOwner","description":"Represents an owner of a Repository.","fields":[{"name":"avatarUrl","description":"A URL pointing to the owner's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username used to login.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"A list of repositories that the user owns.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"affiliations","description":"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":null},{"name":"ownerAffiliations","description":"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":"[OWNER, COLLABORATOR]"},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isFork","description":"If non-null, filters repositories according to whether they are forks of another repository","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Find Repository.","args":[{"name":"name","description":"Name of Repository to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP URL for the owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for the owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"ENUM","name":"RepositoryPermission","description":"The access level to a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ADMIN","description":"Can read, clone, and push to this repository. Can also manage issues, pull requests, and repository settings, including adding collaborators","isDeprecated":false,"deprecationReason":null},{"name":"MAINTAIN","description":"Can read, clone, and push to this repository. They can also manage issues, pull requests, and some repository settings","isDeprecated":false,"deprecationReason":null},{"name":"WRITE","description":"Can read, clone, and push to this repository. Can also manage issues and pull requests","isDeprecated":false,"deprecationReason":null},{"name":"TRIAGE","description":"Can read and clone this repository. Can also manage issues and pull requests","isDeprecated":false,"deprecationReason":null},{"name":"READ","description":"Can read and clone this repository. Can also open and comment on issues and pull requests","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"RepositoryPrivacy","description":"The privacy of a repository","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PUBLIC","description":"Public","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"Private","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryTopic","description":"A repository-topic connects a repository to a topic.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this repository-topic.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"topic","description":"The topic.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Topic","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this repository-topic.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryTopicConnection","description":"The connection type for RepositoryTopic.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryTopicEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryTopic","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryTopicEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"RepositoryTopic","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RepositoryVisibility","description":"The repository's visibility level.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PRIVATE","description":"The repository is visible only to those with explicit access.","isDeprecated":false,"deprecationReason":null},{"name":"PUBLIC","description":"The repository is visible to everyone.","isDeprecated":false,"deprecationReason":null},{"name":"INTERNAL","description":"The repository is visible only to users in the same business.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeDisableAuditEntry","description":"Audit log entry for a repository_visibility_change.disable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryVisibilityChangeEnableAuditEntry","description":"Audit log entry for a repository_visibility_change.enable event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseResourcePath","description":"The HTTP path for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseSlug","description":"The slug of the enterprise.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterpriseUrl","description":"The HTTP URL for this enterprise.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"EnterpriseAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryVulnerabilityAlert","description":"A alert for a repository with an affected vulnerability.","fields":[{"name":"createdAt","description":"When was the alert created?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"dismissReason","description":"The reason the alert was dismissed","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismissedAt","description":"When was the alert dismissed?","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismisser","description":"The user who dismissed the alert","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The associated repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"securityAdvisory","description":"The associated security advisory","args":[],"type":{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"securityVulnerability","description":"The associated security vulnerability","args":[],"type":{"kind":"OBJECT","name":"SecurityVulnerability","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerableManifestFilename","description":"The vulnerable manifest filename","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerableManifestPath","description":"The vulnerable manifest path","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerableRequirements","description":"The vulnerable requirements","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"RepositoryNode","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryVulnerabilityAlertConnection","description":"The connection type for RepositoryVulnerabilityAlert.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryVulnerabilityAlertEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryVulnerabilityAlert","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RepositoryVulnerabilityAlertEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"RepositoryVulnerabilityAlert","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"RequestReviewsInput","description":"Autogenerated input type of RequestReviews","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Node ID of the pull request to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"userIds","description":"The Node IDs of the user to request.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"teamIds","description":"The Node IDs of the team to request.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"union","description":"Add users to the set rather than replace.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RequestReviewsPayload","description":"Autogenerated return type of RequestReviews","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The pull request that is getting requests.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"requestedReviewersEdge","description":"The edge from the pull request to the requested reviewers.","args":[],"type":{"kind":"OBJECT","name":"UserEdge","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"RequestableCheckStatusState","description":"The possible states that can be requested when creating a check run.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUEUED","description":"The check suite or run has been queued.","isDeprecated":false,"deprecationReason":null},{"name":"IN_PROGRESS","description":"The check suite or run is in progress.","isDeprecated":false,"deprecationReason":null},{"name":"COMPLETED","description":"The check suite or run has been completed.","isDeprecated":false,"deprecationReason":null},{"name":"WAITING","description":"The check suite or run is in waiting state.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"UNION","name":"RequestedReviewer","description":"Types that can be requested reviewers.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Mannequin","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"INTERFACE","name":"RequirableByPullRequest","description":"Represents a type that can be required by a pull request for merging.","fields":[{"name":"isRequired","description":"Whether this is required to pass before merging for a specific pull request.","args":[{"name":"pullRequestId","description":"The id of the pull request this is required for","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestNumber","description":"The number of the pull request this is required for","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CheckRun","ofType":null},{"kind":"OBJECT","name":"StatusContext","ofType":null}]},{"kind":"INPUT_OBJECT","name":"RerequestCheckSuiteInput","description":"Autogenerated input type of RerequestCheckSuite","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"checkSuiteId","description":"The Node ID of the check suite.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RerequestCheckSuitePayload","description":"Autogenerated return type of RerequestCheckSuite","fields":[{"name":"checkSuite","description":"The requested check suite.","args":[],"type":{"kind":"OBJECT","name":"CheckSuite","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"ResolveReviewThreadInput","description":"Autogenerated input type of ResolveReviewThread","fields":null,"inputFields":[{"name":"threadId","description":"The ID of the thread to resolve","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ResolveReviewThreadPayload","description":"Autogenerated return type of ResolveReviewThread","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"thread","description":"The thread to resolve.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"RestrictedContribution","description":"Represents a private contribution a user made on GitHub.","fields":[{"name":"isRestricted","description":"Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"occurredAt","description":"When this contribution was made.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this contribution.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who made this contribution.\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Contribution","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewDismissalAllowance","description":"A team or user who has the ability to dismiss a review on a protected branch.","fields":[{"name":"actor","description":"The actor that can dismiss.","args":[],"type":{"kind":"UNION","name":"ReviewDismissalAllowanceActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"branchProtectionRule","description":"Identifies the branch protection rule associated with the allowed user or team.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"ReviewDismissalAllowanceActor","description":"Types that can be an actor.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Team","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"ReviewDismissalAllowanceConnection","description":"The connection type for ReviewDismissalAllowance.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReviewDismissalAllowanceEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReviewDismissalAllowance","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewDismissalAllowanceEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ReviewDismissalAllowance","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewDismissedEvent","description":"Represents a 'review_dismissed' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismissalMessage","description":"Identifies the optional message associated with the 'review_dismissed' event.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dismissalMessageHTML","description":"Identifies the optional message associated with the event, rendered to HTML.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"previousReviewState","description":"Identifies the previous state of the review with the 'review_dismissed' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestReviewState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestCommit","description":"Identifies the commit which caused the review to become stale.","args":[],"type":{"kind":"OBJECT","name":"PullRequestCommit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this review dismissed event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"review","description":"Identifies the review associated with the 'review_dismissed' event.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this review dismissed event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewRequest","description":"A request for a user to review a pull request.","fields":[{"name":"asCodeOwner","description":"Whether this request was created for a code owner","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"Identifies the pull request associated with this review request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requestedReviewer","description":"The reviewer that is requested.","args":[],"type":{"kind":"UNION","name":"RequestedReviewer","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewRequestConnection","description":"The connection type for ReviewRequest.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReviewRequestEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"ReviewRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewRequestEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"ReviewRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewRequestRemovedEvent","description":"Represents an 'review_request_removed' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requestedReviewer","description":"Identifies the reviewer whose review request was removed.","args":[],"type":{"kind":"UNION","name":"RequestedReviewer","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewRequestedEvent","description":"Represents an 'review_requested' event on a given pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"PullRequest referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequest","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"requestedReviewer","description":"Identifies the reviewer whose review was requested.","args":[],"type":{"kind":"UNION","name":"RequestedReviewer","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ReviewStatusHovercardContext","description":"A hovercard context with a message describing the current code review state of the pull\nrequest.\n","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reviewDecision","description":"The current status of the pull request with respect to code review.","args":[],"type":{"kind":"ENUM","name":"PullRequestReviewDecision","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"HovercardContext","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SamlDigestAlgorithm","description":"The possible digest algorithms used to sign SAML requests for an identity provider.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SHA1","description":"SHA1","isDeprecated":false,"deprecationReason":null},{"name":"SHA256","description":"SHA256","isDeprecated":false,"deprecationReason":null},{"name":"SHA384","description":"SHA384","isDeprecated":false,"deprecationReason":null},{"name":"SHA512","description":"SHA512","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"SamlSignatureAlgorithm","description":"The possible signature algorithms used to sign SAML requests for a Identity Provider.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"RSA_SHA1","description":"RSA-SHA1","isDeprecated":false,"deprecationReason":null},{"name":"RSA_SHA256","description":"RSA-SHA256","isDeprecated":false,"deprecationReason":null},{"name":"RSA_SHA384","description":"RSA-SHA384","isDeprecated":false,"deprecationReason":null},{"name":"RSA_SHA512","description":"RSA-SHA512","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SavedReply","description":"A Saved Reply is text a user can use to reply quickly.","fields":[{"name":"body","description":"The body of the saved reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The saved reply body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"The title of the saved reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user that saved this reply.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SavedReplyConnection","description":"The connection type for SavedReply.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SavedReplyEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SavedReply","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SavedReplyEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"SavedReply","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SavedReplyOrder","description":"Ordering options for saved reply connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order saved replies by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SavedReplyOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SavedReplyOrderField","description":"Properties by which saved reply connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"UPDATED_AT","description":"Order saved reply by when they were updated.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"UNION","name":"SearchResultItem","description":"The results of a search.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"App","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"MarketplaceListing","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"SearchResultItemConnection","description":"A list of results that matched against a search query.","fields":[{"name":"codeCount","description":"The number of pieces of code that matched the search query.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SearchResultItemEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issueCount","description":"The number of issues that matched the search query.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"SearchResultItem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryCount","description":"The number of repositories that matched the search query.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userCount","description":"The number of users that matched the search query.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"wikiCount","description":"The number of wiki pages that matched the search query.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SearchResultItemEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"SearchResultItem","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"textMatches","description":"Text matches on the result found.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TextMatch","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SearchType","description":"Represents the individual results of a search.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ISSUE","description":"Returns results matching issues in repositories.","isDeprecated":false,"deprecationReason":null},{"name":"REPOSITORY","description":"Returns results matching repositories.","isDeprecated":false,"deprecationReason":null},{"name":"USER","description":"Returns results matching users and organizations on GitHub.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisory","description":"A GitHub Security Advisory","fields":[{"name":"cvss","description":"The CVSS associated with this advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CVSS","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"cwes","description":"CWEs associated with this Advisory","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CWEConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"This is a long plaintext description of the advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"ghsaId","description":"The GitHub Security Advisory ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"identifiers","description":"A list of identifiers for this advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisoryIdentifier","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"notificationsPermalink","description":"The permalink for the advisory's dependabot alerts page","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"origin","description":"The organization that originated the advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permalink","description":"The permalink for the advisory","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"When the advisory was published","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"references","description":"A list of references for this advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisoryReference","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"severity","description":"The severity of the advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisorySeverity","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"summary","description":"A short plaintext summary of the advisory","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"When the advisory was last updated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerabilities","description":"Vulnerabilities associated with this Advisory","args":[{"name":"orderBy","description":"Ordering options for the returned topics.","type":{"kind":"INPUT_OBJECT","name":"SecurityVulnerabilityOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"},{"name":"ecosystem","description":"An ecosystem to filter vulnerabilities by.","type":{"kind":"ENUM","name":"SecurityAdvisoryEcosystem","ofType":null},"defaultValue":null},{"name":"package","description":"A package name to filter vulnerabilities by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"severities","description":"A list of severities to filter vulnerabilities by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisorySeverity","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityVulnerabilityConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"withdrawnAt","description":"When the advisory was withdrawn, if it has been withdrawn","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryConnection","description":"The connection type for SecurityAdvisory.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisoryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SecurityAdvisoryEcosystem","description":"The possible ecosystems of a security vulnerability's package.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"RUBYGEMS","description":"Ruby gems hosted at RubyGems.org","isDeprecated":false,"deprecationReason":null},{"name":"NPM","description":"JavaScript packages hosted at npmjs.com","isDeprecated":false,"deprecationReason":null},{"name":"PIP","description":"Python packages hosted at PyPI.org","isDeprecated":false,"deprecationReason":null},{"name":"MAVEN","description":"Java artifacts hosted at the Maven central repository","isDeprecated":false,"deprecationReason":null},{"name":"NUGET","description":".NET packages hosted at the NuGet Gallery","isDeprecated":false,"deprecationReason":null},{"name":"COMPOSER","description":"PHP packages hosted at packagist.org","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryIdentifier","description":"A GitHub Security Advisory Identifier","fields":[{"name":"type","description":"The identifier type, e.g. GHSA, CVE","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"value","description":"The identifier","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SecurityAdvisoryIdentifierFilter","description":"An advisory identifier to filter results on.","fields":null,"inputFields":[{"name":"type","description":"The identifier type.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisoryIdentifierType","ofType":null}},"defaultValue":null},{"name":"value","description":"The identifier string. Supports exact or partial matching.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SecurityAdvisoryIdentifierType","description":"Identifier formats available for advisories.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CVE","description":"Common Vulnerabilities and Exposures Identifier.","isDeprecated":false,"deprecationReason":null},{"name":"GHSA","description":"GitHub Security Advisory ID.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SecurityAdvisoryOrder","description":"Ordering options for security advisory connections","fields":null,"inputFields":[{"name":"field","description":"The field to order security advisories by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisoryOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SecurityAdvisoryOrderField","description":"Properties by which security advisory connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PUBLISHED_AT","description":"Order advisories by publication time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order advisories by update time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryPackage","description":"An individual package","fields":[{"name":"ecosystem","description":"The ecosystem the package belongs to, e.g. RUBYGEMS, NPM","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisoryEcosystem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The package name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryPackageVersion","description":"An individual package version","fields":[{"name":"identifier","description":"The package name or version","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityAdvisoryReference","description":"A GitHub Security Advisory Reference","fields":[{"name":"url","description":"A publicly accessible reference","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SecurityAdvisorySeverity","description":"Severity of the vulnerability.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LOW","description":"Low.","isDeprecated":false,"deprecationReason":null},{"name":"MODERATE","description":"Moderate.","isDeprecated":false,"deprecationReason":null},{"name":"HIGH","description":"High.","isDeprecated":false,"deprecationReason":null},{"name":"CRITICAL","description":"Critical.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SecurityVulnerability","description":"An individual vulnerability within an Advisory","fields":[{"name":"advisory","description":"The Advisory associated with this Vulnerability","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisory","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"firstPatchedVersion","description":"The first version containing a fix for the vulnerability","args":[],"type":{"kind":"OBJECT","name":"SecurityAdvisoryPackageVersion","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"package","description":"A description of the vulnerable package","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SecurityAdvisoryPackage","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"severity","description":"The severity of the vulnerability within this package","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityAdvisorySeverity","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"When the vulnerability was last updated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"vulnerableVersionRange","description":"A string that describes the vulnerable package versions.\nThis string follows a basic syntax with a few forms.\n+ `= 0.2.0` denotes a single vulnerable version.\n+ `<= 1.0.8` denotes a version range up to and including the specified version\n+ `< 0.1.11` denotes a version range up to, but excluding, the specified version\n+ `>= 4.3.0, < 4.3.5` denotes a version range with a known minimum and maximum version.\n+ `>= 0.0.1` denotes a version range with a known minimum, but no known maximum\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityVulnerabilityConnection","description":"The connection type for SecurityVulnerability.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SecurityVulnerabilityEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SecurityVulnerability","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SecurityVulnerabilityEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"SecurityVulnerability","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SecurityVulnerabilityOrder","description":"Ordering options for security vulnerability connections","fields":null,"inputFields":[{"name":"field","description":"The field to order security vulnerabilities by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SecurityVulnerabilityOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SecurityVulnerabilityOrderField","description":"Properties by which security vulnerability connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"UPDATED_AT","description":"Order vulnerability by update time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SetEnterpriseIdentityProviderInput","description":"Autogenerated input type of SetEnterpriseIdentityProvider","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set an identity provider.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"ssoUrl","description":"The URL endpoint for the identity provider's SAML SSO.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"defaultValue":null},{"name":"issuer","description":"The Issuer Entity ID for the SAML identity provider","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"idpCertificate","description":"The x509 certificate used by the identity provider to sign assertions and responses.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"signatureMethod","description":"The signature algorithm used to sign SAML requests for the identity provider.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SamlSignatureAlgorithm","ofType":null}},"defaultValue":null},{"name":"digestMethod","description":"The digest algorithm used to sign SAML requests for the identity provider.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SamlDigestAlgorithm","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SetEnterpriseIdentityProviderPayload","description":"Autogenerated return type of SetEnterpriseIdentityProvider","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"identityProvider","description":"The identity provider for the enterprise.","args":[],"type":{"kind":"OBJECT","name":"EnterpriseIdentityProvider","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SetOrganizationInteractionLimitInput","description":"Autogenerated input type of SetOrganizationInteractionLimit","fields":null,"inputFields":[{"name":"organizationId","description":"The ID of the organization to set a limit for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"limit","description":"The limit to set.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInteractionLimit","ofType":null}},"defaultValue":null},{"name":"expiry","description":"When this limit should expire.","type":{"kind":"ENUM","name":"RepositoryInteractionLimitExpiry","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SetOrganizationInteractionLimitPayload","description":"Autogenerated return type of SetOrganizationInteractionLimit","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization that the interaction limit was set for.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SetRepositoryInteractionLimitInput","description":"Autogenerated input type of SetRepositoryInteractionLimit","fields":null,"inputFields":[{"name":"repositoryId","description":"The ID of the repository to set a limit for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"limit","description":"The limit to set.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInteractionLimit","ofType":null}},"defaultValue":null},{"name":"expiry","description":"When this limit should expire.","type":{"kind":"ENUM","name":"RepositoryInteractionLimitExpiry","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SetRepositoryInteractionLimitPayload","description":"Autogenerated return type of SetRepositoryInteractionLimit","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that the interaction limit was set for.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SetUserInteractionLimitInput","description":"Autogenerated input type of SetUserInteractionLimit","fields":null,"inputFields":[{"name":"userId","description":"The ID of the user to set a limit for.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"limit","description":"The limit to set.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryInteractionLimit","ofType":null}},"defaultValue":null},{"name":"expiry","description":"When this limit should expire.","type":{"kind":"ENUM","name":"RepositoryInteractionLimitExpiry","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SetUserInteractionLimitPayload","description":"Autogenerated return type of SetUserInteractionLimit","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user that the interaction limit was set for.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SmimeSignature","description":"Represents an S/MIME signature on a Commit or Tag.","fields":[{"name":"email","description":"Email used to sign this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isValid","description":"True if the signature is valid and verified by GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"Payload for GPG signing object. Raw ODB object without the signature header.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signature","description":"ASCII-armored signature header from object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signer","description":"GitHub user corresponding to the email signing this commit.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"GitSignatureState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"wasSignedByGitHub","description":"True if the signature was made with GitHub's signing key.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"GitSignature","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"Sponsor","description":"Entities that can sponsor others via GitHub Sponsors","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"INTERFACE","name":"Sponsorable","description":"Entities that can be sponsored through GitHub Sponsors","fields":[{"name":"hasSponsorsListing","description":"True if this user/organization has a GitHub Sponsors listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoredBy","description":"Check if the given account is sponsoring this user/organization.","args":[{"name":"accountLogin","description":"The target account's login.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoringViewer","description":"True if the viewer is sponsored by this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorsListing","description":"The GitHub Sponsors listing for this user or organization.","args":[],"type":{"kind":"OBJECT","name":"SponsorsListing","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipForViewerAsSponsor","description":"The viewer's sponsorship of this entity.","args":[],"type":{"kind":"OBJECT","name":"Sponsorship","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsMaintainer","description":"This object's sponsorships as the maintainer.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"includePrivate","description":"Whether or not to include private sponsorships in the result set","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsSponsor","description":"This object's sponsorships as the sponsor.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSponsor","description":"Whether or not the viewer is able to sponsor this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsSponsoring","description":"True if the viewer is sponsoring this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"UNION","name":"SponsorableItem","description":"Entities that can be sponsored via GitHub Sponsors","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"SponsorableItemConnection","description":"The connection type for SponsorableItem.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SponsorableItemEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"SponsorableItem","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorableItemEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"SponsorableItem","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SponsorableOrder","description":"Ordering options for connections to get sponsorable entities for GitHub Sponsors.","fields":null,"inputFields":[{"name":"field","description":"The field to order sponsorable entities by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SponsorableOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SponsorableOrderField","description":"Properties by which sponsorable connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LOGIN","description":"Order sponsorable entities by login (username).","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsGoal","description":"A goal associated with a GitHub Sponsors listing, representing a target the sponsored maintainer would like to attain.","fields":[{"name":"description","description":"A description of the goal from the maintainer.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"kind","description":"What the objective of this goal is.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SponsorsGoalKind","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"percentComplete","description":"The percentage representing how complete this goal is, between 0-100.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"targetValue","description":"What the goal amount is. Represents a dollar amount for monthly sponsorship amount goals. Represents a count of unique sponsors for total sponsors count goals.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"A brief summary of the kind and target value of this goal.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SponsorsGoalKind","description":"The different kinds of goals a GitHub Sponsors member can have.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"TOTAL_SPONSORS_COUNT","description":"The goal is about reaching a certain number of sponsors.","isDeprecated":false,"deprecationReason":null},{"name":"MONTHLY_SPONSORSHIP_AMOUNT","description":"The goal is about getting a certain dollar amount from sponsorships each month.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsListing","description":"A GitHub Sponsors listing.","fields":[{"name":"activeGoal","description":"The current goal the maintainer is trying to reach with GitHub Sponsors, if any.","args":[],"type":{"kind":"OBJECT","name":"SponsorsGoal","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fullDescription","description":"The full description of the listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fullDescriptionHTML","description":"The full description of the listing rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The listing's full name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"shortDescription","description":"The short description of the listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"The short name of the listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tiers","description":"The published tiers for this GitHub Sponsors listing.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for Sponsors tiers returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"SponsorsTierOrder","ofType":null},"defaultValue":"{field: MONTHLY_PRICE_IN_CENTS, direction: ASC}"}],"type":{"kind":"OBJECT","name":"SponsorsTierConnection","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsTier","description":"A GitHub Sponsors tier associated with a GitHub Sponsors listing.","fields":[{"name":"adminInfo","description":"SponsorsTier information only visible to users that can administer the associated Sponsors listing.","args":[],"type":{"kind":"OBJECT","name":"SponsorsTierAdminInfo","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the tier.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"descriptionHTML","description":"The tier description rendered to HTML","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"monthlyPriceInCents","description":"How much this tier costs per month in cents.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"monthlyPriceInDollars","description":"How much this tier costs per month in dollars.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the tier.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorsListing","description":"The sponsors listing that this tier belongs to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorsListing","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsTierAdminInfo","description":"SponsorsTier information only visible to users that can administer the associated Sponsors listing.","fields":[{"name":"sponsorships","description":"The sponsorships associated with this tier.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"includePrivate","description":"Whether or not to include private sponsorships in the result set","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsTierConnection","description":"The connection type for SponsorsTier.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SponsorsTierEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SponsorsTier","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorsTierEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"SponsorsTier","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SponsorsTierOrder","description":"Ordering options for Sponsors tiers connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order tiers by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SponsorsTierOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SponsorsTierOrderField","description":"Properties by which Sponsors tiers connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order tiers by creation time.","isDeprecated":false,"deprecationReason":null},{"name":"MONTHLY_PRICE_IN_CENTS","description":"Order tiers by their monthly price in cents","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"Sponsorship","description":"A sponsorship relationship between a sponsor and a maintainer","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"maintainer","description":"The entity that is being sponsored","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":true,"deprecationReason":"`Sponsorship.maintainer` will be removed. Use `Sponsorship.sponsorable` instead. Removal on 2020-04-01 UTC."},{"name":"privacyLevel","description":"The privacy level for this sponsorship.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SponsorshipPrivacy","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsor","description":"The user that is sponsoring. Returns null if the sponsorship is private or if sponsor is not a user.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":true,"deprecationReason":"`Sponsorship.sponsor` will be removed. Use `Sponsorship.sponsorEntity` instead. Removal on 2020-10-01 UTC."},{"name":"sponsorEntity","description":"The user or organization that is sponsoring, if you have permission to view them.","args":[],"type":{"kind":"UNION","name":"Sponsor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorable","description":"The entity that is being sponsored","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Sponsorable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tier","description":"The associated sponsorship tier","args":[],"type":{"kind":"OBJECT","name":"SponsorsTier","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorshipConnection","description":"The connection type for Sponsorship.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Sponsorship","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SponsorshipEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Sponsorship","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","description":"Ordering options for sponsorship connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order sponsorship by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SponsorshipOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SponsorshipOrderField","description":"Properties by which sponsorship connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order sponsorship by creation time.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"SponsorshipPrivacy","description":"The privacy of a sponsorship","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"PUBLIC","description":"Public","isDeprecated":false,"deprecationReason":null},{"name":"PRIVATE","description":"Private","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"StarOrder","description":"Ways in which star connections can be ordered.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order nodes by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"StarOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"StarOrderField","description":"Properties by which star connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"STARRED_AT","description":"Allows ordering a list of stars by when they were created.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"StargazerConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"StargazerEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StargazerEdge","description":"Represents a user that's starred a repository.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"starredAt","description":"Identifies when the item was starred.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Starrable","description":"Things that can be starred.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazerCount","description":"Returns a count of how many stargazers there are on this object\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazers","description":"A list of users who have starred this starrable.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"StarOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StargazerConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasStarred","description":"Returns a boolean indicating whether the viewing user has starred this starrable.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Gist","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"Topic","ofType":null}]},{"kind":"OBJECT","name":"StarredRepositoryConnection","description":"The connection type for Repository.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"StarredRepositoryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isOverLimit","description":"Is the list of stars for this user truncated? This is true for users that have many stars.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StarredRepositoryEdge","description":"Represents a starred repository.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"starredAt","description":"Identifies when the item was starred.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Status","description":"Represents a commit status.","fields":[{"name":"combinedContexts","description":"A list of status contexts and check runs for this commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StatusCheckRollupContextConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"The commit this status is attached to.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"context","description":"Looks up an individual status context by context name.","args":[{"name":"name","description":"The context name.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"StatusContext","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"contexts","description":"The individual status contexts for this commit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StatusContext","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The combined commit status.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"StatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StatusCheckRollup","description":"Represents the rollup for both the check runs and status for a commit.","fields":[{"name":"commit","description":"The commit the status and check runs are attached to.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"contexts","description":"A list of status contexts and check runs for this commit.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StatusCheckRollupContextConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The combined status for the commit.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"StatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"UNION","name":"StatusCheckRollupContext","description":"Types that can be inside a StatusCheckRollup context.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CheckRun","ofType":null},{"kind":"OBJECT","name":"StatusContext","ofType":null}]},{"kind":"OBJECT","name":"StatusCheckRollupContextConnection","description":"The connection type for StatusCheckRollupContext.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"StatusCheckRollupContextEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"UNION","name":"StatusCheckRollupContext","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StatusCheckRollupContextEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"UNION","name":"StatusCheckRollupContext","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"StatusContext","description":"Represents an individual commit status context","fields":[{"name":"avatarUrl","description":"The avatar of the OAuth application or the user that created the status","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"40"}],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"commit","description":"This commit this status context is attached to.","args":[],"type":{"kind":"OBJECT","name":"Commit","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"context","description":"The name of this status context.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"creator","description":"The actor who created this status context.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description for this status context.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRequired","description":"Whether this is required to pass before merging for a specific pull request.","args":[{"name":"pullRequestId","description":"The id of the pull request this is required for","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestNumber","description":"The number of the pull request this is required for","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of this status context.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"StatusState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"targetUrl","description":"The URL for this status context.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"RequirableByPullRequest","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"StatusState","description":"The possible commit status states.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"EXPECTED","description":"Status is expected.","isDeprecated":false,"deprecationReason":null},{"name":"ERROR","description":"Status is errored.","isDeprecated":false,"deprecationReason":null},{"name":"FAILURE","description":"Status is failing.","isDeprecated":false,"deprecationReason":null},{"name":"PENDING","description":"Status is pending.","isDeprecated":false,"deprecationReason":null},{"name":"SUCCESS","description":"Status is successful.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"SCALAR","name":"String","description":"Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"SubmitPullRequestReviewInput","description":"Autogenerated input type of SubmitPullRequestReview","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Pull Request ID to submit any pending reviews.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"pullRequestReviewId","description":"The Pull Request Review ID to submit.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"event","description":"The event to send to the Pull Request Review.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestReviewEvent","ofType":null}},"defaultValue":null},{"name":"body","description":"The text field to set on the Pull Request Review.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SubmitPullRequestReviewPayload","description":"Autogenerated return type of SubmitPullRequestReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The submitted pull request review.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Submodule","description":"A pointer to a repository at a specific revision embedded inside another repository.","fields":[{"name":"branch","description":"The branch of the upstream submodule for tracking updates","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"gitUrl","description":"The git URL of the submodule repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the submodule in .gitmodules","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The path in the superproject that this submodule is located in","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subprojectCommitOid","description":"The commit revision of the subproject repository being tracked by the submodule","args":[],"type":{"kind":"SCALAR","name":"GitObjectID","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SubmoduleConnection","description":"The connection type for Submodule.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"SubmoduleEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Submodule","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"SubmoduleEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Submodule","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Subscribable","description":"Entities that can be subscribed to for web and email notifications.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"Team","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null}]},{"kind":"OBJECT","name":"SubscribedEvent","description":"Represents a 'subscribed' event on a given `Subscribable`.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subscribable","description":"Object referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Subscribable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"SubscriptionState","description":"The possible states of a subscription.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"UNSUBSCRIBED","description":"The User is only notified when participating or @mentioned.","isDeprecated":false,"deprecationReason":null},{"name":"SUBSCRIBED","description":"The User is notified of all conversations.","isDeprecated":false,"deprecationReason":null},{"name":"IGNORED","description":"The User is never notified.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"SuggestedReviewer","description":"A suggestion to review a pull request based on a user's commit history and review comments.","fields":[{"name":"isAuthor","description":"Is this suggestion based on past commits?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCommenter","description":"Is this suggestion based on past review comments?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"reviewer","description":"Identifies the user suggested to review the pull request.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Tag","description":"Represents a Git tag.","fields":[{"name":"abbreviatedOid","description":"An abbreviated version of the Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitResourcePath","description":"The HTTP path for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitUrl","description":"The HTTP URL for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"The Git tag message.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The Git tag name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"The Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the Git object belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tagger","description":"Details about the tag author.","args":[],"type":{"kind":"OBJECT","name":"GitActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"target","description":"The Git object the tag points to.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"GitObject","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"GitObject","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Team","description":"A team of users in an organization.","fields":[{"name":"ancestors","description":"A list of teams that are ancestors of this team.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"avatarUrl","description":"A URL pointing to the team's avatar.","args":[{"name":"size","description":"The size in pixels of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"400"}],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"childTeams","description":"List of child teams belonging to this team","args":[{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"TeamOrder","ofType":null},"defaultValue":null},{"name":"userLogins","description":"User logins to filter by","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"immediateOnly","description":"Whether to list immediate child teams or all descendant child teams.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"true"},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"combinedSlug","description":"The slug corresponding to the organization and team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":"The description of the team.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"discussion","description":"Find a team discussion by its number.","args":[{"name":"number","description":"The sequence number of the discussion to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"discussions","description":"A list of team discussions.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isPinned","description":"If provided, filters discussions according to whether or not they are pinned.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"TeamDiscussionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"discussionsResourcePath","description":"The HTTP path for team discussions","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"discussionsUrl","description":"The HTTP URL for team discussions","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editTeamResourcePath","description":"The HTTP path for editing this team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editTeamUrl","description":"The HTTP URL for editing this team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"invitations","description":"A list of pending invitations for users to this team","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"OrganizationInvitationConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"memberStatuses","description":"Get the status messages members of this entity have set that are either public or visible only to the organization.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for user statuses returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"UserStatusOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"UserStatusConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"members","description":"A list of users who are members of this team.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"membership","description":"Filter by membership type","type":{"kind":"ENUM","name":"TeamMembershipType","ofType":null},"defaultValue":"ALL"},{"name":"role","description":"Filter by team member role","type":{"kind":"ENUM","name":"TeamMemberRole","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for the connection.","type":{"kind":"INPUT_OBJECT","name":"TeamMemberOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamMemberConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersResourcePath","description":"The HTTP path for the team' members","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"membersUrl","description":"The HTTP URL for the team' members","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The name of the team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"newTeamResourcePath","description":"The HTTP path creating a new team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"newTeamUrl","description":"The HTTP URL creating a new team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization that owns this team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Organization","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeam","description":"The parent team of the team.","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"privacy","description":"The level of privacy the team has.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamPrivacy","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"A list of repositories this team has access to.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"query","description":"The search string to look for.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for the connection.","type":{"kind":"INPUT_OBJECT","name":"TeamRepositoryOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamRepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoriesResourcePath","description":"The HTTP path for this team's repositories","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoriesUrl","description":"The HTTP URL for this team's repositories","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"slug","description":"The slug corresponding to the team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsResourcePath","description":"The HTTP path for this team's teams","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"teamsUrl","description":"The HTTP URL for this team's teams","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this team","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanAdminister","description":"Team is adminable by the viewer.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"MemberStatusable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","description":"Audit log entry for a team.add_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLdapMapped","description":"Whether the team was mapped to an LDAP Group.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","description":"Audit log entry for a team.add_repository event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLdapMapped","description":"Whether the team was mapped to an LDAP Group.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","description":"Metadata for an audit entry with action team.*","fields":[{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"OrgRestoreMemberMembershipTeamAuditEntryData","ofType":null},{"kind":"OBJECT","name":"TeamAddMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamAddRepositoryAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","ofType":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","ofType":null}]},{"kind":"OBJECT","name":"TeamChangeParentTeamAuditEntry","description":"Audit log entry for a team.change_parent_team event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLdapMapped","description":"Whether the team was mapped to an LDAP Group.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeam","description":"The new parent team.","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamName","description":"The name of the new parent team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamNameWas","description":"The name of the former parent team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamResourcePath","description":"The HTTP path for the parent team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamUrl","description":"The HTTP URL for the parent team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamWas","description":"The former parent team.","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamWasResourcePath","description":"The HTTP path for the previous parent team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"parentTeamWasUrl","description":"The HTTP URL for the previous parent team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamConnection","description":"The connection type for Team.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Team","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussion","description":"A team discussion.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the discussion's team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The body as Markdown.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyVersion","description":"Identifies the discussion body hash.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"comments","description":"A list of comments on this discussion.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"TeamDiscussionCommentOrder","ofType":null},"defaultValue":null},{"name":"fromComment","description":"When provided, filters the connection such that results begin with the comment with this number.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussionCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commentsResourcePath","description":"The HTTP path for discussion comments","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commentsUrl","description":"The HTTP URL for discussion comments","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPinned","description":"Whether or not the discussion is pinned.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isPrivate","description":"Whether or not the discussion is only visible to team members and org admins.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"Identifies the discussion within its team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this discussion","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team that defines the context of this discussion.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Team","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"title","description":"The title of the discussion","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this discussion","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanPin","description":"Whether or not the current viewer can pin this discussion.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSubscribe","description":"Check if the viewer is able to change their subscription status for the repository.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerSubscription","description":"Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.","args":[],"type":{"kind":"ENUM","name":"SubscriptionState","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"Subscribable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussionComment","description":"A comment on a team discussion.","fields":[{"name":"author","description":"The actor who authored the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"authorAssociation","description":"Author's association with the comment's team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentAuthorAssociation","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"body","description":"The body as Markdown.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyHTML","description":"The body rendered to HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyText","description":"The body rendered to text.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bodyVersion","description":"The current version of the body content.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdViaEmail","description":"Check if this comment was created via an email reply.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"discussion","description":"The discussion this comment is about.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited the comment.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"includesCreatedEdit","description":"Check if this comment was edited and includes an edit with the creation data","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lastEditedAt","description":"The moment the editor made the last edit","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"number","description":"Identifies the comment number.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publishedAt","description":"Identifies when the comment was published at.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"reactionGroups","description":"A list of reactions grouped by content left on the subject.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionGroup","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"reactions","description":"A list of Reactions left on the Issue.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"content","description":"Allows filtering Reactions by emoji.","type":{"kind":"ENUM","name":"ReactionContent","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Allows specifying the order in which reactions are returned.","type":{"kind":"INPUT_OBJECT","name":"ReactionOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ReactionConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this comment","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this comment","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"userContentEdits","description":"A list of edits to this content.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"OBJECT","name":"UserContentEditConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanDelete","description":"Check if the current viewer can delete this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanReact","description":"Can user react to this subject","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerDidAuthor","description":"Did the viewer author this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Comment","ofType":null},{"kind":"INTERFACE","name":"Deletable","ofType":null},{"kind":"INTERFACE","name":"Reactable","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"Updatable","ofType":null},{"kind":"INTERFACE","name":"UpdatableComment","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussionCommentConnection","description":"The connection type for TeamDiscussionComment.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussionCommentEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussionCommentEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TeamDiscussionCommentOrder","description":"Ways in which team discussion comment connections can be ordered.","fields":null,"inputFields":[{"name":"field","description":"The field by which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamDiscussionCommentOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"TeamDiscussionCommentOrderField","description":"Properties by which team discussion comment connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NUMBER","description":"Allows sequential ordering of team discussion comments (which is equivalent to chronological ordering).","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussionConnection","description":"The connection type for TeamDiscussion.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussionEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamDiscussionEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TeamDiscussionOrder","description":"Ways in which team discussion connections can be ordered.","fields":null,"inputFields":[{"name":"field","description":"The field by which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamDiscussionOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"TeamDiscussionOrderField","description":"Properties by which team discussion connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Allows chronological ordering of team discussions.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"TeamEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamMemberConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamMemberEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamMemberEdge","description":"Represents a user who is a member of a team.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"memberAccessResourcePath","description":"The HTTP path to the organization's member access page.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"memberAccessUrl","description":"The HTTP URL to the organization's member access page.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"role","description":"The role the member has on the team.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamMemberRole","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TeamMemberOrder","description":"Ordering options for team member connections","fields":null,"inputFields":[{"name":"field","description":"The field to order team members by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamMemberOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"TeamMemberOrderField","description":"Properties by which team member connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"LOGIN","description":"Order team members by login","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order team members by creation time","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"TeamMemberRole","description":"The possible team member roles; either 'maintainer' or 'member'.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"MAINTAINER","description":"A team maintainer has permission to add and remove team members.","isDeprecated":false,"deprecationReason":null},{"name":"MEMBER","description":"A team member has no administrative permissions on the team.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"TeamMembershipType","description":"Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"IMMEDIATE","description":"Includes only immediate members of the team.","isDeprecated":false,"deprecationReason":null},{"name":"CHILD_TEAM","description":"Includes only child team members for the team.","isDeprecated":false,"deprecationReason":null},{"name":"ALL","description":"Includes immediate and child team members for the team.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TeamOrder","description":"Ways in which team connections can be ordered.","fields":null,"inputFields":[{"name":"field","description":"The field in which to order nodes by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The direction in which to order nodes.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"TeamOrderField","description":"Properties by which team connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NAME","description":"Allows ordering a list of teams by name.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"TeamPrivacy","description":"The possible team privacy values.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SECRET","description":"A secret team can only be seen by its members.","isDeprecated":false,"deprecationReason":null},{"name":"VISIBLE","description":"A visible team can be seen and @mentioned by every member of the organization.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"TeamRemoveMemberAuditEntry","description":"Audit log entry for a team.remove_member event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLdapMapped","description":"Whether the team was mapped to an LDAP Group.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamRemoveRepositoryAuditEntry","description":"Audit log entry for a team.remove_repository event.","fields":[{"name":"action","description":"The action name","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"actor","description":"The user who initiated the action","args":[],"type":{"kind":"UNION","name":"AuditEntryActor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorIp","description":"The IP address of the actor","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLocation","description":"A readable representation of the actor's location","args":[],"type":{"kind":"OBJECT","name":"ActorLocation","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorLogin","description":"The username of the user who initiated the action","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorResourcePath","description":"The HTTP path for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"actorUrl","description":"The HTTP URL for the actor.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"The time the action was initiated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"PreciseDateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isLdapMapped","description":"Whether the team was mapped to an LDAP Group.","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"operationType","description":"The corresponding operation type for the action","args":[],"type":{"kind":"ENUM","name":"OperationType","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The Organization associated with the Audit Entry.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationName","description":"The name of the Organization.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationResourcePath","description":"The HTTP path for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationUrl","description":"The HTTP URL for the organization","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository associated with the action","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryName","description":"The name of the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryResourcePath","description":"The HTTP path for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repositoryUrl","description":"The HTTP URL for the repository","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"team","description":"The team associated with the action","args":[],"type":{"kind":"OBJECT","name":"Team","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamName","description":"The name of the team","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamResourcePath","description":"The HTTP path for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamUrl","description":"The HTTP URL for this team","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user affected by the action","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userLogin","description":"For actions involving two users, the actor is the initiator and the user is the affected user.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userResourcePath","description":"The HTTP path for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"userUrl","description":"The HTTP URL for the user.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"AuditEntry","ofType":null},{"kind":"INTERFACE","name":"OrganizationAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"RepositoryAuditEntryData","ofType":null},{"kind":"INTERFACE","name":"TeamAuditEntryData","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamRepositoryConnection","description":"The connection type for Repository.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"TeamRepositoryEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TeamRepositoryEdge","description":"Represents a team repository.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"permission","description":"The permission level the team has on the repository","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"RepositoryPermission","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TeamRepositoryOrder","description":"Ordering options for team repository connections","fields":null,"inputFields":[{"name":"field","description":"The field to order repositories by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"TeamRepositoryOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"TeamRepositoryOrderField","description":"Properties by which team repository connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"CREATED_AT","description":"Order repositories by creation time","isDeprecated":false,"deprecationReason":null},{"name":"UPDATED_AT","description":"Order repositories by update time","isDeprecated":false,"deprecationReason":null},{"name":"PUSHED_AT","description":"Order repositories by push time","isDeprecated":false,"deprecationReason":null},{"name":"NAME","description":"Order repositories by name","isDeprecated":false,"deprecationReason":null},{"name":"PERMISSION","description":"Order repositories by permission","isDeprecated":false,"deprecationReason":null},{"name":"STARGAZERS","description":"Order repositories by number of stargazers","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"ENUM","name":"TeamRole","description":"The role of a user on a team.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ADMIN","description":"User has admin rights on the team.","isDeprecated":false,"deprecationReason":null},{"name":"MEMBER","description":"User is a member of the team.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"TextMatch","description":"A text match within a search result.","fields":[{"name":"fragment","description":"The specific text fragment within the property matched on.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"highlights","description":"Highlights within the matched fragment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TextMatchHighlight","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"property","description":"The property matched on.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TextMatchHighlight","description":"Represents a single highlight in a search result match.","fields":[{"name":"beginIndice","description":"The indice in the fragment where the matched text begins.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"endIndice","description":"The indice in the fragment where the matched text ends.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"text","description":"The text matched.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Topic","description":"A topic aggregates entities that are related to a subject.","fields":[{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The topic's name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"relatedTopics","description":"A list of related topics, including aliases of this topic, sorted with the most relevant\nfirst. Returns up to 10 Topics.\n","args":[{"name":"first","description":"How many topics to return.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":"3"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Topic","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazerCount","description":"Returns a count of how many stargazers there are on this object\n","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"stargazers","description":"A list of users who have starred this starrable.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"StarOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StargazerConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerHasStarred","description":"Returns a boolean indicating whether the viewing user has starred this starrable.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Starrable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"TopicAuditEntryData","description":"Metadata for an audit entry with a topic.","fields":[{"name":"topic","description":"The name of the topic added to the repository","args":[],"type":{"kind":"OBJECT","name":"Topic","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topicName","description":"The name of the topic added to the repository","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"RepoAddTopicAuditEntry","ofType":null},{"kind":"OBJECT","name":"RepoRemoveTopicAuditEntry","ofType":null}]},{"kind":"ENUM","name":"TopicSuggestionDeclineReason","description":"Reason that the suggested topic is declined.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"NOT_RELEVANT","description":"The suggested topic is not relevant to the repository.","isDeprecated":false,"deprecationReason":null},{"name":"TOO_SPECIFIC","description":"The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1).","isDeprecated":false,"deprecationReason":null},{"name":"PERSONAL_PREFERENCE","description":"The viewer does not like the suggested topic.","isDeprecated":false,"deprecationReason":null},{"name":"TOO_GENERAL","description":"The suggested topic is too general for the repository.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"TransferIssueInput","description":"Autogenerated input type of TransferIssue","fields":null,"inputFields":[{"name":"issueId","description":"The Node ID of the issue to be transferred","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"The Node ID of the repository the issue should be transferred to","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TransferIssuePayload","description":"Autogenerated return type of TransferIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was transferred","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TransferredEvent","description":"Represents a 'transferred' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"fromRepository","description":"The repository this came from","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"Identifies the issue associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"Tree","description":"Represents a Git tree.","fields":[{"name":"abbreviatedOid","description":"An abbreviated version of the Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitResourcePath","description":"The HTTP path for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitUrl","description":"The HTTP URL for this Git object","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"entries","description":"A list of tree entries.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"TreeEntry","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"The Git object ID","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the Git object belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"GitObject","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"TreeEntry","description":"Represents a Git tree entry.","fields":[{"name":"extension","description":"The extension of the file","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isGenerated","description":"Whether or not this tree entry is generated","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"mode","description":"Entry file mode.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"Entry file name.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"object","description":"Entry file object.","args":[],"type":{"kind":"INTERFACE","name":"GitObject","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"oid","description":"Entry file Git object ID.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"path","description":"The full path of the file.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The Repository the tree entry belongs to","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Repository","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"submodule","description":"If the TreeEntry is for a directory occupied by a submodule project, this returns the corresponding submodule","args":[],"type":{"kind":"OBJECT","name":"Submodule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":"Entry file type.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"URI","description":"An RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI string.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnarchiveRepositoryInput","description":"Autogenerated input type of UnarchiveRepository","fields":null,"inputFields":[{"name":"repositoryId","description":"The ID of the repository to unarchive.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnarchiveRepositoryPayload","description":"Autogenerated return type of UnarchiveRepository","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The repository that was unarchived.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnassignedEvent","description":"Represents an 'unassigned' event on any assignable object.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"assignable","description":"Identifies the assignable associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Assignable","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"assignee","description":"Identifies the user or mannequin that was unassigned.","args":[],"type":{"kind":"UNION","name":"Assignee","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"Identifies the subject (user) who was unassigned.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":true,"deprecationReason":"Assignees can now be mannequins. Use the `assignee` field instead. Removal on 2020-01-01 UTC."}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnfollowUserInput","description":"Autogenerated input type of UnfollowUser","fields":null,"inputFields":[{"name":"userId","description":"ID of the user to unfollow.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnfollowUserPayload","description":"Autogenerated return type of UnfollowUser","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user that was unfollowed.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","description":"Represents a type that can be retrieved by a URL.","fields":[{"name":"resourcePath","description":"The HTML path to this resource.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The URL to this resource.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Bot","ofType":null},{"kind":"OBJECT","name":"CheckRun","ofType":null},{"kind":"OBJECT","name":"ClosedEvent","ofType":null},{"kind":"OBJECT","name":"Commit","ofType":null},{"kind":"OBJECT","name":"ConvertToDraftEvent","ofType":null},{"kind":"OBJECT","name":"CrossReferencedEvent","ofType":null},{"kind":"OBJECT","name":"Gist","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"Mannequin","ofType":null},{"kind":"OBJECT","name":"MergedEvent","ofType":null},{"kind":"OBJECT","name":"Milestone","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestCommit","ofType":null},{"kind":"OBJECT","name":"ReadyForReviewEvent","ofType":null},{"kind":"OBJECT","name":"Release","ofType":null},{"kind":"OBJECT","name":"Repository","ofType":null},{"kind":"OBJECT","name":"RepositoryTopic","ofType":null},{"kind":"OBJECT","name":"ReviewDismissedEvent","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null},{"kind":"OBJECT","name":"User","ofType":null}]},{"kind":"OBJECT","name":"UnknownSignature","description":"Represents an unknown signature on a Commit or Tag.","fields":[{"name":"email","description":"Email used to sign this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isValid","description":"True if the signature is valid and verified by GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"payload","description":"Payload for GPG signing object. Raw ODB object without the signature header.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signature","description":"ASCII-armored signature header from object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"signer","description":"GitHub user corresponding to the email signing this commit.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"state","description":"The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"GitSignatureState","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"wasSignedByGitHub","description":"True if the signature was made with GitHub's signing key.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"GitSignature","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnlabeledEvent","description":"Represents an 'unlabeled' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"label","description":"Identifies the label associated with the 'unlabeled' event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Label","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"labelable","description":"Identifies the `Labelable` associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Labelable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnlinkRepositoryFromProjectInput","description":"Autogenerated input type of UnlinkRepositoryFromProject","fields":null,"inputFields":[{"name":"projectId","description":"The ID of the Project linked to the Repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"The ID of the Repository linked to the Project.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnlinkRepositoryFromProjectPayload","description":"Autogenerated return type of UnlinkRepositoryFromProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The linked Project.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The linked Repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnlockLockableInput","description":"Autogenerated input type of UnlockLockable","fields":null,"inputFields":[{"name":"lockableId","description":"ID of the item to be unlocked.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnlockLockablePayload","description":"Autogenerated return type of UnlockLockable","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unlockedRecord","description":"The item that was unlocked.","args":[],"type":{"kind":"INTERFACE","name":"Lockable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnlockedEvent","description":"Represents an 'unlocked' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"lockable","description":"Object that was unlocked.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Lockable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnmarkFileAsViewedInput","description":"Autogenerated input type of UnmarkFileAsViewed","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Node ID of the pull request.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"path","description":"The path of the file to mark as unviewed","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnmarkFileAsViewedPayload","description":"Autogenerated return type of UnmarkFileAsViewed","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The updated pull request.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnmarkIssueAsDuplicateInput","description":"Autogenerated input type of UnmarkIssueAsDuplicate","fields":null,"inputFields":[{"name":"duplicateId","description":"ID of the issue or pull request currently marked as a duplicate.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"canonicalId","description":"ID of the issue or pull request currently considered canonical/authoritative/original.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnmarkIssueAsDuplicatePayload","description":"Autogenerated return type of UnmarkIssueAsDuplicate","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"duplicate","description":"The issue or pull request that was marked as a duplicate.","args":[],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnmarkedAsDuplicateEvent","description":"Represents an 'unmarked_as_duplicate' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"canonical","description":"The authoritative issue or pull request which has been duplicated by another.","args":[],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"duplicate","description":"The issue or pull request which has been marked as a duplicate of another.","args":[],"type":{"kind":"UNION","name":"IssueOrPullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCrossRepository","description":"Canonical and duplicate belong to different repositories.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnminimizeCommentInput","description":"Autogenerated input type of UnminimizeComment","fields":null,"inputFields":[{"name":"subjectId","description":"The Node ID of the subject to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnminimizeCommentPayload","description":"Autogenerated return type of UnminimizeComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"unminimizedComment","description":"The comment that was unminimized.","args":[],"type":{"kind":"INTERFACE","name":"Minimizable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnpinIssueInput","description":"Autogenerated input type of UnpinIssue","fields":null,"inputFields":[{"name":"issueId","description":"The ID of the issue to be unpinned","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnpinIssuePayload","description":"Autogenerated return type of UnpinIssue","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue that was unpinned","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnpinnedEvent","description":"Represents an 'unpinned' event on a given issue or pull request.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"Identifies the issue associated with the event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Issue","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UnresolveReviewThreadInput","description":"Autogenerated input type of UnresolveReviewThread","fields":null,"inputFields":[{"name":"threadId","description":"The ID of the thread to unresolve","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnresolveReviewThreadPayload","description":"Autogenerated return type of UnresolveReviewThread","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"thread","description":"The thread to resolve.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewThread","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UnsubscribedEvent","description":"Represents an 'unsubscribed' event on a given `Subscribable`.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subscribable","description":"Object referenced by event.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INTERFACE","name":"Subscribable","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"INTERFACE","name":"Updatable","description":"Entities that can be updated.","fields":[{"name":"viewerCanUpdate","description":"Check if the current viewer can update this object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"Project","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}]},{"kind":"INTERFACE","name":"UpdatableComment","description":"Comments that can be updated.","fields":[{"name":"viewerCannotUpdateReasons","description":"Reasons why the current viewer can not update this comment.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"CommentCannotUpdateReason","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"CommitComment","ofType":null},{"kind":"OBJECT","name":"GistComment","ofType":null},{"kind":"OBJECT","name":"Issue","ofType":null},{"kind":"OBJECT","name":"IssueComment","ofType":null},{"kind":"OBJECT","name":"PullRequest","ofType":null},{"kind":"OBJECT","name":"PullRequestReview","ofType":null},{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null}]},{"kind":"INPUT_OBJECT","name":"UpdateBranchProtectionRuleInput","description":"Autogenerated input type of UpdateBranchProtectionRule","fields":null,"inputFields":[{"name":"branchProtectionRuleId","description":"The global relay id of the branch protection rule to be updated.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"pattern","description":"The glob-like pattern used to determine matching branches.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"requiresApprovingReviews","description":"Are approving reviews required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiredApprovingReviewCount","description":"Number of approving reviews required to update matching branches.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"requiresCommitSignatures","description":"Are commits required to be signed.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresLinearHistory","description":"Are merge commits prohibited from being pushed to this branch.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"allowsForcePushes","description":"Are force pushes allowed on this branch.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"allowsDeletions","description":"Can this branch be deleted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"isAdminEnforced","description":"Can admins overwrite branch protection.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresStatusChecks","description":"Are status checks required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresStrictStatusChecks","description":"Are branches required to be up to date before merging.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"requiresCodeOwnerReviews","description":"Are reviews from code owners required to update matching branches.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"dismissesStaleReviews","description":"Will new commits pushed to matching branches dismiss pull request review approvals.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"restrictsReviewDismissals","description":"Is dismissal of pull request reviews restricted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"reviewDismissalActorIds","description":"A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"restrictsPushes","description":"Is pushing to matching branches restricted.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"pushActorIds","description":"A list of User, Team or App IDs allowed to push to matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"requiredStatusCheckContexts","description":"List of required status check contexts that must pass for commits to be accepted to matching branches.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateBranchProtectionRulePayload","description":"Autogenerated return type of UpdateBranchProtectionRule","fields":[{"name":"branchProtectionRule","description":"The newly created BranchProtectionRule.","args":[],"type":{"kind":"OBJECT","name":"BranchProtectionRule","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateCheckRunInput","description":"Autogenerated input type of UpdateCheckRun","fields":null,"inputFields":[{"name":"repositoryId","description":"The node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"checkRunId","description":"The node of the check.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the check.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"detailsUrl","description":"The URL of the integrator's site that has the full details of the check.","type":{"kind":"SCALAR","name":"URI","ofType":null},"defaultValue":null},{"name":"externalId","description":"A reference for the run on the integrator's system.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"status","description":"The current status.","type":{"kind":"ENUM","name":"RequestableCheckStatusState","ofType":null},"defaultValue":null},{"name":"startedAt","description":"The time that the check run began.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"conclusion","description":"The final conclusion of the check.","type":{"kind":"ENUM","name":"CheckConclusionState","ofType":null},"defaultValue":null},{"name":"completedAt","description":"The time that the check run finished.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"output","description":"Descriptive details about the run.","type":{"kind":"INPUT_OBJECT","name":"CheckRunOutput","ofType":null},"defaultValue":null},{"name":"actions","description":"Possible further actions the integrator can perform, which a user may trigger.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckRunAction","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateCheckRunPayload","description":"Autogenerated return type of UpdateCheckRun","fields":[{"name":"checkRun","description":"The updated check run.","args":[],"type":{"kind":"OBJECT","name":"CheckRun","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateCheckSuitePreferencesInput","description":"Autogenerated input type of UpdateCheckSuitePreferences","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"autoTriggerPreferences","description":"The check suite preferences to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"CheckSuiteAutoTriggerPreference","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateCheckSuitePreferencesPayload","description":"Autogenerated return type of UpdateCheckSuitePreferences","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The updated repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseAdministratorRoleInput","description":"Autogenerated input type of UpdateEnterpriseAdministratorRole","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the Enterprise which the admin belongs to.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"login","description":"The login of a administrator whose role is being changed.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"role","description":"The new role for the Enterprise administrator.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseAdministratorRole","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseAdministratorRolePayload","description":"Autogenerated return type of UpdateEnterpriseAdministratorRole","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of changing the administrator's role.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput","description":"Autogenerated input type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the allow private repository forking setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the allow private repository forking setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseAllowPrivateRepositoryForkingSettingPayload","description":"Autogenerated return type of UpdateEnterpriseAllowPrivateRepositoryForkingSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated allow private repository forking setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the allow private repository forking setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseDefaultRepositoryPermissionSettingInput","description":"Autogenerated input type of UpdateEnterpriseDefaultRepositoryPermissionSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the default repository permission setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the default repository permission setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseDefaultRepositoryPermissionSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseDefaultRepositoryPermissionSettingPayload","description":"Autogenerated return type of UpdateEnterpriseDefaultRepositoryPermissionSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated default repository permission setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the default repository permission setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can change repository visibility setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can change repository visibility setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can change repository visibility setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can change repository visibility setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanCreateRepositoriesSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanCreateRepositoriesSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can create repositories setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"Value for the members can create repositories setting on the enterprise. This or the granular public/private/internal allowed fields (but not both) must be provided.","type":{"kind":"ENUM","name":"EnterpriseMembersCanCreateRepositoriesSettingValue","ofType":null},"defaultValue":null},{"name":"membersCanCreateRepositoriesPolicyEnabled","description":"When false, allow member organizations to set their own repository creation member privileges.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"membersCanCreatePublicRepositories","description":"Allow members to create public repositories. Defaults to current value.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"membersCanCreatePrivateRepositories","description":"Allow members to create private repositories. Defaults to current value.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"membersCanCreateInternalRepositories","description":"Allow members to create internal repositories. Defaults to current value.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanCreateRepositoriesSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanCreateRepositoriesSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can create repositories setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can create repositories setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanDeleteIssuesSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanDeleteIssuesSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can delete issues setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can delete issues setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanDeleteIssuesSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanDeleteIssuesSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can delete issues setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can delete issues setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanDeleteRepositoriesSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can delete repositories setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can delete repositories setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanDeleteRepositoriesSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanDeleteRepositoriesSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can delete repositories setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can delete repositories setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanInviteCollaboratorsSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can invite collaborators setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can invite collaborators setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanInviteCollaboratorsSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanInviteCollaboratorsSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can invite collaborators setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can invite collaborators setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanMakePurchasesSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanMakePurchasesSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can make purchases setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can make purchases setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseMembersCanMakePurchasesSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanMakePurchasesSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanMakePurchasesSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can make purchases setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can make purchases setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can update protected branches setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can update protected branches setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanUpdateProtectedBranchesSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanUpdateProtectedBranchesSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can update protected branches setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can update protected branches setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseMembersCanViewDependencyInsightsSettingInput","description":"Autogenerated input type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the members can view dependency insights setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the members can view dependency insights setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseMembersCanViewDependencyInsightsSettingPayload","description":"Autogenerated return type of UpdateEnterpriseMembersCanViewDependencyInsightsSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated members can view dependency insights setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the members can view dependency insights setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseOrganizationProjectsSettingInput","description":"Autogenerated input type of UpdateEnterpriseOrganizationProjectsSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the organization projects setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the organization projects setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseOrganizationProjectsSettingPayload","description":"Autogenerated return type of UpdateEnterpriseOrganizationProjectsSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated organization projects setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the organization projects setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseProfileInput","description":"Autogenerated input type of UpdateEnterpriseProfile","fields":null,"inputFields":[{"name":"enterpriseId","description":"The Enterprise ID to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of the enterprise.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"description","description":"The description of the enterprise.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"websiteUrl","description":"The URL of the enterprise's website.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"location","description":"The location of the enterprise.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseProfilePayload","description":"Autogenerated return type of UpdateEnterpriseProfile","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The updated enterprise.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseRepositoryProjectsSettingInput","description":"Autogenerated input type of UpdateEnterpriseRepositoryProjectsSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the repository projects setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the repository projects setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseRepositoryProjectsSettingPayload","description":"Autogenerated return type of UpdateEnterpriseRepositoryProjectsSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated repository projects setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the repository projects setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseTeamDiscussionsSettingInput","description":"Autogenerated input type of UpdateEnterpriseTeamDiscussionsSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the team discussions setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the team discussions setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledDisabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseTeamDiscussionsSettingPayload","description":"Autogenerated return type of UpdateEnterpriseTeamDiscussionsSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated team discussions setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the team discussions setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingInput","description":"Autogenerated input type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting","fields":null,"inputFields":[{"name":"enterpriseId","description":"The ID of the enterprise on which to set the two factor authentication required setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the two factor authentication required setting on the enterprise.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"EnterpriseEnabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateEnterpriseTwoFactorAuthenticationRequiredSettingPayload","description":"Autogenerated return type of UpdateEnterpriseTwoFactorAuthenticationRequiredSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enterprise","description":"The enterprise with the updated two factor authentication required setting.","args":[],"type":{"kind":"OBJECT","name":"Enterprise","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A message confirming the result of updating the two factor authentication required setting.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateIpAllowListEnabledSettingInput","description":"Autogenerated input type of UpdateIpAllowListEnabledSetting","fields":null,"inputFields":[{"name":"ownerId","description":"The ID of the owner on which to set the IP allow list enabled setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the IP allow list enabled setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IpAllowListEnabledSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateIpAllowListEnabledSettingPayload","description":"Autogenerated return type of UpdateIpAllowListEnabledSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The IP allow list owner on which the setting was updated.","args":[],"type":{"kind":"UNION","name":"IpAllowListOwner","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateIpAllowListEntryInput","description":"Autogenerated input type of UpdateIpAllowListEntry","fields":null,"inputFields":[{"name":"ipAllowListEntryId","description":"The ID of the IP allow list entry to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"allowListValue","description":"An IP address or range of addresses in CIDR notation.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"name","description":"An optional name for the IP allow list entry.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"isActive","description":"Whether the IP allow list entry is active when an IP allow list is enabled.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateIpAllowListEntryPayload","description":"Autogenerated return type of UpdateIpAllowListEntry","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ipAllowListEntry","description":"The IP allow list entry that was updated.","args":[],"type":{"kind":"OBJECT","name":"IpAllowListEntry","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateIssueCommentInput","description":"Autogenerated input type of UpdateIssueComment","fields":null,"inputFields":[{"name":"id","description":"The ID of the IssueComment to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The updated text of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateIssueCommentPayload","description":"Autogenerated return type of UpdateIssueComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issueComment","description":"The updated comment.","args":[],"type":{"kind":"OBJECT","name":"IssueComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateIssueInput","description":"Autogenerated input type of UpdateIssue","fields":null,"inputFields":[{"name":"id","description":"The ID of the Issue to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"title","description":"The title for the issue.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"body","description":"The body for the issue description.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"assigneeIds","description":"An array of Node IDs of users for this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"milestoneId","description":"The Node ID of the milestone for this issue.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"labelIds","description":"An array of Node IDs of labels for this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"state","description":"The desired issue state.","type":{"kind":"ENUM","name":"IssueState","ofType":null},"defaultValue":null},{"name":"projectIds","description":"An array of Node IDs for projects associated with this issue.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateIssuePayload","description":"Autogenerated return type of UpdateIssue","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"issue","description":"The issue.","args":[],"type":{"kind":"OBJECT","name":"Issue","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateNotificationRestrictionSettingInput","description":"Autogenerated input type of UpdateNotificationRestrictionSetting","fields":null,"inputFields":[{"name":"ownerId","description":"The ID of the owner on which to set the restrict notifications setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"settingValue","description":"The value for the restrict notifications setting.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"NotificationRestrictionSettingValue","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateNotificationRestrictionSettingPayload","description":"Autogenerated return type of UpdateNotificationRestrictionSetting","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The owner on which the setting was updated.","args":[],"type":{"kind":"UNION","name":"VerifiableDomainOwner","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateProjectCardInput","description":"Autogenerated input type of UpdateProjectCard","fields":null,"inputFields":[{"name":"projectCardId","description":"The ProjectCard ID to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"isArchived","description":"Whether or not the ProjectCard should be archived","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"note","description":"The note of ProjectCard.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateProjectCardPayload","description":"Autogenerated return type of UpdateProjectCard","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projectCard","description":"The updated ProjectCard.","args":[],"type":{"kind":"OBJECT","name":"ProjectCard","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateProjectColumnInput","description":"Autogenerated input type of UpdateProjectColumn","fields":null,"inputFields":[{"name":"projectColumnId","description":"The ProjectColumn ID to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of project column.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateProjectColumnPayload","description":"Autogenerated return type of UpdateProjectColumn","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projectColumn","description":"The updated project column.","args":[],"type":{"kind":"OBJECT","name":"ProjectColumn","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateProjectInput","description":"Autogenerated input type of UpdateProject","fields":null,"inputFields":[{"name":"projectId","description":"The Project ID to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The name of project.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"body","description":"The description of project.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"state","description":"Whether the project is open or closed.","type":{"kind":"ENUM","name":"ProjectState","ofType":null},"defaultValue":null},{"name":"public","description":"Whether the project is public or not.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateProjectPayload","description":"Autogenerated return type of UpdateProject","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"The updated project.","args":[],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdatePullRequestInput","description":"Autogenerated input type of UpdatePullRequest","fields":null,"inputFields":[{"name":"pullRequestId","description":"The Node ID of the pull request.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"baseRefName","description":"The name of the branch you want your changes pulled into. This should be an existing branch\non the current repository.\n","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"title","description":"The title of the pull request.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"body","description":"The contents of the pull request.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"state","description":"The target state of the pull request.","type":{"kind":"ENUM","name":"PullRequestUpdateState","ofType":null},"defaultValue":null},{"name":"maintainerCanModify","description":"Indicates whether maintainers can modify the pull request.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"assigneeIds","description":"An array of Node IDs of users for this pull request.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"milestoneId","description":"The Node ID of the milestone for this pull request.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"labelIds","description":"An array of Node IDs of labels for this pull request.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"projectIds","description":"An array of Node IDs for projects associated with this pull request.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdatePullRequestPayload","description":"Autogenerated return type of UpdatePullRequest","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequest","description":"The updated pull request.","args":[],"type":{"kind":"OBJECT","name":"PullRequest","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdatePullRequestReviewCommentInput","description":"Autogenerated input type of UpdatePullRequestReviewComment","fields":null,"inputFields":[{"name":"pullRequestReviewCommentId","description":"The Node ID of the comment to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The text of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdatePullRequestReviewCommentPayload","description":"Autogenerated return type of UpdatePullRequestReviewComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReviewComment","description":"The updated comment.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReviewComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdatePullRequestReviewInput","description":"Autogenerated input type of UpdatePullRequestReview","fields":null,"inputFields":[{"name":"pullRequestReviewId","description":"The Node ID of the pull request review to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The contents of the pull request review body.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdatePullRequestReviewPayload","description":"Autogenerated return type of UpdatePullRequestReview","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequestReview","description":"The updated pull request review.","args":[],"type":{"kind":"OBJECT","name":"PullRequestReview","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateRefInput","description":"Autogenerated input type of UpdateRef","fields":null,"inputFields":[{"name":"refId","description":"The Node ID of the Ref to be updated.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"oid","description":"The GitObjectID that the Ref shall be updated to target.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"GitObjectID","ofType":null}},"defaultValue":null},{"name":"force","description":"Permit updates of branch Refs that are not fast-forwards?","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateRefPayload","description":"Autogenerated return type of UpdateRef","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ref","description":"The updated Ref.","args":[],"type":{"kind":"OBJECT","name":"Ref","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateRepositoryInput","description":"Autogenerated input type of UpdateRepository","fields":null,"inputFields":[{"name":"repositoryId","description":"The ID of the repository to update.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"name","description":"The new name of the repository.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"description","description":"A new description for the repository. Pass an empty string to erase the existing description.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"template","description":"Whether this repository should be marked as a template such that anyone who can access it can create new repositories with the same files and directory structure.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"homepageUrl","description":"The URL for a web page about this repository. Pass an empty string to erase the existing URL.","type":{"kind":"SCALAR","name":"URI","ofType":null},"defaultValue":null},{"name":"hasWikiEnabled","description":"Indicates if the repository should have the wiki feature enabled.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"hasIssuesEnabled","description":"Indicates if the repository should have the issues feature enabled.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"hasProjectsEnabled","description":"Indicates if the repository should have the project boards feature enabled.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateRepositoryPayload","description":"Autogenerated return type of UpdateRepository","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The updated repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateSubscriptionInput","description":"Autogenerated input type of UpdateSubscription","fields":null,"inputFields":[{"name":"subscribableId","description":"The Node ID of the subscribable object to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"state","description":"The new state of the subscription.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"SubscriptionState","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateSubscriptionPayload","description":"Autogenerated return type of UpdateSubscription","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"subscribable","description":"The input subscribable entity.","args":[],"type":{"kind":"INTERFACE","name":"Subscribable","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateTeamDiscussionCommentInput","description":"Autogenerated input type of UpdateTeamDiscussionComment","fields":null,"inputFields":[{"name":"id","description":"The ID of the comment to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"body","description":"The updated text of the comment.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"bodyVersion","description":"The current version of the body content.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateTeamDiscussionCommentPayload","description":"Autogenerated return type of UpdateTeamDiscussionComment","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussionComment","description":"The updated comment.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussionComment","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateTeamDiscussionInput","description":"Autogenerated input type of UpdateTeamDiscussion","fields":null,"inputFields":[{"name":"id","description":"The Node ID of the discussion to modify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"title","description":"The updated title of the discussion.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"body","description":"The updated text of the discussion.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"bodyVersion","description":"The current version of the body content. If provided, this update operation will be rejected if the given version does not match the latest version on the server.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"pinned","description":"If provided, sets the pinned state of the updated discussion.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateTeamDiscussionPayload","description":"Autogenerated return type of UpdateTeamDiscussion","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"teamDiscussion","description":"The updated discussion.","args":[],"type":{"kind":"OBJECT","name":"TeamDiscussion","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UpdateTopicsInput","description":"Autogenerated input type of UpdateTopics","fields":null,"inputFields":[{"name":"repositoryId","description":"The Node ID of the repository.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"topicNames","description":"An array of topic names.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UpdateTopicsPayload","description":"Autogenerated return type of UpdateTopics","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"invalidTopicNames","description":"Names of the provided topics that are not valid.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"The updated repository.","args":[],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"User","description":"A user is an individual's account on GitHub that owns repositories and can make new content.","fields":[{"name":"anyPinnableItems","description":"Determine if this repository owner has any items that can be pinned to their profile.","args":[{"name":"type","description":"Filter to only a particular kind of pinnable item.","type":{"kind":"ENUM","name":"PinnableItemType","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"avatarUrl","description":"A URL pointing to the user's public avatar.","args":[{"name":"size","description":"The size of the resulting square image.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"bio","description":"The user's public profile bio.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"bioHTML","description":"The user's public profile bio as HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"commitComments","description":"A list of commit comments made by this user.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"CommitCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"company","description":"The user's public profile company.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"companyHTML","description":"The user's public profile company as HTML.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"HTML","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"contributionsCollection","description":"The collection of contributions this user has made to different repositories.","args":[{"name":"organizationID","description":"The ID of the organization used to filter contributions.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"from","description":"Only contributions made at this time or later will be counted. If omitted, defaults to a year ago.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null},{"name":"to","description":"Only contributions made before and up to and including this time will be counted. If omitted, defaults to the current time.","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ContributionsCollection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"email","description":"The user's publicly visible profile email.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"followers","description":"A list of users the given user is followed by.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"FollowerConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"following","description":"A list of users the given user is following.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"FollowingConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"gist","description":"Find gist by repo name.","args":[{"name":"name","description":"The gist name to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Gist","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"gistComments","description":"A list of gist comments made by this user.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GistCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"gists","description":"A list of the Gists the user has created.","args":[{"name":"privacy","description":"Filters Gists according to privacy.","type":{"kind":"ENUM","name":"GistPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for gists returned from the connection","type":{"kind":"INPUT_OBJECT","name":"GistOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"GistConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasSponsorsListing","description":"True if this user/organization has a GitHub Sponsors listing.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hovercard","description":"The hovercard information for this user in a given context","args":[{"name":"primarySubjectId","description":"The ID of the subject to get the hovercard in the context of","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"Hovercard","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"interactionAbility","description":"The interaction ability settings for this user.","args":[],"type":{"kind":"OBJECT","name":"RepositoryInteractionAbility","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isBountyHunter","description":"Whether or not this user is a participant in the GitHub Security Bug Bounty.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isCampusExpert","description":"Whether or not this user is a participant in the GitHub Campus Experts Program.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isDeveloperProgramMember","description":"Whether or not this user is a GitHub Developer Program member.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isEmployee","description":"Whether or not this user is a GitHub employee.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isGitHubStar","description":"Whether or not this user is a member of the GitHub Stars Program.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isHireable","description":"Whether or not the user has marked themselves as for hire.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSiteAdmin","description":"Whether or not this user is a site administrator.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoredBy","description":"Check if the given account is sponsoring this user/organization.","args":[{"name":"accountLogin","description":"The target account's login.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isSponsoringViewer","description":"True if the viewer is sponsored by this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isViewer","description":"Whether or not this user is the viewing user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issueComments","description":"A list of issue comments made by this user.","args":[{"name":"orderBy","description":"Ordering options for issue comments returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueCommentOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueCommentConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"issues","description":"A list of issues associated with this user.","args":[{"name":"orderBy","description":"Ordering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"states","description":"A list of states to filter the issues by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"IssueState","ofType":null}}},"defaultValue":null},{"name":"filterBy","description":"Filtering options for issues returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueFilters","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"IssueConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"itemShowcase","description":"Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProfileItemShowcase","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"location","description":"The user's public profile location.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"login","description":"The username used to login.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":"The user's public profile name.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"Find an organization by its login that the user belongs to.","args":[{"name":"login","description":"The login of the organization to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organizationVerifiedDomainEmails","description":"Verified email addresses that match verified domains for a specified organization the user is a member of.","args":[{"name":"login","description":"The login of the organization to match verified domains from.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"organizations","description":"A list of organizations the user belongs to.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"OrganizationConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"packages","description":"A list of packages under the owner.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"names","description":"Find packages by their names.","type":{"kind":"LIST","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null},{"name":"repositoryId","description":"Find packages in a repository by ID.","type":{"kind":"SCALAR","name":"ID","ofType":null},"defaultValue":null},{"name":"packageType","description":"Filter registry package by type.","type":{"kind":"ENUM","name":"PackageType","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering of the returned packages.","type":{"kind":"INPUT_OBJECT","name":"PackageOrder","ofType":null},"defaultValue":"{field: CREATED_AT, direction: DESC}"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PackageConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnableItems","description":"A list of repositories and gists this profile owner can pin to their profile.","args":[{"name":"types","description":"Filter the types of pinnable items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItems","description":"A list of repositories and gists this profile owner has pinned to their profile","args":[{"name":"types","description":"Filter the types of pinned items that are returned.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PinnableItemType","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PinnableItemConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pinnedItemsRemaining","description":"Returns how many more items this profile owner can pin to their profile.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"project","description":"Find project by number.","args":[{"name":"number","description":"The project number to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Project","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"projects","description":"A list of projects under the owner.","args":[{"name":"orderBy","description":"Ordering options for projects returned from the connection","type":{"kind":"INPUT_OBJECT","name":"ProjectOrder","ofType":null},"defaultValue":null},{"name":"search","description":"Query to search projects by, currently only searching by name.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"states","description":"A list of states to filter the projects by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"ProjectState","ofType":null}}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"ProjectConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsResourcePath","description":"The HTTP path listing user's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"projectsUrl","description":"The HTTP URL listing user's projects","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"publicKeys","description":"A list of public keys associated with this user.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PublicKeyConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pullRequests","description":"A list of pull requests associated with this user.","args":[{"name":"states","description":"A list of states to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"PullRequestState","ofType":null}}},"defaultValue":null},{"name":"labels","description":"A list of label names to filter the pull requests by.","type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}}},"defaultValue":null},{"name":"headRefName","description":"The head ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"baseRefName","description":"The base ref name to filter the pull requests by.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for pull requests returned from the connection.","type":{"kind":"INPUT_OBJECT","name":"IssueOrder","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PullRequestConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositories","description":"A list of repositories that the user owns.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"affiliations","description":"Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":null},{"name":"ownerAffiliations","description":"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":"[OWNER, COLLABORATOR]"},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"isFork","description":"If non-null, filters repositories according to whether they are forks of another repository","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repositoriesContributedTo","description":"A list of repositories that the user recently contributed to.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"includeUserRepositories","description":"If true, include user repositories","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"contributionTypes","description":"If non-null, include only the specified types of contributions. The GitHub.com UI uses [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryContributionType","ofType":null}},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"repository","description":"Find Repository.","args":[{"name":"name","description":"Name of Repository to find.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"defaultValue":null}],"type":{"kind":"OBJECT","name":"Repository","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"resourcePath","description":"The HTTP path for this user","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"savedReplies","description":"Replies this user has saved","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"The field to order saved replies by.","type":{"kind":"INPUT_OBJECT","name":"SavedReplyOrder","ofType":null},"defaultValue":"{field: UPDATED_AT, direction: DESC}"}],"type":{"kind":"OBJECT","name":"SavedReplyConnection","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorsListing","description":"The GitHub Sponsors listing for this user or organization.","args":[],"type":{"kind":"OBJECT","name":"SponsorsListing","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipForViewerAsSponsor","description":"The viewer's sponsorship of this entity.","args":[],"type":{"kind":"OBJECT","name":"Sponsorship","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsMaintainer","description":"This object's sponsorships as the maintainer.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"includePrivate","description":"Whether or not to include private sponsorships in the result set","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"sponsorshipsAsSponsor","description":"This object's sponsorships as the sponsor.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for sponsorships returned from this connection. If left blank, the sponsorships will be ordered based on relevancy to the viewer.","type":{"kind":"INPUT_OBJECT","name":"SponsorshipOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"SponsorshipConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"starredRepositories","description":"Repositories the user has starred.","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"ownedByViewer","description":"Filters starred repositories to only return repositories owned by the viewer.","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Order for connection","type":{"kind":"INPUT_OBJECT","name":"StarOrder","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"StarredRepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"status","description":"The user's description of what they're currently doing.","args":[],"type":{"kind":"OBJECT","name":"UserStatus","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"topRepositories","description":"Repositories the user has contributed to, ordered by contribution rank, plus repositories the user has created\n","args":[{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null}},"defaultValue":null},{"name":"since","description":"How far back in time to fetch contributed repositories","type":{"kind":"SCALAR","name":"DateTime","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"twitterUsername","description":"The user's Twitter username.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"url","description":"The HTTP URL for this user","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanChangePinnedItems","description":"Can the viewer pin repositories and gists to the profile?","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanCreateProjects","description":"Can the current viewer create new projects on this owner.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanFollow","description":"Whether or not the viewer is able to follow the user.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerCanSponsor","description":"Whether or not the viewer is able to sponsor this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsFollowing","description":"Whether or not this user is followed by the viewer.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewerIsSponsoring","description":"True if the viewer is sponsoring this user/organization.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"watching","description":"A list of repositories the given user is watching.","args":[{"name":"privacy","description":"If non-null, filters repositories according to privacy","type":{"kind":"ENUM","name":"RepositoryPrivacy","ofType":null},"defaultValue":null},{"name":"orderBy","description":"Ordering options for repositories returned from the connection","type":{"kind":"INPUT_OBJECT","name":"RepositoryOrder","ofType":null},"defaultValue":null},{"name":"affiliations","description":"Affiliation options for repositories returned from the connection. If none specified, the results will include repositories for which the current viewer is an owner or collaborator, or member.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":null},{"name":"ownerAffiliations","description":"Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.","type":{"kind":"LIST","name":null,"ofType":{"kind":"ENUM","name":"RepositoryAffiliation","ofType":null}},"defaultValue":"[OWNER, COLLABORATOR]"},{"name":"isLocked","description":"If non-null, filters repositories according to whether they have been locked","type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":null},{"name":"after","description":"Returns the elements in the list that come after the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"before","description":"Returns the elements in the list that come before the specified cursor.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null},{"name":"first","description":"Returns the first _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null},{"name":"last","description":"Returns the last _n_ elements from the list.","type":{"kind":"SCALAR","name":"Int","ofType":null},"defaultValue":null}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"RepositoryConnection","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"websiteUrl","description":"A URL pointing to the user's public website/blog.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null},{"kind":"INTERFACE","name":"Actor","ofType":null},{"kind":"INTERFACE","name":"PackageOwner","ofType":null},{"kind":"INTERFACE","name":"ProjectOwner","ofType":null},{"kind":"INTERFACE","name":"RepositoryOwner","ofType":null},{"kind":"INTERFACE","name":"UniformResourceLocatable","ofType":null},{"kind":"INTERFACE","name":"ProfileOwner","ofType":null},{"kind":"INTERFACE","name":"Sponsorable","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"UserBlockDuration","description":"The possible durations that a user can be blocked for.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"ONE_DAY","description":"The user was blocked for 1 day","isDeprecated":false,"deprecationReason":null},{"name":"THREE_DAYS","description":"The user was blocked for 3 days","isDeprecated":false,"deprecationReason":null},{"name":"ONE_WEEK","description":"The user was blocked for 7 days","isDeprecated":false,"deprecationReason":null},{"name":"ONE_MONTH","description":"The user was blocked for 30 days","isDeprecated":false,"deprecationReason":null},{"name":"PERMANENT","description":"The user was blocked permanently","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"UserBlockedEvent","description":"Represents a 'user_blocked' event on a given user.","fields":[{"name":"actor","description":"Identifies the actor who performed the event.","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"blockDuration","description":"Number of days that the user was blocked for.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"UserBlockDuration","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subject","description":"The user who was blocked.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserConnection","description":"The connection type for User.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserContentEdit","description":"An edit on user content","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"deletedAt","description":"Identifies the date and time when the object was deleted.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deletedBy","description":"The actor who deleted this content","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"diff","description":"A summary of the changes for this edit","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"editedAt","description":"When this content was edited","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"editor","description":"The actor who edited this content","args":[],"type":{"kind":"INTERFACE","name":"Actor","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserContentEditConnection","description":"A list of edits to content.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserContentEditEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserContentEdit","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserContentEditEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"UserContentEdit","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserEdge","description":"Represents a user.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"User","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserEmailMetadata","description":"Email attributes from External Identity","fields":[{"name":"primary","description":"Boolean to identify primary emails","args":[],"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":"Type of email","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"value","description":"Email id","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserStatus","description":"The user's description of what they're currently doing.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"emoji","description":"An emoji summarizing the user's status.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"emojiHTML","description":"The status emoji as HTML.","args":[],"type":{"kind":"SCALAR","name":"HTML","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"expiresAt","description":"If set, the status will not be shown after this date.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":"ID of the object.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"indicatesLimitedAvailability","description":"Whether this status indicates the user is not fully available on GitHub.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"message","description":"A brief message describing what the user is doing.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"organization","description":"The organization whose members can see this status. If null, this status is publicly visible.","args":[],"type":{"kind":"OBJECT","name":"Organization","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"user","description":"The user who has this status.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserStatusConnection","description":"The connection type for UserStatus.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserStatusEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"UserStatus","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"UserStatusEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"UserStatus","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"UserStatusOrder","description":"Ordering options for user status connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order user statuses by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"UserStatusOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"UserStatusOrderField","description":"Properties by which user status connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"UPDATED_AT","description":"Order user statuses by when they were updated.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"VerifiableDomain","description":"A domain that can be verified for an organization or an enterprise.","fields":[{"name":"createdAt","description":"Identifies the date and time when the object was created.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"databaseId","description":"Identifies the primary key from the database.","args":[],"type":{"kind":"SCALAR","name":"Int","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"dnsHostName","description":"The DNS host name that should be used for verification.","args":[],"type":{"kind":"SCALAR","name":"URI","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"domain","description":"The unicode encoded domain.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasFoundHostName","description":"Whether a TXT record for verification with the expected host name was found.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"hasFoundVerificationToken","description":"Whether a TXT record for verification with the expected verification token was found.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"id","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isRequiredForPolicyEnforcement","description":"Whether this domain is required to exist for an organization or enterprise policy to be enforced.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"isVerified","description":"Whether or not the domain is verified.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"owner","description":"The owner of the domain.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"UNION","name":"VerifiableDomainOwner","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"punycodeEncodedDomain","description":"The punycode encoded domain.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"URI","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"tokenExpirationTime","description":"The time that the current verification token will expire.","args":[],"type":{"kind":"SCALAR","name":"DateTime","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"updatedAt","description":"Identifies the date and time when the object was last updated.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"DateTime","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"verificationToken","description":"The current verification token for the domain.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"Node","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"VerifiableDomainConnection","description":"The connection type for VerifiableDomain.","fields":[{"name":"edges","description":"A list of edges.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"VerifiableDomainEdge","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"nodes","description":"A list of nodes.","args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"OBJECT","name":"VerifiableDomain","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"pageInfo","description":"Information to aid in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"PageInfo","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"totalCount","description":"Identifies the total count of items in the connection.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Int","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"VerifiableDomainEdge","description":"An edge in a connection.","fields":[{"name":"cursor","description":"A cursor for use in pagination.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"node","description":"The item at the end of the edge.","args":[],"type":{"kind":"OBJECT","name":"VerifiableDomain","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"INPUT_OBJECT","name":"VerifiableDomainOrder","description":"Ordering options for verifiable domain connections.","fields":null,"inputFields":[{"name":"field","description":"The field to order verifiable domains by.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"VerifiableDomainOrderField","ofType":null}},"defaultValue":null},{"name":"direction","description":"The ordering direction.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"OrderDirection","ofType":null}},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"VerifiableDomainOrderField","description":"Properties by which verifiable domain connections can be ordered.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"DOMAIN","description":"Order verifiable domains by the domain name.","isDeprecated":false,"deprecationReason":null},{"name":"CREATED_AT","description":"Order verifiable domains by their creation date.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"UNION","name":"VerifiableDomainOwner","description":"Types that can own a verifiable domain.","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":[{"kind":"OBJECT","name":"Enterprise","ofType":null},{"kind":"OBJECT","name":"Organization","ofType":null}]},{"kind":"INPUT_OBJECT","name":"VerifyVerifiableDomainInput","description":"Autogenerated input type of VerifyVerifiableDomain","fields":null,"inputFields":[{"name":"id","description":"The ID of the verifiable domain to verify.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"ID","ofType":null}},"defaultValue":null},{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":null}],"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"VerifyVerifiableDomainPayload","description":"Autogenerated return type of VerifyVerifiableDomain","fields":[{"name":"clientMutationId","description":"A unique identifier for the client performing the mutation.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"domain","description":"The verifiable domain that was verified.","args":[],"type":{"kind":"OBJECT","name":"VerifiableDomain","ofType":null},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"ViewerHovercardContext","description":"A hovercard context with a message describing how the viewer is related.","fields":[{"name":"message","description":"A string describing this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"octicon","description":"An octicon to accompany this context","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"viewer","description":"Identifies the user who is related to this context.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"User","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[{"kind":"INTERFACE","name":"HovercardContext","ofType":null}],"enumValues":null,"possibleTypes":null},{"kind":"SCALAR","name":"X509Certificate","description":"A valid x509 certificate string","fields":null,"inputFields":null,"interfaces":null,"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Directive","description":"A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.","fields":[{"name":"args","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"locations","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__DirectiveLocation","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"onField","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onFragment","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."},{"name":"onOperation","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":true,"deprecationReason":"Use `locations`."}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__DirectiveLocation","description":"A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"QUERY","description":"Location adjacent to a query operation.","isDeprecated":false,"deprecationReason":null},{"name":"MUTATION","description":"Location adjacent to a mutation operation.","isDeprecated":false,"deprecationReason":null},{"name":"SUBSCRIPTION","description":"Location adjacent to a subscription operation.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD","description":"Location adjacent to a field.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_DEFINITION","description":"Location adjacent to a fragment definition.","isDeprecated":false,"deprecationReason":null},{"name":"FRAGMENT_SPREAD","description":"Location adjacent to a fragment spread.","isDeprecated":false,"deprecationReason":null},{"name":"INLINE_FRAGMENT","description":"Location adjacent to an inline fragment.","isDeprecated":false,"deprecationReason":null},{"name":"SCHEMA","description":"Location adjacent to a schema definition.","isDeprecated":false,"deprecationReason":null},{"name":"SCALAR","description":"Location adjacent to a scalar definition.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Location adjacent to an object type definition.","isDeprecated":false,"deprecationReason":null},{"name":"FIELD_DEFINITION","description":"Location adjacent to a field definition.","isDeprecated":false,"deprecationReason":null},{"name":"ARGUMENT_DEFINITION","description":"Location adjacent to an argument definition.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Location adjacent to an interface definition.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Location adjacent to a union definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Location adjacent to an enum definition.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM_VALUE","description":"Location adjacent to an enum value definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Location adjacent to an input object type definition.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_FIELD_DEFINITION","description":"Location adjacent to an input object field definition.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null},{"kind":"OBJECT","name":"__EnumValue","description":"One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.","fields":[{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Field","description":"Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.","fields":[{"name":"args","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__InputValue","description":"Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.","fields":[{"name":"defaultValue","description":"A GraphQL-formatted string representing the default value for this input value.","args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"deprecationReason","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"isDeprecated","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"type","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Schema","description":"A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.","fields":[{"name":"directives","description":"A list of all directives supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Directive","ofType":null}}}},"isDeprecated":false,"deprecationReason":null},{"name":"mutationType","description":"If this server supports mutation, the type that mutation operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"queryType","description":"The type that query operations will be rooted at.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"subscriptionType","description":"If this server support subscription, the type that subscription operations will be rooted at.","args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"types","description":"A list of all types supported by this server.","args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"OBJECT","name":"__Type","description":"The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.","fields":[{"name":"description","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"enumValues","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__EnumValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"fields","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Field","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"inputFields","description":null,"args":[{"name":"includeDeprecated","description":null,"type":{"kind":"SCALAR","name":"Boolean","ofType":null},"defaultValue":"false"}],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__InputValue","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"interfaces","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null},{"name":"kind","description":null,"args":[],"type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"ENUM","name":"__TypeKind","ofType":null}},"isDeprecated":false,"deprecationReason":null},{"name":"name","description":null,"args":[],"type":{"kind":"SCALAR","name":"String","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"ofType","description":null,"args":[],"type":{"kind":"OBJECT","name":"__Type","ofType":null},"isDeprecated":false,"deprecationReason":null},{"name":"possibleTypes","description":null,"args":[],"type":{"kind":"LIST","name":null,"ofType":{"kind":"NON_NULL","name":null,"ofType":{"kind":"OBJECT","name":"__Type","ofType":null}}},"isDeprecated":false,"deprecationReason":null}],"inputFields":null,"interfaces":[],"enumValues":null,"possibleTypes":null},{"kind":"ENUM","name":"__TypeKind","description":"An enum describing what kind of type a given `__Type` is.","fields":null,"inputFields":null,"interfaces":null,"enumValues":[{"name":"SCALAR","description":"Indicates this type is a scalar.","isDeprecated":false,"deprecationReason":null},{"name":"OBJECT","description":"Indicates this type is an object. `fields` and `interfaces` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"INTERFACE","description":"Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.","isDeprecated":false,"deprecationReason":null},{"name":"UNION","description":"Indicates this type is a union. `possibleTypes` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"ENUM","description":"Indicates this type is an enum. `enumValues` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"INPUT_OBJECT","description":"Indicates this type is an input object. `inputFields` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"LIST","description":"Indicates this type is a list. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null},{"name":"NON_NULL","description":"Indicates this type is a non-null. `ofType` is a valid field.","isDeprecated":false,"deprecationReason":null}],"possibleTypes":null}],"directives":[{"name":"include","description":"Directs the executor to include this field or fragment only when the `if` argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Included when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"skip","description":"Directs the executor to skip this field or fragment when the `if` argument is true.","locations":["FIELD","FRAGMENT_SPREAD","INLINE_FRAGMENT"],"args":[{"name":"if","description":"Skipped when true.","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"Boolean","ofType":null}},"defaultValue":null}]},{"name":"deprecated","description":"Marks an element of a GraphQL schema as no longer supported.","locations":["FIELD_DEFINITION","ENUM_VALUE","ARGUMENT_DEFINITION","INPUT_FIELD_DEFINITION"],"args":[{"name":"reason","description":"Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).","type":{"kind":"SCALAR","name":"String","ofType":null},"defaultValue":"\"No longer supported\""}]}]}}} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy new file mode 100644 index 00000000000..b468cfcff9b --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import io.spring.gradle.propdeps.PropDepsMavenPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.GroovyPlugin; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.MavenPlugin; +import org.gradle.api.plugins.PluginManager; +import org.gradle.internal.impldep.org.apache.maven.Maven; +import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin; +import org.gradle.plugins.ide.idea.IdeaPlugin; +import io.spring.gradle.propdeps.PropDepsEclipsePlugin; +import io.spring.gradle.propdeps.PropDepsIdeaPlugin; +import io.spring.gradle.propdeps.PropDepsPlugin; + +/** + * @author Rob Winch + */ +public abstract class AbstractSpringJavaPlugin implements Plugin<Project> { + + @Override + public final void apply(Project project) { + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaPlugin.class); + pluginManager.apply(ManagementConfigurationPlugin.class); + if (project.file("src/main/groovy").exists() + || project.file("src/test/groovy").exists() + || project.file("src/integration-test/groovy").exists()) { + pluginManager.apply(GroovyPlugin.class); + } + pluginManager.apply("io.spring.convention.repository"); + pluginManager.apply(EclipseWtpPlugin); + pluginManager.apply(IdeaPlugin); + pluginManager.apply(PropDepsPlugin); + pluginManager.apply(PropDepsEclipsePlugin); + pluginManager.apply(PropDepsIdeaPlugin); + project.getPlugins().withType(MavenPlugin) { + pluginManager.apply(PropDepsMavenPlugin); + } + pluginManager.apply("io.spring.convention.tests-configuration"); + pluginManager.apply("io.spring.convention.integration-test"); + pluginManager.apply("io.spring.convention.dependency-set"); + pluginManager.apply("io.spring.convention.javadoc-options"); + pluginManager.apply("io.spring.convention.checkstyle"); + + copyPropertyFromRootProjectTo("group", project); + copyPropertyFromRootProjectTo("version", project); + copyPropertyFromRootProjectTo("description", project); + + project.jar { + manifest.attributes["Created-By"] = + "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" + manifest.attributes["Implementation-Title"] = project.name + manifest.attributes["Implementation-Version"] = project.version + manifest.attributes["Automatic-Module-Name"] = project.name.replace('-', '.') + } + additionalPlugins(project); + } + + private void copyPropertyFromRootProjectTo(String propertyName, Project project) { + Project rootProject = project.getRootProject(); + Object property = rootProject.findProperty(propertyName); + if(property != null) { + project.setProperty(propertyName, property); + } + } + + protected abstract void additionalPlugins(Project project); +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy new file mode 100644 index 00000000000..d21e08bc5f7 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/ArtifactoryPlugin.groovy @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +class ArtifactoryPlugin implements Plugin<Project> { + + @Override + void apply(Project project) { + project.plugins.apply('com.jfrog.artifactory') + String name = Utils.getProjectName(project); + boolean isSnapshot = Utils.isSnapshot(project); + boolean isMilestone = Utils.isMilestone(project); + project.artifactory { + contextUrl = 'https://repo.spring.io' + publish { + repository { + repoKey = isSnapshot ? 'libs-snapshot-local' : isMilestone ? 'libs-milestone-local' : 'libs-release-local' + if(project.hasProperty('artifactoryUsername')) { + username = artifactoryUsername + password = artifactoryPassword + } + } + } + } + + project.artifactoryPublish { + publishIvy false + properties = [ + 'bintray.package': "${project.group}:${name}", + 'bintray.version': "${project.version}" + ] + } + + project.artifactory { + publish { + defaults { + publishConfigs('archives') + } + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy new file mode 100644 index 00000000000..f50a1b1f7bc --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds and configures Checkstyle plugin. + * + * @author Vedran Pavic + */ +class CheckstylePlugin implements Plugin<Project> { + + final CHECKSTYLE_DIR = 'etc/checkstyle' + + @Override + void apply(Project project) { + project.plugins.withType(JavaPlugin) { + def checkstyleDir = project.rootProject.file(CHECKSTYLE_DIR) + if (checkstyleDir.exists() && checkstyleDir.directory) { + project.getPluginManager().apply('checkstyle') + project.dependencies.add('checkstyle', 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15') + project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.5.RELEASE') + + project.checkstyle { + configDirectory = checkstyleDir + toolVersion = '8.21' + } + } + } + } + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy new file mode 100644 index 00000000000..b4670771e0d --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DependencySetPlugin.groovy @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds sets of dependencies to make it easy to add a grouping of dependencies. The + * dependencies added are: + * + * <ul> + * <li>sockDependencies</li> + * <li>seleniumDependencies</li> + * <li>gebDependencies</li> + * <li>powerMockDependencies</li> + * <li>slf4jDependencies</li> + * <li>jstlDependencies</li> + * <li>apachedsDependencies</li> + * </ul> + * + * @author Rob Winch + */ +public class DependencySetPlugin implements Plugin<Project> { + @Override + public void apply(Project project) { + + project.ext.spockDependencies = [ + project.dependencies.create("org.spockframework:spock-spring") { + exclude group: 'junit', module: 'junit-dep' + }, + project.dependencies.create("org.spockframework:spock-core") { + exclude group: 'junit', module: 'junit-dep' + } + ] + + project.ext.seleniumDependencies = [ + "org.seleniumhq.selenium:htmlunit-driver", + "org.seleniumhq.selenium:selenium-support" + ] + + project.ext.gebDependencies = project.spockDependencies + + project.seleniumDependencies + [ + "org.gebish:geb-spock", + 'commons-httpclient:commons-httpclient', + "org.codehaus.groovy:groovy", + "org.codehaus.groovy:groovy-all" + ] + + project.ext.powerMockDependencies = [ + "org.powermock:powermock-core", + "org.powermock:powermock-api-support", + "org.powermock:powermock-module-junit4-common", + "org.powermock:powermock-module-junit4", + project.dependencies.create("org.powermock:powermock-api-mockito") { + exclude group: 'org.mockito', module: 'mockito-all' + }, + "org.powermock:powermock-reflect" + ] + + project.ext.powerMock2Dependencies = [ + "org.powermock:powermock-core", + "org.powermock:powermock-api-support", + "org.powermock:powermock-module-junit4-common", + "org.powermock:powermock-module-junit4", + project.dependencies.create("org.powermock:powermock-api-mockito2") { + exclude group: 'org.mockito', module: 'mockito-all' + }, + "org.powermock:powermock-reflect" + ] + + project.ext.slf4jDependencies = [ + "org.slf4j:slf4j-api", + "org.slf4j:jcl-over-slf4j", + "org.slf4j:log4j-over-slf4j", + "ch.qos.logback:logback-classic" + ] + + project.ext.springCoreDependency = [ + project.dependencies.create("org.springframework:spring-core") { + exclude(group: 'commons-logging', module: 'commons-logging') + } + ] + + project.ext.testDependencies = [ + "junit:junit", + "org.mockito:mockito-core", + "org.springframework:spring-test", + "org.assertj:assertj-core" + ] + + project.ext.jstlDependencies = [ + "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api", + "org.apache.taglibs:taglibs-standard-jstlel" + ] + + project.ext.apachedsDependencies = [ + "org.apache.directory.server:apacheds-core", + "org.apache.directory.server:apacheds-core-entry", + "org.apache.directory.server:apacheds-protocol-shared", + "org.apache.directory.server:apacheds-protocol-ldap", + "org.apache.directory.server:apacheds-server-jndi", + 'org.apache.directory.shared:shared-ldap' + ] + + project.plugins.withType(JavaPlugin) { + project.dependencies { + testImplementation project.testDependencies + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy new file mode 100644 index 00000000000..bf1add50653 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class DeployDocsPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + project.getPluginManager().apply('org.hidetake.ssh') + + project.ssh.settings { + knownHosts = allowAnyHosts + } + project.remotes { + docs { + role 'docs' + if (project.hasProperty('deployDocsHost')) { + host = project.findProperty('deployDocsHost') + } else { + host = 'docs.af.pivotal.io' + } + retryCount = 5 // retry 5 times (default is 0) + retryWaitSec = 10 // wait 10 seconds between retries (default is 0) + user = project.findProperty('deployDocsSshUsername') + if (project.hasProperty('deployDocsSshKeyPath')) { + identity = project.file(project.findProperty('deployDocsSshKeyPath')) + } else if (project.hasProperty('deployDocsSshKey')) { + identity = project.findProperty('deployDocsSshKey') + } + if(project.hasProperty('deployDocsSshPassphrase')) { + passphrase = project.findProperty('deployDocsSshPassphrase') + } + } + } + + project.task('deployDocs') { + dependsOn 'docsZip' + doFirst { + project.ssh.run { + session(project.remotes.docs) { + def now = System.currentTimeMillis() + def name = project.rootProject.name + def version = project.rootProject.version + def tempPath = "/tmp/${name}-${now}-docs/".replaceAll(' ', '_') + execute "mkdir -p $tempPath" + + project.tasks.docsZip.outputs.each { o -> + put from: o.files, into: tempPath + } + + execute "unzip $tempPath*.zip -d $tempPath" + + def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/docs/${name}/${version}/" + + execute "rm -rf $extractPath" + execute "mkdir -p $extractPath" + execute "mv $tempPath/docs/* $extractPath" + execute "chmod -R g+w $extractPath" + } + } + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy new file mode 100644 index 00000000000..40c29998cbe --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy @@ -0,0 +1,73 @@ +package io.spring.gradle.convention + +import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask +import org.gradle.api.Action +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.DependencySet +import org.gradle.api.plugins.PluginManager +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.bundling.Zip + +/** + * Aggregates asciidoc, javadoc, and deploying of the docs into a single plugin + */ +public class DocsPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply("org.asciidoctor.jvm.convert"); + pluginManager.apply("org.asciidoctor.jvm.pdf"); + pluginManager.apply(AsciidoctorConventionPlugin); + pluginManager.apply(DeployDocsPlugin); + pluginManager.apply(JavadocApiPlugin); + + String projectName = Utils.getProjectName(project); + String pdfFilename = projectName + "-reference.pdf"; + + project.tasks.withType(AbstractAsciidoctorTask) { t -> + project.configure(t) { + sources { + include "**/*.adoc" + exclude '_*/**' + } + } + } + + + Task docsZip = project.tasks.create('docsZip', Zip) { + dependsOn 'api', 'asciidoctor' + group = 'Distribution' + archiveBaseName = project.rootProject.name + archiveClassifier = 'docs' + description = "Builds -${classifier} archive containing all " + + "Docs for deployment at docs.spring.io" + + from(project.tasks.asciidoctor.outputs) { + into 'reference/html5' + include '**' + } + from(project.tasks.asciidoctorPdf.outputs) { + into 'reference/pdf' + include '**' + rename "index.pdf", pdfFilename + } + from(project.tasks.api.outputs) { + into 'api' + } + into 'docs' + duplicatesStrategy 'exclude' + } + + Task docs = project.tasks.create("docs") { + group = 'Documentation' + description 'An aggregator task to generate all the documentation' + dependsOn docsZip + } + project.tasks.assemble.dependsOn docs + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy new file mode 100644 index 00000000000..720f1b05629 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy @@ -0,0 +1,121 @@ +/* + * Copyright 2016-2018 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.propdeps.PropDepsPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.GroovyPlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.testing.Test +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.idea.IdeaPlugin + +/** + * + * Adds support for integration tests to java projects. + * + * <ul> + * <li>Adds integrationTestCompile and integrationTestRuntime configurations</li> + * <li>A new source test folder of src/integration-test/java has been added</li> + * <li>A task to run integration tests named integrationTest is added</li> + * <li>If Groovy plugin is added a new source test folder src/integration-test/groovy is added</li> + * </ul> + * + * @author Rob Winch + */ +public class IntegrationTestPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + project.plugins.withType(JavaPlugin.class) { + applyJava(project) + } + } + + private applyJava(Project project) { + if(!project.file('src/integration-test/').exists()) { + // ensure we don't add if no tests to avoid adding Gretty + return + } + project.configurations { + integrationTestCompile { + extendsFrom testCompile, testImplementation + } + integrationTestRuntime { + extendsFrom integrationTestCompile, testRuntime, testRuntimeOnly + } + } + + project.sourceSets { + integrationTest { + java.srcDir project.file('src/integration-test/java') + resources.srcDir project.file('src/integration-test/resources') + compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompile + runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntime + } + } + + Task integrationTestTask = project.tasks.create("integrationTest", Test) { + group = 'Verification' + description = 'Runs the integration tests.' + dependsOn 'jar' + testClassesDirs = project.sourceSets.integrationTest.output.classesDirs + classpath = project.sourceSets.integrationTest.runtimeClasspath + shouldRunAfter project.tasks.test + } + project.tasks.check.dependsOn integrationTestTask + + project.plugins.withType(IdeaPlugin) { + project.idea { + module { + testSourceDirs += project.file('src/integration-test/java') + scopes.TEST.plus += [ project.configurations.integrationTestCompile ] + } + } + } + + project.plugins.withType(GroovyPlugin) { + project.sourceSets { + integrationTest { + groovy.srcDirs project.file('src/integration-test/groovy') + } + } + project.plugins.withType(IdeaPlugin) { + project.idea { + module { + testSourceDirs += project.file('src/integration-test/groovy') + } + } + } + } + + project.plugins.withType(PropDepsPlugin) { + project.configurations { + integrationTestCompile { + extendsFrom optional, provided + } + } + } + + project.plugins.withType(EclipsePlugin) { + project.eclipse.classpath { + plusConfigurations += [ project.configurations.integrationTestCompile ] + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy new file mode 100644 index 00000000000..900cf9f1442 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JacocoPlugin.groovy @@ -0,0 +1,41 @@ +/* + * Copyright 2016-2018 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +/** + * Adds a version of jacoco to use and makes check depend on jacocoTestReport. + * + * @author Rob Winch + */ +class JacocoPlugin implements Plugin<Project> { + + @Override + void apply(Project project) { + project.plugins.withType(JavaPlugin) { + project.getPluginManager().apply("jacoco") + project.tasks.check.dependsOn project.tasks.jacocoTestReport + + project.jacoco { + toolVersion = '0.8.2' + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy new file mode 100644 index 00000000000..fc18e4e7afa --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocApiPlugin.groovy @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import java.io.File; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Rob Winch + */ +public class JavadocApiPlugin implements Plugin<Project> { + Logger logger = LoggerFactory.getLogger(getClass()); + Set<Pattern> excludes = Collections.singleton(Pattern.compile("test")); + + @Override + public void apply(Project project) { + logger.info("Applied"); + Project rootProject = project.getRootProject(); + + + //Task docs = project.getTasks().findByPath("docs") ?: project.getTasks().create("docs"); + Javadoc api = project.getTasks().create("api", Javadoc); + + api.setGroup("Documentation"); + api.setDescription("Generates aggregated Javadoc API documentation."); + + Set<Project> subprojects = rootProject.getSubprojects(); + for (Project subproject : subprojects) { + addProject(api, subproject); + } + + if (subprojects.isEmpty()) { + addProject(api, project); + } + + api.setMaxMemory("1024m"); + api.setDestinationDir(new File(project.getBuildDir(), "api")); + + project.getPluginManager().apply("io.spring.convention.javadoc-options"); + } + + public void setExcludes(String... excludes) { + if(excludes == null) { + this.excludes = Collections.emptySet(); + } + this.excludes = new HashSet<Pattern>(excludes.length); + for(String exclude : excludes) { + this.excludes.add(Pattern.compile(exclude)); + } + } + + private void addProject(final Javadoc api, final Project project) { + for(Pattern exclude : excludes) { + if(exclude.matcher(project.getName()).matches()) { + logger.info("Skipping {} because it is excluded by {}", project, exclude); + return; + } + } + logger.info("Try add sources for {}", project); + project.getPlugins().withType(SpringModulePlugin.class).all(new Action<SpringModulePlugin>() { + @Override + public void execute(SpringModulePlugin plugin) { + logger.info("Added sources for {}", project); + + JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class); + SourceSet mainSourceSet = java.getSourceSets().getByName("main"); + + api.setSource(api.getSource().plus(mainSourceSet.getAllJava())); + project.getTasks().withType(Javadoc.class).all(new Action<Javadoc>() { + @Override + public void execute(Javadoc projectJavadoc) { + api.setClasspath(api.getClasspath().plus(projectJavadoc.getClasspath())); + } + }); + } + }); + } +} + diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy new file mode 100644 index 00000000000..e0fef7ecbed --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/JavadocOptionsPlugin.groovy @@ -0,0 +1,15 @@ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.javadoc.Javadoc + +public class JavadocOptionsPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + project.getTasks().withType(Javadoc).all { t-> + t.options.addStringOption('Xdoclint:none', '-quiet') + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java b/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java new file mode 100644 index 00000000000..e18a378f65f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/ManagementConfigurationPlugin.java @@ -0,0 +1,61 @@ +package io.spring.gradle.convention; + + +import io.spring.gradle.propdeps.PropDepsPlugin; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaTestFixturesPlugin; +import org.gradle.api.plugins.PluginContainer; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +/** + * Creates a Management configuration that is appropriate for adding a platform to that is not exposed externally. If + * the JavaPlugin is applied, the compileClasspath, runtimeClasspath, testCompileClasspath, and testRuntimeClasspath + * will extend from it. + * @author Rob Winch + */ +public class ManagementConfigurationPlugin implements Plugin<Project> { + + public static final String MANAGEMENT_CONFIGURATION_NAME = "management"; + + @Override + public void apply(Project project) { + ConfigurationContainer configurations = project.getConfigurations(); + configurations.create(MANAGEMENT_CONFIGURATION_NAME, (management) -> { + management.setVisible(false); + management.setCanBeConsumed(false); + management.setCanBeResolved(false); + + PluginContainer plugins = project.getPlugins(); + plugins.withType(JavaPlugin.class, (javaPlugin) -> { + configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.COMPILE_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.TEST_COMPILE_CONFIGURATION_NAME).extendsFrom(management); + configurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(management); + }); + plugins.withType(JavaTestFixturesPlugin.class, (javaTestFixturesPlugin) -> { + configurations.getByName("testFixturesCompileClasspath").extendsFrom(management); + configurations.getByName("testFixturesRuntimeClasspath").extendsFrom(management); + }); + plugins.withType(MavenPublishPlugin.class, (mavenPublish) -> { + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + publishing.getPublications().withType(MavenPublication.class, (mavenPublication -> { + mavenPublication.versionMapping((versions) -> + versions.allVariants(versionMapping -> versionMapping.fromResolutionResult()) + ); + })); + }); + plugins.withType(PropDepsPlugin.class, (propDepsPlugin -> { + configurations.getByName("optional").extendsFrom(management); + configurations.getByName("provided").extendsFrom(management); + })); + }); + } +} + diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy new file mode 100644 index 00000000000..92ce552feb2 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomPlugin.groovy @@ -0,0 +1,54 @@ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.MavenPlugin +import org.gradle.plugins.signing.SigningPlugin +import org.sonarqube.gradle.SonarQubePlugin + +public class MavenBomPlugin implements Plugin<Project> { + static String MAVEN_BOM_TASK_NAME = "mavenBom" + + public void apply(Project project) { + project.configurations { + archives + } + project.plugins.apply('io.spring.convention.artifactory') + project.plugins.apply('io.spring.convention.maven') + project.plugins.apply(MavenPlugin) + project.plugins.apply(SigningPlugin) + project.plugins.apply("io.spring.convention.ossrh") + + project.group = project.rootProject.group + project.task(MAVEN_BOM_TASK_NAME, type: MavenBomTask, group: 'Generate', description: 'Configures the pom as a Maven Build of Materials (BOM)') + project.install.dependsOn project.mavenBom + project.tasks.uploadArchives.dependsOn project.mavenBom + project.tasks.artifactoryPublish.dependsOn project.mavenBom + + project.plugins.withType(SonarQubePlugin) { + project.sonarqube.skipProject = true + } + + project.rootProject.allprojects.each { p -> + p.plugins.withType(io.spring.gradle.convention.SpringMavenPlugin) { + if (!project.name.equals(p.name)) { + project.mavenBom.projects.add(p) + } + } + } + + def deployArtifacts = project.task("deployArtifacts") + deployArtifacts.group = 'Deploy tasks' + deployArtifacts.description = "Deploys the artifacts to either Artifactor or Maven Central" + if(Utils.isRelease(project)) { + deployArtifacts.dependsOn project.tasks.uploadArchives + } else { + deployArtifacts.dependsOn project.tasks.artifactoryPublish + } + + project.artifacts { + archives project.mavenBom.bomFile + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomTask.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomTask.groovy new file mode 100644 index 00000000000..beaf4acfbc6 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/MavenBomTask.groovy @@ -0,0 +1,87 @@ +package io.spring.gradle.convention + +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +public class MavenBomTask extends DefaultTask { + + @Internal + Set<Project> projects = [] + + @OutputFile + File bomFile + + @Input + Set<String> getProjectNames() { + return projects*.name as Set + } + + public MavenBomTask() { + this.group = "Generate" + this.description = "Generates a Maven Build of Materials (BOM). See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies" + this.projects = project.subprojects + this.bomFile = project.file("${->project.buildDir}/maven-bom/${->project.name}-${->project.version}.txt") + this.outputs.upToDateWhen { false } + } + + @TaskAction + public void configureBom() { +// project.configurations.archives.artifacts.clear() + bomFile.parentFile.mkdirs() + bomFile.write("Maven Build of Materials (BOM). See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies") + project.artifacts { + // work around GRADLE-2406 by attaching text artifact + archives(bomFile) + } + project.install { + repositories.mavenInstaller { + pom.whenConfigured { + packaging = "pom" + withXml { + asNode().children().last() + { + delegate.dependencyManagement { + delegate.dependencies { + projects.sort { dep -> "$dep.group:$dep.name" }.each { p -> + + delegate.dependency { + delegate.groupId(p.group) + delegate.artifactId(p.name) + delegate.version(p.version) + } + } + } + } + } + } + } + } + } + project.uploadArchives { + repositories.mavenDeployer { + pom.whenConfigured { + packaging = "pom" + withXml { + asNode().children().last() + { + delegate.dependencyManagement { + delegate.dependencies { + projects.sort { dep -> "$dep.group:$dep.name" }.each { p -> + + delegate.dependency { + delegate.groupId(p.group) + delegate.artifactId(p.name) + delegate.version(p.version) + } + } + } + } + } + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/OssrhPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/OssrhPlugin.groovy new file mode 100644 index 00000000000..c414e39ce2f --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/OssrhPlugin.groovy @@ -0,0 +1,26 @@ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class OssrhPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + if(project.hasProperty('ossrhUsername')) { + project.uploadArchives { + repositories { + mavenDeployer { + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: project.ossrhUsername, password: project.ossrhPassword) + } + + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: project.ossrhUsername, password: project.ossrhPassword) + } + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy new file mode 100644 index 00000000000..c2420814290 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy @@ -0,0 +1,84 @@ +/* + * Copyright 2016-2018 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Plugin +import org.gradle.api.Project + +class RepositoryConventionPlugin implements Plugin<Project> { + + @Override + void apply(Project project) { + String[] forceMavenRepositories = ((String) project.findProperty("forceMavenRepositories"))?.split(',') + boolean isImplicitSnapshotRepository = forceMavenRepositories == null && Utils.isSnapshot(project) + boolean isImplicitMilestoneRepository = forceMavenRepositories == null && Utils.isMilestone(project) + + boolean isSnapshot = isImplicitSnapshotRepository || forceMavenRepositories?.contains('snapshot') + boolean isMilestone = isImplicitMilestoneRepository || forceMavenRepositories?.contains('milestone') + + project.repositories { + if (forceMavenRepositories?.contains('local')) { + mavenLocal() + } + mavenCentral() + jcenter() { + content { + includeGroup "org.gretty" + } + } + if (isSnapshot) { + maven { + name = 'artifactory-snapshot' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/snapshot/' + } + } + if (isSnapshot || isMilestone) { + maven { + name = 'artifactory-milestone' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/milestone/' + } + } + maven { + name = 'artifactory-release' + if (project.hasProperty('artifactoryUsername')) { + credentials { + username project.artifactoryUsername + password project.artifactoryPassword + } + } + url = 'https://repo.spring.io/release/' + } + maven { + name = 'shibboleth' + url = 'https://build.shibboleth.net/nexus/content/repositories/releases/' + } + } + } + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy new file mode 100644 index 00000000000..d457eaec468 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy @@ -0,0 +1,67 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.nohttp.gradle.NoHttpPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.plugins.PluginManager + +class RootProjectPlugin implements Plugin<Project> { + + @Override + void apply(Project project) { + PluginManager pluginManager = project.getPluginManager() + pluginManager.apply(BasePlugin) + pluginManager.apply(SchemaPlugin) + pluginManager.apply(NoHttpPlugin) + pluginManager.apply("org.sonarqube") + + project.repositories.mavenCentral() + + String projectName = Utils.getProjectName(project) + project.sonarqube { + properties { + property "sonar.java.coveragePlugin", "jacoco" + property "sonar.projectName", projectName + property "sonar.jacoco.reportPath", "${project.buildDir.name}/jacoco.exec" + property "sonar.links.homepage", "https://spring.io/${projectName}" + property "sonar.links.ci", "https://jenkins.spring.io/job/${projectName}/" + property "sonar.links.issue", "https://github.com/spring-projects/${projectName}/issues" + property "sonar.links.scm", "https://github.com/spring-projects/${projectName}" + property "sonar.links.scm_dev", "https://github.com/spring-projects/${projectName}.git" + } + } + + def finalizeDeployArtifacts = project.task("finalizeDeployArtifacts") + if (Utils.isRelease(project) && project.hasProperty("ossrhUsername")) { + project.ext.nexusUsername = project.ossrhUsername + project.ext.nexusPassword = project.ossrhPassword + project.getPluginManager().apply("io.codearte.nexus-staging") + finalizeDeployArtifacts.dependsOn project.tasks.closeAndReleaseRepository + project.nexusStaging { + packageGroup = 'org.springframework' + + // try for 5 minutes total + numberOfRetries = 60 // default is 20 + delayBetweenRetriesInMillis = 5000 // default is 2000 + } + } + } + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy new file mode 100644 index 00000000000..4b9c038dbb1 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaDeployPlugin.groovy @@ -0,0 +1,71 @@ +package io.spring.gradle.convention + +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class SchemaDeployPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + project.getPluginManager().apply('org.hidetake.ssh') + + project.ssh.settings { + knownHosts = allowAnyHosts + } + project.remotes { + docs { + role 'docs' + if (project.hasProperty('deployDocsHost')) { + host = project.findProperty('deployDocsHost') + } else { + host = 'docs.af.pivotal.io' + } + retryCount = 5 // retry 5 times (default is 0) + retryWaitSec = 10 // wait 10 seconds between retries (default is 0) + user = project.findProperty('deployDocsSshUsername') + if(project.hasProperty('deployDocsSshKeyPath')) { + identity = project.file(project.findProperty('deployDocsSshKeyPath')) + } else if (project.hasProperty('deployDocsSshKey')) { + identity = project.findProperty('deployDocsSshKey') + } + if(project.hasProperty('deployDocsSshPassphrase')) { + passphrase = project.findProperty('deployDocsSshPassphrase') + } + } + } + + project.task('deploySchema') { + dependsOn 'schemaZip' + doFirst { + project.ssh.run { + session(project.remotes.docs) { + def now = System.currentTimeMillis() + def name = project.rootProject.name + def version = project.rootProject.version + def tempPath = "/tmp/${name}-${now}-schema/".replaceAll(' ', '_') + + execute "mkdir -p $tempPath" + + project.tasks.schemaZip.outputs.each { o -> + println "Putting $o.files" + put from: o.files, into: tempPath + } + + execute "unzip $tempPath*.zip -d $tempPath" + + def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/schema/${name}/${version}/" + + execute "rm -rf $extractPath" + execute "mkdir -p $extractPath" + execute "rm -f $tempPath*.zip" + execute "rm -rf $extractPath*" + execute "mv $tempPath/* $extractPath" + execute "chmod -R g+w $extractPath" + } + } + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy new file mode 100644 index 00000000000..769ae80d110 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaPlugin.groovy @@ -0,0 +1,15 @@ +package io.spring.gradle.convention + +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class SchemaPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + project.getPluginManager().apply(SchemaZipPlugin) + project.getPluginManager().apply(SchemaDeployPlugin) + } +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy new file mode 100644 index 00000000000..72de4b3f734 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SchemaZipPlugin.groovy @@ -0,0 +1,43 @@ +package io.spring.gradle.convention + +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.Plugin +import org.gradle.api.Project + +public class SchemaZipPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + Zip schemaZip = project.tasks.create('schemaZip', Zip) + schemaZip.group = 'Distribution' + schemaZip.archiveBaseName = project.rootProject.name + schemaZip.archiveClassifier = 'schema' + schemaZip.description = "Builds -${schemaZip.archiveClassifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + + project.rootProject.subprojects.each { module -> + + module.getPlugins().withType(JavaPlugin.class).all { + def Properties schemas = new Properties(); + + module.sourceSets.main.resources.find { + it.path.endsWith('META-INF/spring.schemas') + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') + assert shortName != key + File xsdFile = module.sourceSets.main.resources.find { + it.path.endsWith(schemas.get(key)) + } + assert xsdFile != null + schemaZip.into (shortName) { + duplicatesStrategy 'exclude' + from xsdFile.path + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SortedProperties.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SortedProperties.groovy new file mode 100644 index 00000000000..5b950d83daf --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SortedProperties.groovy @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +/** + * A Properties which sorts they keys so that they can be written to a File with + * the keys sorted. + * + * @author Rob Winch + * + */ +class SortedProperties extends Properties { + private static final long serialVersionUID = -6199017589626540836L; + + public Enumeration<Object> keys() { + Enumeration<Object> keysEnum = super.keys(); + List<Object> keyList = new ArrayList<Object>(); + + while (keysEnum.hasMoreElements()) { + keyList.add(keysEnum.nextElement()); + } + + Collections.sort(keyList, new Comparator<Object>() { + @Override + public int compare(Object o1, Object o2) { + return o1.toString().compareTo(o2.toString()); + } + }); + + return Collections.enumeration(keyList); + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy new file mode 100644 index 00000000000..454aebfba77 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringMavenPlugin.groovy @@ -0,0 +1,212 @@ +package io.spring.gradle.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.XmlProvider +import org.gradle.api.artifacts.component.ModuleComponentSelector +import org.gradle.api.artifacts.maven.MavenDeployment +import org.gradle.api.artifacts.maven.MavenPom +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.plugins.MavenPlugin +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.javadoc.Javadoc +import org.gradle.plugins.signing.SigningPlugin +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +public class SpringMavenPlugin implements Plugin<Project> { + private static final String ARCHIVES = "archives"; + Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void apply(Project project) { + project.getPluginManager().apply(JavaPlugin.class); + project.getPluginManager().apply(MavenPlugin.class); + project.getPluginManager().apply(SigningPlugin.class); + + Javadoc javadoc = (Javadoc) project.getTasks().findByPath("javadoc"); + Jar javadocJar = project.getTasks().create("javadocJar", Jar.class); + javadocJar.setClassifier("javadoc"); + javadocJar.from(javadoc); + + JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class); + SourceSet mainSourceSet = java.getSourceSets().getByName("main"); + Jar sourcesJar = project.getTasks().create("sourcesJar", Jar.class); + sourcesJar.setClassifier("sources"); + sourcesJar.from(mainSourceSet.getAllSource()); + + project.getArtifacts().add(ARCHIVES, javadocJar); + project.getArtifacts().add(ARCHIVES, sourcesJar); + + project.install { + repositories.mavenInstaller { + configurePom(project, pom) + } + } + project.uploadArchives { + repositories.mavenDeployer { + configurePom(project, pom) + } + } + + inlineDependencyManagement(project); + + def hasSigningKey = project.hasProperty("signing.keyId") || project.findProperty("signingKey") + if(hasSigningKey && Utils.isRelease(project)) { + sign(project) + } + + project.getPluginManager().apply("io.spring.convention.ossrh"); + } + + private void inlineDependencyManagement(Project project) { + project.install { + repositories.mavenInstaller { + configurePomForInlineDependencies(project, pom) + } + } + project.uploadArchives { + repositories.mavenDeployer { + configurePomForInlineDependencies(project, pom) + } + } + } + + private void configurePomForInlineDependencies(Project project, MavenPom pom) { + pom.withXml { XmlProvider xml -> + project.plugins.withType(JavaBasePlugin) { + def dependencies = xml.asNode()?.dependencies?.dependency + def configuredDependencies = project.configurations.findAll{ it.canBeResolved && it.canBeConsumed }*.incoming*.resolutionResult*.allDependencies.flatten() + dependencies?.each { Node dep -> + def group = dep.groupId.text() + def name = dep.artifactId.text() + + ResolvedDependencyResult resolved = configuredDependencies.find { r -> + (r.requested instanceof ModuleComponentSelector) && + (r.requested.group == group) && + (r.requested.module == name) + } + + if (!resolved) { + return + } + + def versionNode = dep.version + if (!versionNode) { + dep.appendNode('version') + } + def moduleVersion = resolved.selected.moduleVersion + dep.groupId[0].value = moduleVersion.group + dep.artifactId[0].value = moduleVersion.name + dep.version[0].value = moduleVersion.version + } + } + } + } + + private void sign(Project project) { + project.install { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> project.signing.signPom(deployment) } + } + } + } + + project.uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> project.signing.signPom(deployment) } + } + } + } + + project.signing { + required { project.gradle.taskGraph.hasTask("uploadArchives") } + def signingKeyId = project.findProperty("signingKeyId") + def signingKey = project.findProperty("signingKey") + def signingPassword = project.findProperty("signingPassword") + if (signingKeyId) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } else if (signingKey) { + useInMemoryPgpKeys(signingKey, signingPassword) + } + sign project.configurations.archives + } + } + + private static void configurePom(Project project, MavenPom pom) { + pom.whenConfigured { p -> + p.dependencies = p.dependencies.sort { dep -> + "$dep.scope:$dep.optional:$dep.groupId:$dep.artifactId" + } + } + + pom.project { + boolean isWar = project.hasProperty("war"); + String projectVersion = String.valueOf(project.getVersion()); + String projectName = Utils.getProjectName(project); + + if(isWar) { + packaging = "war" + } + name = project.name + description = project.name + url = 'https://spring.io/spring-security' + organization { + name = 'spring.io' + url = 'https://spring.io/' + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + scm { + url = 'https://github.com/spring-projects/spring-security' + connection = 'scm:git:git://github.com/spring-projects/spring-security' + developerConnection = 'scm:git:git://github.com/spring-projects/spring-security' + } + developers { + developer { + id = 'rwinch' + name = 'Rob Winch' + email = 'rwinch@pivotal.io' + } + developer { + id = 'jgrandja' + name = 'Joe Grandja' + email = 'jgrandja@pivotal.io' + } + } + + if(isWar) { + properties { + 'm2eclipse.wtp.contextRoot' '/' + } + } + if (Utils.isSnapshot(project)) { + repositories { + repository { + id 'spring-snapshot' + url 'https://repo.spring.io/snapshot' + } + } + } + else if (Utils.isMilestone(project)) { + repositories { + repository { + id 'spring-milestone' + url 'https://repo.spring.io/milestone' + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy new file mode 100644 index 00000000000..07e16acd696 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Project +import org.gradle.api.plugins.JavaLibraryPlugin; +import org.gradle.api.plugins.MavenPlugin; +import org.gradle.api.plugins.PluginManager; + +/** + * @author Rob Winch + */ +class SpringModulePlugin extends AbstractSpringJavaPlugin { + + @Override + void additionalPlugins(Project project) { + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaLibraryPlugin.class) + pluginManager.apply(MavenPlugin.class); + pluginManager.apply("io.spring.convention.maven"); + pluginManager.apply("io.spring.convention.artifactory"); + pluginManager.apply("io.spring.convention.jacoco"); + + def deployArtifacts = project.task("deployArtifacts") + deployArtifacts.group = 'Deploy tasks' + deployArtifacts.description = "Deploys the artifacts to either Artifactory or Maven Central" + if (Utils.isRelease(project)) { + deployArtifacts.dependsOn project.tasks.uploadArchives + } + else { + deployArtifacts.dependsOn project.tasks.artifactoryPublish + } + } + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringPomPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringPomPlugin.groovy new file mode 100644 index 00000000000..955914c54a6 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringPomPlugin.groovy @@ -0,0 +1,24 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +/** + * @author Rob Winch + */ +public class SpringPomPlugin extends SpringModulePlugin { + +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy new file mode 100644 index 00000000000..55807dc0f17 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy @@ -0,0 +1,30 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Project; + +/** + * @author Rob Winch + */ +public class SpringTestPlugin extends AbstractSpringJavaPlugin { + + @Override + public void additionalPlugins(Project project) { + project.sonarqube.skipProject = true + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy new file mode 100644 index 00000000000..99aac80abe2 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.jvm.tasks.Jar + +/** + * Adds the ability to depends on the test jar within other projects using: + * + * <code> + * testCompile project(path: ':foo', configuration: 'tests') + * </code> + * + * @author Rob Winch + */ +public class TestsConfigurationPlugin implements Plugin<Project> { + @Override + public void apply(Project project) { + project.plugins.withType(JavaPlugin) { + applyJavaProject(project) + } + } + + private void applyJavaProject(Project project) { + project.configurations { + tests.extendsFrom testRuntime, testRuntimeClasspath + } + + project.tasks.create('testJar', Jar) { + classifier = 'test' + from project.sourceSets.test.output + } + + project.artifacts { + tests project.testJar + } + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy new file mode 100644 index 00000000000..8f5a6a90648 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy @@ -0,0 +1,34 @@ +package io.spring.gradle.convention; + +import org.gradle.api.Project; + +public class Utils { + + static String getProjectName(Project project) { + String projectName = project.getRootProject().getName(); + if(projectName.endsWith("-build")) { + projectName = projectName.substring(0, projectName.length() - "-build".length()); + } + return projectName; + } + + static boolean isSnapshot(Project project) { + String projectVersion = projectVersion(project) + return projectVersion.matches('^.*([.-]BUILD)?-SNAPSHOT$') + } + + static boolean isMilestone(Project project) { + String projectVersion = projectVersion(project) + return projectVersion.matches('^.*[.-]M\\d+$') || projectVersion.matches('^.*[.-]RC\\d+$') + } + + static boolean isRelease(Project project) { + return !(isSnapshot(project) || isMilestone(project)) + } + + private static String projectVersion(Project project) { + return String.valueOf(project.getVersion()); + } + + private Utils() {} +} diff --git a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java new file mode 100644 index 00000000000..548dfbca513 --- /dev/null +++ b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java @@ -0,0 +1,208 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider; +import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; +import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; +import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; +import org.asciidoctor.gradle.jvm.AsciidoctorTask; +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.file.CopySpec; +import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.Sync; + +import java.io.File; +import java.net.URI; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +/** + * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When + * the plugin is applied: + * + * <ul> + * <li>All warnings are made fatal. + * <li>A task is created to resolve and unzip our documentation resources (CSS and + * Javascript). + * <li>For each {@link AsciidoctorTask} (HTML only): + * <ul> + * <li>A configuration named asciidoctorExtensions is ued to add the + * <a href="https://github.com/spring-io/spring-asciidoctor-extensions#block-switch">block + * switch</a> extension + * <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. + * <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax + * highlighting, CSS styling, docinfo, etc. + * </ul> + * <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF): + * <ul> + * <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable + * warnings for references to missing attributes, the year is added as @{code today-year}, + * etc + * <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} + * is enabled. + * </ul> + * </ul> + * + * @author Andy Wilkinson + * @author Rob Winch + */ +public class AsciidoctorConventionPlugin implements Plugin<Project> { + + public void apply(Project project) { + project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> { + createDefaultAsciidoctorRepository(project); + makeAllWarningsFatal(project); + Sync unzipResources = createUnzipDocumentationResourcesTask(project); + project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> { + asciidoctorTask.dependsOn(unzipResources); + configureExtensions(project, asciidoctorTask); + configureCommonAttributes(project, asciidoctorTask); + configureOptions(asciidoctorTask); + asciidoctorTask.baseDirFollowsSourceDir(); + asciidoctorTask.useIntermediateWorkDir(); + asciidoctorTask.resources(new Action<CopySpec>() { + @Override + public void execute(CopySpec resourcesSpec) { + resourcesSpec.from(unzipResources); + resourcesSpec.from(asciidoctorTask.getSourceDir(), new Action<CopySpec>() { + @Override + public void execute(CopySpec resourcesSrcDirSpec) { + // https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523 + // For now copy the entire sourceDir over so that include files are + // available in the intermediateWorkDir + // resourcesSrcDirSpec.include("images/**"); + } + }); + } + }); + if (asciidoctorTask instanceof AsciidoctorTask) { + configureHtmlOnlyAttributes(project, asciidoctorTask); + } + }); + }); + } + + private void createDefaultAsciidoctorRepository(Project project) { + project.getGradle().afterProject(new Action<Project>() { + @Override + public void execute(Project project) { + RepositoryHandler repositories = project.getRepositories(); + if (repositories.isEmpty()) { + repositories.mavenCentral(); + repositories.maven(repo -> { + repo.setUrl(URI.create("https://repo.spring.io/release")); + }); + } + } + }); + } + + private void makeAllWarningsFatal(Project project) { + project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); + } + + private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) { + Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions"); + extensionsConfiguration.defaultDependencies(new Action<DependencySet>() { + @Override + public void execute(DependencySet dependencies) { + dependencies.add(project.getDependencies().create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE")); + } + }); + asciidoctorTask.configurations(extensionsConfiguration); + } + + private Sync createUnzipDocumentationResourcesTask(Project project) { + Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources"); + documentationResources.getDependencies() + .add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5")); + Sync unzipResources = project.getTasks().create("unzipDocumentationResources", + Sync.class, new Action<Sync>() { + @Override + public void execute(Sync sync) { + sync.dependsOn(documentationResources); + sync.from(new Callable<List<FileTree>>() { + @Override + public List<FileTree> call() throws Exception { + List<FileTree> result = new ArrayList<>(); + documentationResources.getAsFileTree().forEach(new Consumer<File>() { + @Override + public void accept(File file) { + result.add(project.zipTree(file)); + } + }); + return result; + } + }); + File destination = new File(project.getBuildDir(), "docs/resources"); + sync.into(project.relativePath(destination)); + } + }); + return unzipResources; + } + + private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { + asciidoctorTask.options(Collections.singletonMap("doctype", "book")); + } + + private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { + Map<String, Object> attributes = new HashMap<>(); + attributes.put("source-highlighter", "highlight.js"); + attributes.put("highlightjsdir", "js/highlight"); + attributes.put("highlightjs-theme", "github"); + attributes.put("linkcss", true); + attributes.put("icons", "font"); + attributes.put("stylesheet", "css/spring.css"); + asciidoctorTask.getAttributeProviders().add(new AsciidoctorAttributeProvider() { + @Override + public Map<String, Object> getAttributes() { + Object version = project.getVersion(); + Map<String, Object> attrs = new HashMap<>(); + if (version != null && version.toString() != Project.DEFAULT_VERSION) { + attrs.put("revnumber", version); + } + return attrs; + } + }); + asciidoctorTask.attributes(attributes); + } + + private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { + Map<String, Object> attributes = new HashMap<>(); + attributes.put("attribute-missing", "warn"); + attributes.put("icons", "font"); + attributes.put("idprefix", ""); + attributes.put("idseparator", "-"); + attributes.put("docinfo", "shared"); + attributes.put("sectanchors", ""); + attributes.put("sectnums", ""); + attributes.put("today-year", LocalDate.now().getYear()); + asciidoctorTask.attributes(attributes); + } +} diff --git a/buildSrc/src/main/java/org/springframework/security/convention/versions/GitHubApi.java b/buildSrc/src/main/java/org/springframework/security/convention/versions/GitHubApi.java new file mode 100644 index 00000000000..74738e2ce4e --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/security/convention/versions/GitHubApi.java @@ -0,0 +1,179 @@ +package org.springframework.security.convention.versions; + +import com.apollographql.apollo.ApolloCall; +import com.apollographql.apollo.ApolloClient; +import com.apollographql.apollo.api.Input; +import com.apollographql.apollo.api.Response; +import com.apollographql.apollo.exception.ApolloException; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; +import reactor.util.retry.RetrySpec; + +import java.io.IOException; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class GitHubApi { + + private final ApolloClient apolloClient; + + public GitHubApi(String githubToken) { + if (githubToken == null) { + throw new IllegalArgumentException("githubToken is required. You can set it using -PgitHubAccessToken="); + } + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + clientBuilder.addInterceptor(new AuthorizationInterceptor(githubToken)); + this.apolloClient = ApolloClient.builder() + .serverUrl("https://api.github.com/graphql") + .okHttpClient(clientBuilder.build()) + .build(); + } + + public Mono<FindCreateIssueResult> findCreateIssueInput(String owner, String name, String milestone) { + String label = "\"type: dependency-upgrade\""; + FindCreateIssueInputQuery findCreateIssueInputQuery = new FindCreateIssueInputQuery(owner, name, Input.optional(label), Input.optional(milestone)); + return Mono.create( sink -> this.apolloClient.query(findCreateIssueInputQuery) + .enqueue(new ApolloCall.Callback<FindCreateIssueInputQuery.Data>() { + @Override + public void onResponse(@NotNull Response<FindCreateIssueInputQuery.Data> response) { + if (response.hasErrors()) { + sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" ")))); + } else { + FindCreateIssueInputQuery.Data data = response.getData(); + FindCreateIssueInputQuery.Repository repository = data.repository(); + List<String> labels = repository.labels().nodes().stream().map(FindCreateIssueInputQuery.Node::id).collect(Collectors.toList()); + if (labels.isEmpty()) { + sink.error(new IllegalArgumentException("Could not find label for " + label)); + return; + } + Optional<String> firstMilestoneId = repository.milestones().nodes().stream().map(FindCreateIssueInputQuery.Node1::id).findFirst(); + if (!firstMilestoneId.isPresent()) { + sink.error(new IllegalArgumentException("Could not find OPEN milestone id for " + milestone)); + return; + } + String milestoneId = firstMilestoneId.get(); + String repositoryId = repository.id(); + String assigneeId = data.viewer().id(); + sink.success(new FindCreateIssueResult(repositoryId, labels, milestoneId, assigneeId)); + } + } + @Override + public void onFailure(@NotNull ApolloException e) { + sink.error(e); + } + })); + } + + public static class FindCreateIssueResult { + private final String repositoryId; + private final List<String> labelIds; + private final String milestoneId; + private final String assigneeId; + + public FindCreateIssueResult(String repositoryId, List<String> labelIds, String milestoneId, String assigneeId) { + this.repositoryId = repositoryId; + this.labelIds = labelIds; + this.milestoneId = milestoneId; + this.assigneeId = assigneeId; + } + + public String getRepositoryId() { + return repositoryId; + } + + public List<String> getLabelIds() { + return labelIds; + } + + public String getMilestoneId() { + return milestoneId; + } + + public String getAssigneeId() { + return assigneeId; + } + } + + public Mono<RateLimitQuery.RateLimit> findRateLimit() { + return Mono.create( sink -> this.apolloClient.query(new RateLimitQuery()) + .enqueue(new ApolloCall.Callback<RateLimitQuery.Data>() { + @Override + public void onResponse(@NotNull Response<RateLimitQuery.Data> response) { + if (response.hasErrors()) { + sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" ")))); + } else { + sink.success(response.getData().rateLimit()); + } + } + @Override + public void onFailure(@NotNull ApolloException e) { + sink.error(e); + } + })); + } + + public Mono<Integer> createIssue(String repositoryId, String title, List<String> labelIds, String milestoneId, String assigneeId) { + CreateIssueInputMutation createIssue = new CreateIssueInputMutation.Builder() + .repositoryId(repositoryId) + .title(title) + .labelIds(labelIds) + .milestoneId(milestoneId) + .assigneeId(assigneeId) + .build(); + return Mono.create( sink -> this.apolloClient.mutate(createIssue) + .enqueue(new ApolloCall.Callback<CreateIssueInputMutation.Data>() { + @Override + public void onResponse(@NotNull Response<CreateIssueInputMutation.Data> response) { + if (response.hasErrors()) { + String message = response.getErrors().stream().map(e -> e.getMessage() + " " + e.getCustomAttributes() + " " + e.getLocations()).collect(Collectors.joining(" ")); + if (message.contains("was submitted too quickly")) { + sink.error(new SubmittedTooQuick(message)); + } else { + sink.error(new RuntimeException(message)); + } + } else { + sink.success(response.getData().createIssue().issue().number()); + } + } + @Override + public void onFailure(@NotNull ApolloException e) { + sink.error(e); + } + })) + .retryWhen( + RetrySpec.fixedDelay(3, Duration.ofMinutes(1)) + .filter(SubmittedTooQuick.class::isInstance) + .doBeforeRetry(r -> System.out.println("Pausing for 1 minute and then retrying due to receiving \"submitted too quickly\" error from GitHub API")) + ) + .cast(Integer.class); + } + + public static class SubmittedTooQuick extends RuntimeException { + public SubmittedTooQuick(String message) { + super(message); + } + } + + private static class AuthorizationInterceptor implements Interceptor { + + private final String token; + + public AuthorizationInterceptor(String token) { + this.token = token; + } + + @Override + public okhttp3.Response intercept(Chain chain) throws IOException { + Request request = chain.request().newBuilder() + .addHeader("Authorization", "Bearer " + this.token).build(); + return chain.proceed(request); + } + } +} diff --git a/buildSrc/src/main/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtils.java b/buildSrc/src/main/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtils.java new file mode 100644 index 00000000000..66eb8b5f0db --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * 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 + * + * https://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.springframework.security.convention.versions; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.InputStream; + +class TransitiveDependencyLookupUtils { + static String OIDC_SDK_NAME = "oauth2-oidc-sdk"; + static String NIMBUS_JOSE_JWT_NAME = "nimbus-jose-jwt"; + + private static OkHttpClient client = new OkHttpClient(); + + static String lookupJwtVersion(String oauthSdcVersion) { + Request request = new Request.Builder() + .get() + .url("https://repo.maven.apache.org/maven2/com/nimbusds/" + OIDC_SDK_NAME + "/" + oauthSdcVersion + "/" + OIDC_SDK_NAME + "-" + oauthSdcVersion + ".pom") + .build(); + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException("Unexpected code " + response); + } + InputStream inputStream = response.body().byteStream(); + return getVersion(inputStream); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static String getVersion(InputStream inputStream) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder db = dbf.newDocumentBuilder(); + + Document doc = db.parse(inputStream); + + doc.getDocumentElement().normalize(); + + XPath xPath = XPathFactory.newInstance().newXPath(); + return xPath.evaluate("/project/dependencies/dependency/version[../artifactId/text() = \"" + NIMBUS_JOSE_JWT_NAME + "\"]", doc); + } +} diff --git a/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java b/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java new file mode 100644 index 00000000000..a4fb5dc5bcd --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesExtension.java @@ -0,0 +1,180 @@ +package org.springframework.security.convention.versions; + +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionRulesWithCurrent; +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent; +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent; +import org.gradle.api.Action; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.regex.Pattern; + +public class UpdateDependenciesExtension { + private Supplier<List<File>> files; + + private UpdateMode updateMode = UpdateMode.COMMIT; + + private DependencyExcludes dependencyExcludes = new DependencyExcludes(); + + private GitHub gitHub = new GitHub(); + + public UpdateDependenciesExtension(Supplier<List<File>> files) { + this.files = files; + } + + public void setUpdateMode(UpdateMode updateMode) { + this.updateMode = updateMode; + } + + public UpdateMode getUpdateMode() { + return updateMode; + } + + GitHub getGitHub() { + return this.gitHub; + } + + DependencyExcludes getExcludes() { + return dependencyExcludes; + } + + Supplier<List<File>> getFiles() { + return files; + } + + public void setFiles(Supplier<List<File>> files) { + this.files = files; + } + + public void addFiles(Supplier<List<File>> files) { + Supplier<List<File>> original = this.files; + setFiles(() -> { + List<File> result = new ArrayList<>(original.get()); + result.addAll(files.get()); + return result; + }); + } + + public void dependencyExcludes(Action<DependencyExcludes> excludes) { + excludes.execute(this.dependencyExcludes); + } + + public void gitHub(Action<GitHub> gitHub) { + gitHub.execute(this.gitHub); + } + + public enum UpdateMode { + COMMIT, + GITHUB_ISSUE + } + + public class GitHub { + private String organization; + + private String repository; + + private String accessToken; + + private String milestone; + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getRepository() { + return repository; + } + + public void setRepository(String repository) { + this.repository = repository; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getMilestone() { + return milestone; + } + + public void setMilestone(String milestone) { + this.milestone = milestone; + } + } + + /** + * Consider creating some Predicates instead since they are composible + */ + public class DependencyExcludes { + private List<Action<ComponentSelectionWithCurrent>> actions = new ArrayList<>(); + private List<Action<ComponentSelectionRulesWithCurrent>> components = new ArrayList<>(); + + List<Action<ComponentSelectionWithCurrent>> getActions() { + return actions; + } + + public List<Action<ComponentSelectionRulesWithCurrent>> getComponents() { + return components; + } + + public DependencyExcludes alphaBetaVersions() { + this.actions.add(excludeVersionWithRegex("(?i).*?(alpha|beta).*", "an alpha or beta version")); + return this; + } + + public DependencyExcludes majorVersionBump() { + this.actions.add((selection) -> { + String currentVersion = selection.getCurrentVersion(); + int separator = currentVersion.indexOf("."); + String major = separator > 0 ? currentVersion.substring(0, separator) : currentVersion; + String candidateVersion = selection.getCandidate().getVersion(); + Pattern calVerPattern = Pattern.compile("\\d\\d\\d\\d.*"); + boolean isCalVer = calVerPattern.matcher(candidateVersion).matches(); + if (!isCalVer && !candidateVersion.startsWith(major)) { + selection.reject("Cannot upgrade to new Major Version"); + } + }); + return this; + } + + public DependencyExcludes releaseCandidatesVersions() { + this.actions.add(excludeVersionWithRegex("(?i).*?rc\\d+.*", "a release candidate version")); + return this; + } + + public DependencyExcludes milestoneVersions() { + this.actions.add(excludeVersionWithRegex("(?i).*?m\\d+.*", "a milestone version")); + return this; + } + + public DependencyExcludes snapshotVersions() { + this.actions.add(excludeVersionWithRegex(".*?-SNAPSHOT.*", "a SNAPSHOT version")); + return this; + } + + public DependencyExcludes addRule(Action<ComponentSelectionRulesWithCurrent> rule) { + this.components.add(rule); + return this; + } + + private Action<ComponentSelectionWithCurrent> excludeVersionWithRegex(String regex, String reason) { + Pattern pattern = Pattern.compile(regex); + return (selection) -> { + String candidateVersion = selection.getCandidate().getVersion(); + if (pattern.matcher(candidateVersion).matches()) { + selection.reject(candidateVersion + " is not allowed because it is " + reason); + } + }; + } + } +} diff --git a/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java new file mode 100644 index 00000000000..f7e11fa6d57 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/security/convention/versions/UpdateDependenciesPlugin.java @@ -0,0 +1,296 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * 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 + * + * https://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.springframework.security.convention.versions; + +import com.github.benmanes.gradle.versions.reporter.result.Dependency; +import com.github.benmanes.gradle.versions.reporter.result.DependencyOutdated; +import com.github.benmanes.gradle.versions.reporter.result.Result; +import com.github.benmanes.gradle.versions.reporter.result.VersionAvailable; +import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask; +import com.github.benmanes.gradle.versions.updates.gradle.GradleUpdateResult; +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionRulesWithCurrent; +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent; +import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent; +import groovy.lang.Closure; +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.component.ModuleComponentIdentifier; +import reactor.core.publisher.Mono; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.time.Duration; +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.NIMBUS_JOSE_JWT_NAME; +import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.OIDC_SDK_NAME; + +public class UpdateDependenciesPlugin implements Plugin<Project> { + private GitHubApi gitHubApi; + + @Override + public void apply(Project project) { + UpdateDependenciesExtension updateDependenciesSettings = project.getExtensions().create("updateDependenciesSettings", UpdateDependenciesExtension.class, defaultFiles(project)); + if (project.hasProperty("updateMode")) { + String updateMode = String.valueOf(project.findProperty("updateMode")); + updateDependenciesSettings.setUpdateMode(UpdateDependenciesExtension.UpdateMode.valueOf(updateMode)); + } + if (project.hasProperty("nextVersion")) { + String nextVersion = String.valueOf(project.findProperty("nextVersion")); + updateDependenciesSettings.getGitHub().setMilestone(nextVersion); + } + if (project.hasProperty("gitHubAccessToken")) { + String gitHubAccessToken = String.valueOf(project.findProperty("gitHubAccessToken")); + updateDependenciesSettings.getGitHub().setAccessToken(gitHubAccessToken); + } + project.getTasks().register("updateDependencies", DependencyUpdatesTask.class, new Action<DependencyUpdatesTask>() { + @Override + public void execute(DependencyUpdatesTask updateDependencies) { + updateDependencies.setDescription("Update the dependencies"); + updateDependencies.setCheckConstraints(true); + updateDependencies.setOutputFormatter(new Closure<Void>(null) { + @Override + public Void call(Object argument) { + Result result = (Result) argument; + if (gitHubApi == null && updateDependenciesSettings.getUpdateMode() != UpdateDependenciesExtension.UpdateMode.COMMIT) { + gitHubApi = new GitHubApi(updateDependenciesSettings.getGitHub().getAccessToken()); + } + updateDependencies(result, project, updateDependenciesSettings); + updateGradleVersion(result, project, updateDependenciesSettings); + return null; + } + }); + updateDependencies.resolutionStrategy(new Action<ResolutionStrategyWithCurrent>() { + @Override + public void execute(ResolutionStrategyWithCurrent resolution) { + resolution.componentSelection(new Action<ComponentSelectionRulesWithCurrent>() { + @Override + public void execute(ComponentSelectionRulesWithCurrent components) { + updateDependenciesSettings.getExcludes().getActions().forEach((action) -> { + components.all(action); + }); + updateDependenciesSettings.getExcludes().getComponents().forEach((action) -> { + action.execute(components); + }); + components.all((selection) -> { + ModuleComponentIdentifier candidate = selection.getCandidate(); + if (candidate.getGroup().startsWith("org.apache.directory.") && !candidate.getVersion().equals(selection.getCurrentVersion())) { + selection.reject("org.apache.directory.* has breaking changes in newer versions"); + } + }); + String jaxbBetaRegex = ".*?b\\d+.*"; + components.withModule("javax.xml.bind:jaxb-api", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions")); + components.withModule("com.sun.xml.bind:jaxb-impl", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions")); + components.withModule("commons-collections:commons-collections", excludeWithRegex("^\\d{3,}.*", "Reject commons-collections date based releases")); + } + }); + } + }); + } + }); + } + + private void updateDependencies(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) { + SortedSet<DependencyOutdated> dependencies = result.getOutdated().getDependencies(); + if (dependencies.isEmpty()) { + return; + } + Map<String, List<DependencyOutdated>> groups = new LinkedHashMap<>(); + dependencies.forEach(outdated -> { + groups.computeIfAbsent(outdated.getGroup(), (key) -> new ArrayList<>()).add(outdated); + }); + List<DependencyOutdated> nimbusds = groups.getOrDefault("com.nimbusds", new ArrayList<>()); + DependencyOutdated oidcSdc = nimbusds.stream().filter(d -> d.getName().equals(OIDC_SDK_NAME)).findFirst().orElseGet(() -> null); + if(oidcSdc != null) { + String oidcVersion = updatedVersion(oidcSdc); + String jwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oidcVersion); + + Dependency nimbusJoseJwtDependency = result.getCurrent().getDependencies().stream().filter(d -> d.getName().equals(NIMBUS_JOSE_JWT_NAME)).findFirst().get(); + DependencyOutdated outdatedJwt = new DependencyOutdated(); + outdatedJwt.setVersion(nimbusJoseJwtDependency.getVersion()); + outdatedJwt.setGroup(oidcSdc.getGroup()); + outdatedJwt.setName(NIMBUS_JOSE_JWT_NAME); + VersionAvailable available = new VersionAvailable(); + available.setRelease(jwtVersion); + outdatedJwt.setAvailable(available); + nimbusds.add(outdatedJwt); + } + File gradlePropertiesFile = project.getRootProject().file(Project.GRADLE_PROPERTIES); + Mono<GitHubApi.FindCreateIssueResult> createIssueResult = createIssueResultMono(updateDependenciesSettings); + List<File> filesWithDependencies = updateDependenciesSettings.getFiles().get(); + groups.forEach((group, outdated) -> { + outdated.forEach((dependency) -> { + String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; + String originalDependency = ga + dependency.getVersion(); + String replacementDependency = ga + updatedVersion(dependency); + System.out.println("Update " + originalDependency + " to " + replacementDependency); + filesWithDependencies.forEach((fileWithDependency) -> { + updateDependencyInlineVersion(fileWithDependency, dependency); + updateDependencyWithVersionVariable(fileWithDependency, gradlePropertiesFile, dependency); + }); + }); + + // commit + DependencyOutdated firstDependency = outdated.get(0); + String updatedVersion = updatedVersion(firstDependency); + String title = outdated.size() == 1 ? "Update " + firstDependency.getName() + " to " + updatedVersion : "Update " + firstDependency.getGroup() + " to " + updatedVersion; + afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResult); + }); + } + + private void afterGroup(UpdateDependenciesExtension updateDependenciesExtension, File rootDir, String title, Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono) { + + String commitMessage = title; + if (updateDependenciesExtension.getUpdateMode() == UpdateDependenciesExtension.UpdateMode.GITHUB_ISSUE) { + GitHubApi.FindCreateIssueResult createIssueResult = createIssueResultMono.block(); + RateLimitQuery.RateLimit rateLimit = gitHubApi.findRateLimit().block(); + rateLimit = gitHubApi.findRateLimit().block(); + System.out.println("remaining " + rateLimit.remaining() + " reset at " + rateLimit.resetAt()); + Integer issueNumber = gitHubApi.createIssue(createIssueResult.getRepositoryId(), title, createIssueResult.getLabelIds(), createIssueResult.getMilestoneId(), createIssueResult.getAssigneeId()).delayElement(Duration.ofSeconds(1)).block(); + commitMessage += "\n\nCloses gh-" + issueNumber; + } + runCommand(rootDir, "git", "commit", "-am", commitMessage); + } + + private Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono(UpdateDependenciesExtension updateDependenciesExtension) { + return Mono.defer(() -> { + UpdateDependenciesExtension.GitHub gitHub = updateDependenciesExtension.getGitHub(); + return gitHubApi.findCreateIssueInput(gitHub.getOrganization(), gitHub.getRepository(), gitHub.getMilestone()).cache(); + }); + } + + private void updateGradleVersion(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) { + GradleUpdateResult current = result.getGradle().getCurrent(); + GradleUpdateResult running = result.getGradle().getRunning(); + if (current.compareTo(running) > 0) { + String title = "Update Gradle to " + current.getVersion(); + System.out.println(title); + runCommand(project.getRootDir(), "./gradlew", "wrapper", "--gradle-version", current.getVersion(), "--no-daemon"); + afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResultMono(updateDependenciesSettings)); + } + } + + private static Supplier<List<File>> defaultFiles(Project project) { + return () -> { + List<File> result = new ArrayList<>(); + result.add(project.getBuildFile()); + project.getChildProjects().values().forEach((childProject) -> + result.add(childProject.getBuildFile()) + ); + result.add(project.getRootProject().file("buildSrc/build.gradle")); + return result; + }; + } + + static void runCommand(File dir, String... args) { + try { + Process process = new ProcessBuilder() + .directory(dir) + .command(args) + .start(); + writeLinesTo(process.getInputStream(), System.out); + writeLinesTo(process.getErrorStream(), System.out); + if (process.waitFor() != 0) { + new RuntimeException("Failed to run " + Arrays.toString(args)); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException("Failed to run " + Arrays.toString(args), e); + } + } + + static void writeLinesTo(InputStream input, PrintStream out) { + Scanner scanner = new Scanner(input); + while(scanner.hasNextLine()) { + out.println(scanner.nextLine()); + } + } + + + static Action<ComponentSelectionWithCurrent> excludeWithRegex(String regex, String reason) { + Pattern pattern = Pattern.compile(regex); + return (selection) -> { + String candidateVersion = selection.getCandidate().getVersion(); + if (pattern.matcher(candidateVersion).matches()) { + selection.reject(candidateVersion + " is not allowed because it is " + reason); + } + }; + } + + static void updateDependencyInlineVersion(File buildFile, DependencyOutdated dependency){ + String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; + String originalDependency = ga + dependency.getVersion(); + String replacementDependency = ga + updatedVersion(dependency); + replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency)); + } + + static void replaceFileText(File file, Function<String, String> replaceText) { + String buildFileText = readString(file); + String updatedBuildFileText = replaceText.apply(buildFileText); + writeString(file, updatedBuildFileText); + } + + private static String readString(File file) { + try { + byte[] bytes = Files.readAllBytes(file.toPath()); + return new String(bytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void writeString(File file, String text) { + try { + Files.write(file.toPath(), text.getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static void updateDependencyWithVersionVariable(File scanFile, File gradlePropertiesFile, DependencyOutdated dependency) { + if (!gradlePropertiesFile.exists()) { + return; + } + replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> { + String ga = dependency.getGroup() + ":" + dependency.getName() + ":"; + Pattern pattern = Pattern.compile("\"" + ga + "\\$\\{?([^'\"]+?)\\}?\""); + String buildFileText = readString(scanFile); + Matcher matcher = pattern.matcher(buildFileText); + while (matcher.find()) { + String versionVariable = matcher.group(1); + gradlePropertiesText = gradlePropertiesText.replace(versionVariable + "=" + dependency.getVersion(), versionVariable + "=" + updatedVersion(dependency)); + } + return gradlePropertiesText; + }); + } + + private static String updatedVersion(DependencyOutdated dependency) { + VersionAvailable available = dependency.getAvailable(); + String release = available.getRelease(); + if (release != null) { + return release; + } + return available.getMilestone(); + } +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties new file mode 100644 index 00000000000..573d128134d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.artifactory.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.ArtifactoryPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties new file mode 100644 index 00000000000..dec2e7a8078 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.bom.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.MavenBomPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties new file mode 100644 index 00000000000..9259a914db7 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.checkstyle.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.CheckstylePlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties new file mode 100644 index 00000000000..497ecd5908d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.dependency-set.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.DependencySetPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties new file mode 100644 index 00000000000..fcad41ab0b8 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.docs.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.DocsPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties new file mode 100644 index 00000000000..0da39b33090 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.integration-test.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.IntegrationTestPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties new file mode 100644 index 00000000000..70cfb7d0d8f --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.jacoco.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JacocoPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties new file mode 100644 index 00000000000..bfb92306a2a --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-api.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JavadocApiPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties new file mode 100644 index 00000000000..77e7832e44a --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.javadoc-options.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.JavadocOptionsPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.maven.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.maven.properties new file mode 100644 index 00000000000..a75655edad9 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.maven.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringMavenPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.ossrh.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.ossrh.properties new file mode 100644 index 00000000000..567272412a8 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.ossrh.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.OssrhPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties new file mode 100644 index 00000000000..b427d5521df --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.repository.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.RepositoryConventionPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties new file mode 100644 index 00000000000..9844db6244f --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.root.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.RootProjectPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties new file mode 100644 index 00000000000..124dd85f947 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-module.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringModulePlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-pom.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-pom.properties new file mode 100644 index 00000000000..e59252be871 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-pom.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringPomPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties new file mode 100644 index 00000000000..4881e2d0705 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.spring-test.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.SpringTestPlugin \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties new file mode 100644 index 00000000000..afd0a712ade --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.tests-configuration.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.TestsConfigurationPlugin \ No newline at end of file diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/DependencySetPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/DependencySetPluginITest.groovy new file mode 100644 index 00000000000..43efa0a2d04 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/DependencySetPluginITest.groovy @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class DependencySetPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "dependencies"() { + when: + BuildResult result = testKit.withProjectResource("samples/dependencyset") + .withArguments('dependencies') + .build(); + then: + result.task(":dependencies").outcome == SUCCESS + !result.output.contains("FAILED") + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/DocsPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/DocsPluginITest.groovy new file mode 100644 index 00000000000..99ae2d6473b --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/DocsPluginITest.groovy @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import java.util.zip.ZipFile + +import static org.gradle.testkit.runner.TaskOutcome.FAILED +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class DocsPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "build triggers docs"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/simple/") + .withArguments('build') + .build(); + then: + result.task(":build").outcome == SUCCESS + and: + result.task(":docs").outcome == SUCCESS + and: + result.task(":docsZip").outcome == SUCCESS + and: + def zip = new File(testKit.getRootDir(), 'build/distributions/simple-1.0.0.BUILD-SNAPSHOT-docs.zip') + def names = new ZipFile(zip).entries()*.name + names.contains("docs/reference/html5/index.html") + names.contains("docs/reference/pdf/simple-reference.pdf") + } + + def "asciidoc copies images"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/simple/") + .withArguments('asciidoctor') + .build(); + then: + result.task(":asciidoctor").outcome == SUCCESS + new File(testKit.getRootDir(), "build/docs/asciidoc/images").exists() + } + + def "asciidoc docinfo from resources used"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/simple/") + .withArguments('asciidoctor') + .build(); + then: + result.task(":asciidoctor").outcome == SUCCESS + new File(testKit.getRootDir(), "build/docs/asciidoc/index.html").getText().contains("""<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>""") + } + + def "missing attribute fails"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/missing-attribute/") + .withArguments(':asciidoctor') + .buildAndFail(); + then: + result.task(":asciidoctor").outcome == FAILED + } + + def "missing include"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/missing-include/") + .withArguments(':asciidoctor') + .buildAndFail(); + then: + result.task(":asciidoctor").outcome == FAILED + } + + def "missing cross reference"() { + when: + BuildResult result = testKit.withProjectResource("samples/docs/missing-cross-reference/") + .withArguments(':asciidoctor') + .buildAndFail(); + then: + result.task(":asciidoctor").outcome == FAILED + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/IntegrationTestPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/IntegrationTestPluginITest.groovy new file mode 100644 index 00000000000..f31ed287f21 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/IntegrationTestPluginITest.groovy @@ -0,0 +1,63 @@ +/* + * Copyright 2016-2018 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class IntegrationTestPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "check with java plugin"() { + when: + BuildResult result = testKit.withProjectResource("samples/integrationtest/withjava/") + .withArguments('check') + .build(); + then: + result.task(":check").outcome == SUCCESS + and: + new File(testKit.getRootDir(), 'build/test-results/integrationTest/').exists() + new File(testKit.getRootDir(), 'build/reports/tests/integrationTest/').exists() + } + + def "check with propdeps"() { + when: + BuildResult result = testKit.withProjectResource("samples/integrationtest/withpropdeps/") + .withArguments('check') + .build(); + then: + result.task(":check").outcome == SUCCESS + and: + new File(testKit.getRootDir(), 'build/test-results/integrationTest/').exists() + new File(testKit.getRootDir(), 'build/reports/tests/integrationTest/').exists() + } + + def "check with groovy plugin"() { + when: + BuildResult result = testKit.withProjectResource("samples/integrationtest/withgroovy/") + .withArguments('check') + .build(); + then: + result.task(":check").outcome == SUCCESS + and: + new File(testKit.getRootDir(), 'build/test-results/integrationTest/').exists() + new File(testKit.getRootDir(), 'build/reports/tests/integrationTest/').exists() + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/JacocoPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/JacocoPluginITest.groovy new file mode 100644 index 00000000000..cda62362e62 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/JacocoPluginITest.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class JacocoPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "check with java plugin"() { + when: + BuildResult result = testKit.withProjectResource("samples/jacoco/java/") + .withArguments('check') + .build(); + then: + result.task(":check").outcome == SUCCESS + and: + new File(testKit.getRootDir(), 'build/jacoco').exists() + new File(testKit.getRootDir(), 'build/reports/jacoco/test/html/').exists() + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/JavadocApiPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/JavadocApiPluginITest.groovy new file mode 100644 index 00000000000..a7281315525 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/JavadocApiPluginITest.groovy @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.*; + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.apache.commons.io.FileUtils + +class JavadocApiPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "multimodule api"() { + when: + BuildResult result = testKit.withProjectResource("samples/javadocapi/multimodule/") + .withArguments('api') + .build(); + then: + result.task(":api").outcome == SUCCESS + and: + File allClasses = new File(testKit.getRootDir(), 'build/api/allclasses-noframe.html'); + File index = new File(testKit.getRootDir(), 'build/api/allclasses.html'); + new File(testKit.getRootDir(), "build/api/").listFiles().each { println it } + File listing = allClasses.exists() ? allClasses : index + listing.text.contains('sample/Api.html') + listing.text.contains('sample/Impl.html') + !listing.text.contains('sample/Sample.html') + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/ShowcaseITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/ShowcaseITest.groovy new file mode 100644 index 00000000000..6001b9daf74 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/ShowcaseITest.groovy @@ -0,0 +1,124 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import spock.lang.Specification + +class ShowcaseITest extends Specification { + + @Rule final TestKit testKit = new TestKit() + + def "build"() { + when: + BuildResult result = testKit.withProjectResource("samples/showcase/") + .withArguments('build','--stacktrace') + .forwardOutput() + .build(); + then: 'entire build passes' + result.output.contains("BUILD SUCCESSFUL") + } + + def "install"() { + when: + BuildResult result = testKit.withProjectResource("samples/showcase/") + .withArguments('install','--stacktrace') + .build(); + then: + result.output.contains("SUCCESS") + + and: 'pom exists' + File pom = new File(testKit.getRootDir(), 'sgbcs-core/build/poms/pom-default.xml') + pom.exists() + String pomText = pom.getText() + + and: 'pom does not contain <dependencyManagement>' + !pomText.contains('<dependencyManagement>') + + and: 'creates optional dependencies correctly' + pomText.replaceAll('\\s','').contains("""<dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <scope>test</scope> + <version>4.3.6.RELEASE</version> + </dependency>""".replaceAll('\\s','')) + + and: 'adds author' + pomText.replaceAll('\\s','').contains("""<developers> + <developer> + <id>rwinch</id> + <name>Rob Winch</name> + <email>rwinch@pivotal.io</email> + </developer> + <developer> + <id>jgrandja</id> + <name>Joe Grandja</name> + <email>jgrandja@pivotal.io</email> + </developer> + </developers>""".replaceAll('\\s','')) + + and: 'adds repositories' + pomText.replaceAll('\\s','').contains("""<scm> + <connection>scm:git:git://github.com/spring-projects/spring-security</connection> + <developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection> + <url>https://github.com/spring-projects/spring-security</url> + </scm>""".replaceAll('\\s','')) + + and: 'adds description & url' + pomText.contains('<description>sgbcs-core</description>') + pomText.contains('<url>https://spring.io/spring-security</url>') + + and: 'adds organization' + pomText.replaceAll('\\s','').contains('''<organization> + <name>spring.io</name> + <url>https://spring.io/</url> + </organization>'''.replaceAll('\\s','')) + + and: 'adds licenses' + pomText.replaceAll('\\s','').contains(''' <licenses> + <license> + <name>The Apache Software License, Version 2.0</name> + <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + </license> + </licenses>'''.replaceAll('\\s','')) + + and: 'adds scm' + pomText.replaceAll('\\s','').replaceAll('\\s','').contains("""<scm> + <connection>scm:git:git://github.com/spring-projects/spring-security</connection> + <developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection> + <url>https://github.com/spring-projects/spring-security</url> + </scm>""".replaceAll('\\s','')) + + and: 'bom created' + File bom = new File(testKit.getRootDir(), 'bom/build/poms/pom-default.xml') + bom.exists() + String bomText = bom.getText() + bomText.contains("""<artifactId>sgbcs-core</artifactId>""") + + when: 'mavenBom ran again' + result = testKit.withProjectResource("samples/showcase/") + .withArguments('mavenBom','--stacktrace') + .build(); + then: 'mavenBom is not up to date since install is never up to date' + result.task(':bom:mavenBom').getOutcome() == TaskOutcome.SUCCESS + } + +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/SpringMavenPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/SpringMavenPluginITest.groovy new file mode 100644 index 00000000000..1415d8f5b21 --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/SpringMavenPluginITest.groovy @@ -0,0 +1,88 @@ +/* + * Copyright 2016-2019 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class SpringMavenPluginITest extends Specification { + + @Rule final TestKit testKit = new TestKit() + + def "install"() { + when: + BuildResult result = testKit.withProjectResource("samples/maven/install") + .withArguments('install') + .build(); + then: 'pom contains optional' + result.output.contains("SUCCESS") + File pom = new File(testKit.getRootDir(), 'build/poms/pom-default.xml') + pom.exists() + String pomText = pom.getText() + pomText.replaceAll('\\s','').contains("""<dependency> + <groupId>aopalliance</groupId> + <artifactId>aopalliance</artifactId> + <version>1.0</version> + <scope>compile</scope> + <optional>true</optional> + </dependency>""".replaceAll('\\s','')) + } + + def "signArchives when in memory"() { + when: + BuildResult result = testKit.withProjectResource("samples/maven/signing") + .withArguments('signArchives') + .withEnvironment(["ORG_GRADLE_PROJECT_signingKey" : signingKey, + "ORG_GRADLE_PROJECT_signingPassword" : "password"]) + .forwardOutput() + .build(); + then: + result.output.contains("SUCCESS") + File jar = new File(testKit.getRootDir(), 'build/libs/signing-1.0.0.RELEASE.jar') + jar.exists() + File signature = new File("${jar.absolutePath}.asc") + signature.exists() + } + + def "upload"() { + when: + BuildResult result = testKit.withProjectResource("samples/maven/upload") + .withArguments('uploadArchives') + .forwardOutput() + .build(); + then: 'pom contains optional' + result.output.contains("SUCCESS") + File pom = new File(testKit.getRootDir(), 'build/poms/pom-default.xml') + pom.exists() + String pomText = pom.getText() + pomText.replaceAll('\\s','').contains("""<dependency> + <groupId>aopalliance</groupId> + <artifactId>aopalliance</artifactId> + <version>1.0</version> + <scope>compile</scope> + <optional>true</optional> + </dependency>""".replaceAll('\\s','')) + } + + def getSigningKey() { + getClass().getResource("/test-private.pgp").text + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/convention/TestsConfigurationPluginITest.groovy b/buildSrc/src/test/groovy/io/spring/gradle/convention/TestsConfigurationPluginITest.groovy new file mode 100644 index 00000000000..045e595841b --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/convention/TestsConfigurationPluginITest.groovy @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention + +import io.spring.gradle.testkit.junit.rules.TestKit +import org.gradle.testkit.runner.BuildResult +import org.junit.Rule +import spock.lang.Specification + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class TestsConfigurationPluginITest extends Specification { + @Rule final TestKit testKit = new TestKit() + + def "can find dependency"() { + when: + BuildResult result = testKit.withProjectResource("samples/testsconfiguration") + .withArguments('check') + .build(); + then: + result.task(":web:check").outcome == SUCCESS + } +} diff --git a/buildSrc/src/test/groovy/io/spring/gradle/testkit/junit/rules/TestKit.java b/buildSrc/src/test/groovy/io/spring/gradle/testkit/junit/rules/TestKit.java new file mode 100644 index 00000000000..f9b5385540b --- /dev/null +++ b/buildSrc/src/test/groovy/io/spring/gradle/testkit/junit/rules/TestKit.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.testkit.junit.rules; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Enumeration; + +import org.apache.commons.io.FileUtils; +import org.gradle.testkit.runner.GradleRunner; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class TestKit implements TestRule { + final TemporaryFolder testProjectDir = new TemporaryFolder(); + File buildDir; + + @Override + public Statement apply(Statement base, Description description) { + Statement wrapped = new Statement() { + + @Override + public void evaluate() throws Throwable { + try { + buildDir = testProjectDir.newFolder(); + } catch(IOException e) { + throw new RuntimeException(e); + } + base.evaluate(); + } + }; + return testProjectDir.apply(wrapped, description); + } + + public File getRootDir() { + return buildDir; + } + + public GradleRunner withProjectDir(File projectDir) throws IOException { + FileUtils.copyDirectory(projectDir, buildDir); + return GradleRunner.create() + .withProjectDir(buildDir) + .withPluginClasspath(); + } + + public GradleRunner withProjectResource(String projectResourceName) throws IOException, URISyntaxException { + ClassLoader classLoader = getClass().getClassLoader(); + Enumeration<URL> resources = classLoader.getResources(projectResourceName); + if(!resources.hasMoreElements()) { + throw new IOException("Cannot find resource " + projectResourceName + " with " + classLoader); + } + URL resourceUrl = resources.nextElement(); + File projectDir = Paths.get(resourceUrl.toURI()).toFile(); + return withProjectDir(projectDir); + } +} diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/IntegrationPluginTest.java b/buildSrc/src/test/java/io/spring/gradle/convention/IntegrationPluginTest.java new file mode 100644 index 00000000000..e8a613c65c3 --- /dev/null +++ b/buildSrc/src/test/java/io/spring/gradle/convention/IntegrationPluginTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.apache.commons.io.FileUtils; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.After; +import org.junit.Test; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Rob Winch + */ +public class IntegrationPluginTest { + Project rootProject; + + @After + public void cleanup() throws Exception { + if (rootProject != null) { + FileUtils.deleteDirectory(rootProject.getProjectDir()); + } + } + + @Test + public void applyWhenNoSourceThenIntegrationTestTaskNull() { + rootProject = ProjectBuilder.builder().build(); + rootProject.getPlugins().apply(JavaPlugin.class); + rootProject.getPlugins().apply(IntegrationTestPlugin.class); + + assertThat(rootProject.getTasks().findByPath("integrationTest")).isNull(); + } + +} diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginTest.java b/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginTest.java new file mode 100644 index 00000000000..ddcd3c9191b --- /dev/null +++ b/buildSrc/src/test/java/io/spring/gradle/convention/JavadocApiPluginTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import java.io.File; + +import org.apache.commons.io.FileUtils; +import static org.assertj.core.api.Assertions.assertThat; +import org.gradle.api.Project; +import org.gradle.api.tasks.javadoc.Javadoc; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.After; +import org.junit.Test; + +/** + * @author Rob Winch + */ +public class JavadocApiPluginTest { + Project rootProject; + + @After + public void cleanup() throws Exception { + if (rootProject != null) { + FileUtils.deleteDirectory(rootProject.getProjectDir()); + } + } + + @Test + public void applyWhenNotOverrideThenPropertiesDefaulted() { + rootProject = ProjectBuilder.builder().build(); + rootProject.getPlugins().apply(JavadocApiPlugin.class); + + Javadoc apiTask = (Javadoc) rootProject.getTasks().getByPath("api"); + + assertThat(apiTask).isNotNull(); + assertThat(apiTask.getGroup()).isEqualTo("Documentation"); + assertThat(apiTask.getDescription()).isEqualTo("Generates aggregated Javadoc API documentation."); + assertThat(apiTask.getMaxMemory()).isEqualTo("1024m"); + assertThat(apiTask.getDestinationDir()).isEqualTo(new File(rootProject.getBuildDir(), "api")); + } + +} diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/RepositoryConventionPluginTests.java b/buildSrc/src/test/java/io/spring/gradle/convention/RepositoryConventionPluginTests.java new file mode 100644 index 00000000000..5ce1613c789 --- /dev/null +++ b/buildSrc/src/test/java/io/spring/gradle/convention/RepositoryConventionPluginTests.java @@ -0,0 +1,158 @@ +/* + * Copyright 2016-2018 the original author or authors. + * + * 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 + * + * https://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 io.spring.gradle.convention; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.ArtifactRepository; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.gradle.api.plugins.ExtraPropertiesExtension; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RepositoryConventionPlugin}. + */ +public class RepositoryConventionPluginTests { + + private Project project = ProjectBuilder.builder().build(); + + @Before + public void setUp() { + this.project.getProperties().clear(); + } + + @Test + public void applyWhenIsReleaseThenShouldIncludeReleaseRepo() { + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertReleaseRepository(repositories); + } + + @Test + public void applyWhenIsMilestoneThenShouldIncludeMilestoneRepo() { + this.project.setVersion("1.0.0.M1"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertMilestoneRepository(repositories); // milestone + } + + @Test + public void applyWhenIsSnapshotThenShouldIncludeSnapshotRepo() { + this.project.setVersion("1.0.0.BUILD-SNAPSHOT"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertSnapshotRepository(repositories); + } + + @Test + public void applyWhenIsSnapshotWithForceReleaseThenShouldOnlyIncludeReleaseRepo() { + this.project.getExtensions().getByType(ExtraPropertiesExtension.class) + .set("forceMavenRepositories", "release"); + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertReleaseRepository(repositories); + } + + @Test + public void applyWhenIsReleaseWithForceMilestoneThenShouldIncludeMilestoneRepo() { + this.project.getExtensions().getByType(ExtraPropertiesExtension.class) + .set("forceMavenRepositories", "milestone"); + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertMilestoneRepository(repositories); + } + + @Test + public void applyWhenIsReleaseWithForceSnapshotThenShouldIncludeSnapshotRepo() { + this.project.getExtensions().getByType(ExtraPropertiesExtension.class) + .set("forceMavenRepositories", "snapshot"); + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertSnapshotRepository(repositories); + } + + @Test + public void applyWhenIsReleaseWithForceLocalThenShouldIncludeReleaseAndLocalRepos() { + this.project.getExtensions().getByType(ExtraPropertiesExtension.class) + .set("forceMavenRepositories", "local"); + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertThat(repositories).hasSize(5); + assertThat((repositories.get(0)).getName()).isEqualTo("MavenLocal"); + } + + @Test + public void applyWhenIsReleaseWithForceMilestoneAndLocalThenShouldIncludeMilestoneAndLocalRepos() { + this.project.getExtensions().getByType(ExtraPropertiesExtension.class) + .set("forceMavenRepositories", "milestone,local"); + this.project.setVersion("1.0.0.RELEASE"); + this.project.getPluginManager().apply(RepositoryConventionPlugin.class); + + RepositoryHandler repositories = this.project.getRepositories(); + assertThat(repositories).hasSize(6); + assertThat((repositories.get(0)).getName()).isEqualTo("MavenLocal"); + } + + private void assertSnapshotRepository(RepositoryHandler repositories) { + assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(6); + assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString()) + .isEqualTo("https://repo.maven.apache.org/maven2/"); + assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString()) + .isEqualTo("https://jcenter.bintray.com/"); + assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString()) + .isEqualTo("https://repo.spring.io/snapshot/"); + assertThat(((MavenArtifactRepository) repositories.get(3)).getUrl().toString()) + .isEqualTo("https://repo.spring.io/milestone/"); + } + + private void assertMilestoneRepository(RepositoryHandler repositories) { + assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(5); + assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString()) + .isEqualTo("https://repo.maven.apache.org/maven2/"); + assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString()) + .isEqualTo("https://jcenter.bintray.com/"); + assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString()) + .isEqualTo("https://repo.spring.io/milestone/"); + } + + private void assertReleaseRepository(RepositoryHandler repositories) { + assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(4); + assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString()) + .isEqualTo("https://repo.maven.apache.org/maven2/"); + assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString()) + .isEqualTo("https://jcenter.bintray.com/"); + assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString()) + .isEqualTo("https://repo.spring.io/release/"); + } + +} diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/UtilsTest.java b/buildSrc/src/test/java/io/spring/gradle/convention/UtilsTest.java new file mode 100644 index 00000000000..76fbea38701 --- /dev/null +++ b/buildSrc/src/test/java/io/spring/gradle/convention/UtilsTest.java @@ -0,0 +1,151 @@ +package io.spring.gradle.convention; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import org.gradle.api.Project; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class UtilsTest { + @Mock + Project project; + @Mock + Project rootProject; + + @Before + public void setup() { + when(project.getRootProject()).thenReturn(rootProject); + } + + @Test + public void getProjectName() { + when(rootProject.getName()).thenReturn("spring-security"); + + assertThat(Utils.getProjectName(project)).isEqualTo("spring-security"); + } + + @Test + public void getProjectNameWhenEndsWithBuildThenStrippedOut() { + when(rootProject.getName()).thenReturn("spring-security-build"); + + assertThat(Utils.getProjectName(project)).isEqualTo("spring-security"); + } + + @Test + public void isSnapshotValidWithDot() { + when(project.getVersion()).thenReturn("1.0.0.BUILD-SNAPSHOT"); + + assertThat(Utils.isSnapshot(project)).isTrue(); + } + + @Test + public void isSnapshotValidWithNoBuild() { + when(project.getVersion()).thenReturn("1.0.0-SNAPSHOT"); + + assertThat(Utils.isSnapshot(project)).isTrue(); + } + + @Test + public void isSnapshotValidWithDash() { + when(project.getVersion()).thenReturn("Theme-BUILD-SNAPSHOT"); + + assertThat(Utils.isSnapshot(project)).isTrue(); + } + + @Test + public void isSnapshotInvalid() { + when(project.getVersion()).thenReturn("1.0.0.SNAPSHOT"); + + assertThat(Utils.isSnapshot(project)).isFalse(); + } + + @Test + public void isMilestoneValidWithDot() { + when(project.getVersion()).thenReturn("1.0.0.M1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isMilestoneValidWithDash() { + when(project.getVersion()).thenReturn("Theme-M1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isMilestoneValidWithNumberDash() { + when(project.getVersion()).thenReturn("1.0.0-M1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isMilestoneInvalid() { + when(project.getVersion()).thenReturn("1.0.0.M"); + + assertThat(Utils.isMilestone(project)).isFalse(); + } + + @Test + public void isReleaseCandidateValidWithDot() { + when(project.getVersion()).thenReturn("1.0.0.RC1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isReleaseCandidateValidWithNumberDash() { + when(project.getVersion()).thenReturn("1.0.0-RC1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isReleaseCandidateValidWithDash() { + when(project.getVersion()).thenReturn("Theme-RC1"); + + assertThat(Utils.isMilestone(project)).isTrue(); + } + + @Test + public void isReleaseCandidateInvalid() { + when(project.getVersion()).thenReturn("1.0.0.RC"); + + assertThat(Utils.isMilestone(project)).isFalse(); + } + + @Test + public void isReleaseValidWithDot() { + when(project.getVersion()).thenReturn("1.0.0.RELEASE"); + + assertThat(Utils.isRelease(project)).isTrue(); + } + + @Test + public void isReleaseValidWithNoRelease() { + when(project.getVersion()).thenReturn("1.0.0"); + + assertThat(Utils.isRelease(project)).isTrue(); + } + + @Test + public void isReleaseValidWithDash() { + when(project.getVersion()).thenReturn("Theme-RELEASE"); + + assertThat(Utils.isRelease(project)).isTrue(); + } + + @Test + public void isServiceReleaseValid() { + when(project.getVersion()).thenReturn("Theme-SR1"); + + assertThat(Utils.isRelease(project)).isTrue(); + } +} diff --git a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/Service.java b/buildSrc/src/test/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtilsTest.java similarity index 58% rename from samples/javaconfig/aspectj/src/main/java/sample/aspectj/Service.java rename to buildSrc/src/test/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtilsTest.java index 70b8c0c71f9..34cf5e25fb3 100644 --- a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/Service.java +++ b/buildSrc/src/test/java/org/springframework/security/convention/versions/TransitiveDependencyLookupUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2019-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,25 +14,18 @@ * limitations under the License. */ -package sample.aspectj; +package org.springframework.security.convention.versions; -import org.springframework.security.access.annotation.Secured; -/** - * Service which is secured on method level - * - * @author Mike Wiesner - * @since 1.0 - */ -public class Service { +import org.junit.Test; - @Secured("ROLE_USER") - public void secureMethod() { - // nothing - } +import static org.assertj.core.api.Assertions.assertThat; - public void publicMethod() { - // nothing - } +public class TransitiveDependencyLookupUtilsTest { + @Test + public void lookupJwtVersionWhen93Then961() { + String s = TransitiveDependencyLookupUtils.lookupJwtVersion("9.3"); + assertThat(s).isEqualTo("9.6.1"); + } } diff --git a/buildSrc/src/test/resources/samples/dependencyset/build.gradle b/buildSrc/src/test/resources/samples/dependencyset/build.gradle new file mode 100644 index 00000000000..c4327c7e8cf --- /dev/null +++ b/buildSrc/src/test/resources/samples/dependencyset/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'io.spring.convention.dependency-set' +} + +apply plugin: 'java' +apply from: "$rootDir/gradle/dependency-management.gradle" + +repositories { + mavenCentral() +} + +dependencies { + testCompile spockDependencies + testCompile gebDependencies + testCompile powerMockDependencies + testCompile seleniumDependencies + testCompile slf4jDependencies + testCompile springCoreDependency + testCompile jstlDependencies + testCompile apachedsDependencies +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/dependencyset/gradle/dependency-management.gradle b/buildSrc/src/test/resources/samples/dependencyset/gradle/dependency-management.gradle new file mode 100644 index 00000000000..032446af5f6 --- /dev/null +++ b/buildSrc/src/test/resources/samples/dependencyset/gradle/dependency-management.gradle @@ -0,0 +1,724 @@ +// we use this to mock dependency management plugin + +def deps = [ + "antlr:antlr" : "2.7.7", + "aopalliance:aopalliance" : "1.0", + "biz.paluch.redis:lettuce" : "5.0.0.Beta1", + "ch.qos.logback:logback-access" : "1.1.9", + "ch.qos.logback:logback-classic" : "1.1.9", + "ch.qos.logback:logback-core" : "1.1.9", + "com.atomikos:transactions-jdbc" : "3.9.3", + "com.atomikos:transactions-jms" : "3.9.3", + "com.atomikos:transactions-jta" : "3.9.3", + "com.caucho:hessian" : "4.0.38", + "com.couchbase.client:couchbase-spring-cache" : "2.1.0", + "com.couchbase.client:java-client" : "2.3.7", + "com.datastax.cassandra:cassandra-driver-core" : "3.1.3", + "com.datastax.cassandra:cassandra-driver-mapping" : "3.1.3", + "com.esotericsoftware:kryo" : "3.0.3", + "com.esotericsoftware:kryo-shaded" : "3.0.3", + "com.fasterxml:classmate" : "1.3.3", + "com.fasterxml.jackson.core:jackson-annotations" : "2.8.0", + "com.fasterxml.jackson.core:jackson-core" : "2.8.6", + "com.fasterxml.jackson.core:jackson-databind" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-avro" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-csv" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-properties" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-protobuf" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-smile" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-xml" : "2.8.6", + "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-guava" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-hibernate3" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-hibernate4" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-hppc" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-jaxrs" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-jdk8" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-joda" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-json-org" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-jsr353" : "2.8.6", + "com.fasterxml.jackson.datatype:jackson-datatype-pcollections" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-cbor-provider" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-smile-provider" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-xml-provider" : "2.8.6", + "com.fasterxml.jackson.jaxrs:jackson-jaxrs-yaml-provider" : "2.8.6", + "com.fasterxml.jackson.jr:jackson-jr-all" : "2.8.6", + "com.fasterxml.jackson.jr:jackson-jr-objects" : "2.8.6", + "com.fasterxml.jackson.jr:jackson-jr-retrofit2" : "2.8.6", + "com.fasterxml.jackson.jr:jackson-jr-stree" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-afterburner" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-guice" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-jaxb-annotations" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-jsonSchema" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-kotlin" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-mrbean" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-osgi" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-parameter-names" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-paranamer" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-scala_2.10" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-scala_2.11" : "2.8.6", + "com.fasterxml.jackson.module:jackson-module-scala_2.12" : "2.8.6", + "com.gemstone.gemfire:gemfire" : "8.2.0", + "com.github.ben-manes.caffeine:caffeine" : "2.3.5", + "com.github.mxab.thymeleaf.extras:thymeleaf-extras-data-attribute" : "1.3", + "com.github.spullara.redis:client" : "0.7", + "com.goldmansachs:gs-collections" : "5.1.0", + "com.google.appengine:appengine-api-1.0-sdk" : "1.9.48", + "com.google.code.findbugs:annotations" : "2.0.3", + "com.google.code.findbugs:jsr305" : "3.0.1", + "com.google.code.gson:gson" : "2.8.0", + "com.google.code.typica:typica" : "1.3", + "com.google.guava:guava" : "20.0", + "com.google.inject:guice" : "3.0", + "com.google.protobuf:protobuf-java" : "2.6.1", + "com.googlecode.json-simple:json-simple" : "1.1.1", + "com.googlecode.protobuf-java-format:protobuf-java-format" : "1.4", + "com.h2database:h2" : "1.4.193", + "com.hazelcast:hazelcast" : "3.7.5", + "com.hazelcast:hazelcast-client" : "3.7.5", + "com.hazelcast:hazelcast-hibernate4" : "3.7.1", + "com.hazelcast:hazelcast-hibernate5" : "1.1.3", + "com.hazelcast:hazelcast-spring" : "3.7.5", + "com.ibm.jbatch:com.ibm.jbatch-tck-spi" : "1.0", + "com.ibm.websphere:uow" : "6.0.2.17", + "com.jamonapi:jamon" : "2.81", + "com.jayway.jsonpath:json-path" : "2.2.0", + "com.jayway.jsonpath:json-path-assert" : "2.2.0", + "com.jayway.restassured:rest-assured" : "2.9.0", + "com.jcraft:jsch" : "0.1.54", + "com.lowagie:itext" : "2.1.7", + "com.maxmind.geoip2:geoip2" : "2.3.1", + "com.mchange:c3p0" : "0.9.5.2", + "com.microsoft.sqlserver:mssql-jdbc" : "6.1.0.jre7", + "com.querydsl:querydsl-apt" : "4.1.4", + "com.querydsl:querydsl-collections" : "4.1.4", + "com.querydsl:querydsl-core" : "4.1.4", + "com.querydsl:querydsl-jpa" : "4.1.4", + "com.querydsl:querydsl-mongodb" : "4.1.4", + "com.rabbitmq:amqp-client" : "4.0.2", + "com.rabbitmq:http-client" : "1.1.0.RELEASE", + "com.rometools:rome" : "1.6.1", + "com.rometools:rome-fetcher" : "1.6.1", + "com.samskivert:jmustache" : "1.13", + "com.sendgrid:sendgrid-java" : "2.2.2", + "com.splunk:splunk" : "1.3.0", + "com.squareup.okhttp:okhttp" : "2.7.5", + "com.squareup.okhttp3:okhttp" : "3.6.0", + "com.sun:ldapbp" : "1.0", + "com.sun.facelets:jsf-facelets" : "1.1.14", + "com.sun.faces:jsf-api" : "2.2.14", + "com.sun.faces:jsf-impl" : "2.2.14", + "com.sun.mail:imap" : "1.5.6", + "com.sun.mail:javax.mail" : "1.5.6", + "com.sun.xml.messaging.saaj:saaj-impl" : "1.3.28", + "com.sun.xml.wss:xws-security" : "3.0", + "com.thoughtworks.xstream:xstream" : "1.4.9", + "com.timgroup:java-statsd-client" : "3.1.0", + "com.unboundid:unboundid-ldapsdk" : "3.2.0", + "com.zaxxer:HikariCP" : "2.5.1", + "com.zaxxer:HikariCP-java6" : "2.3.13", + "commons-beanutils:commons-beanutils" : "1.9.3", + "commons-cli:commons-cli" : "1.3.1", + "commons-codec:commons-codec" : "1.10", + "commons-collections:commons-collections" : "3.2.2", + "commons-dbcp:commons-dbcp" : "1.4", + "commons-digester:commons-digester" : "2.1", + "commons-fileupload:commons-fileupload" : "1.3.2", + "commons-httpclient:commons-httpclient" : "3.1", + "commons-io:commons-io" : "2.5", + "commons-lang:commons-lang" : "2.6", + "commons-logging:commons-logging" : "1.2", + "commons-net:commons-net" : "3.5", + "commons-pool:commons-pool" : "1.6", + "de.flapdoodle.embed:de.flapdoodle.embed.mongo" : "1.50.5", + "dom4j:dom4j" : "1.6.1", + "edu.umd.cs.mtc:multithreadedtc" : "1.01", + "io.dropwizard.metrics:metrics-core" : "3.1.2", + "io.dropwizard.metrics:metrics-ganglia" : "3.1.2", + "io.dropwizard.metrics:metrics-graphite" : "3.1.2", + "io.dropwizard.metrics:metrics-servlets" : "3.1.2", + "io.fastjson:boon" : "0.34", + "io.javaslang:javaslang" : "2.0.5", + "io.javaslang:javaslang-match" : "2.0.5", + "io.netty:netty-all" : "4.0.44.Final", + "io.projectreactor:reactor-bus" : "2.0.8.RELEASE", + "io.projectreactor:reactor-core" : "2.0.8.RELEASE", + "io.projectreactor:reactor-groovy" : "2.0.8.RELEASE", + "io.projectreactor:reactor-groovy-extensions" : "2.0.8.RELEASE", + "io.projectreactor:reactor-logback" : "2.0.8.RELEASE", + "io.projectreactor:reactor-net" : "2.0.8.RELEASE", + "io.projectreactor:reactor-stream" : "2.0.8.RELEASE", + "io.projectreactor.spring:reactor-spring-context" : "2.0.7.RELEASE", + "io.projectreactor.spring:reactor-spring-core" : "2.0.7.RELEASE", + "io.projectreactor.spring:reactor-spring-messaging" : "2.0.7.RELEASE", + "io.projectreactor.spring:reactor-spring-webmvc" : "2.0.7.RELEASE", + "io.searchbox:jest" : "2.0.4", + "io.undertow:undertow-core" : "1.4.8.Final", + "io.undertow:undertow-servlet" : "1.4.8.Final", + "io.undertow:undertow-websockets-jsr" : "1.4.8.Final", + "javax.activation:activation" : "1.1.1", + "javax.annotation:jsr250-api" : "1.0", + "javax.batch:javax.batch-api" : "1.0.1", + "javax.cache:cache-api" : "1.0.0", + "javax.ejb:javax.ejb-api" : "3.2", + "javax.el:javax.el-api" : "2.2.5", + "javax.enterprise:cdi-api" : "1.2", + "javax.enterprise.concurrent:javax.enterprise.concurrent-api" : "1.0", + "javax.faces:javax.faces-api" : "2.2", + "javax.inject:javax.inject" : "1", + "javax.interceptor:javax.interceptor-api" : "1.2", + "javax.jdo:jdo-api" : "3.0.1", + "javax.jms:jms-api" : "1.1-rev-1", + "javax.mail:javax.mail-api" : "1.5.6", + "javax.money:money-api" : "1.0.1", + "javax.portlet:portlet-api" : "2.0", + "javax.resource:connector-api" : "1.5", + "javax.servlet:javax.servlet-api" : "3.1.0", + "javax.servlet:jstl" : "1.2", + "javax.servlet.jsp:javax.servlet.jsp-api" : "2.3.1", + "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api" : "1.2.1", + "javax.transaction:javax.transaction-api" : "1.2", + "javax.validation:validation-api" : "1.1.0.Final", + "javax.websocket:javax.websocket-api" : "1.1", + "javax.ws.rs:javax.ws.rs-api" : "2.0.1", + "jaxen:jaxen" : "1.1.6", + "jline:jline" : "2.14.3", + "joda-time:joda-time" : "2.9.7", + "junit:junit" : "4.12", + "ldapsdk:ldapsdk" : "4.1", + "log4j:log4j" : "1.2.17", + "mysql:mysql-connector-java" : "5.1.40", + "net.java.dev.jna:jna" : "4.2.2", + "net.openhft:chronicle" : "3.4.4", + "net.openhft:lang" : "6.6.16", + "net.sf.ehcache:ehcache" : "2.10.3", + "net.sf.jasperreports:jasperreports" : "6.4.0", + "net.sf.jopt-simple:jopt-simple" : "5.0.3", + "net.sourceforge.htmlunit:htmlunit" : "2.21", + "net.sourceforge.jexcelapi:jxl" : "2.6.12", + "net.sourceforge.jtds:jtds" : "1.3.1", + "net.sourceforge.nekohtml:nekohtml" : "1.9.22", + "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect" : "1.4.0", + "opensymphony:ognl" : "2.6.11", + "org.apache.activemq:activemq-amqp" : "5.14.3", + "org.apache.activemq:activemq-blueprint" : "5.14.3", + "org.apache.activemq:activemq-broker" : "5.14.3", + "org.apache.activemq:activemq-camel" : "5.14.3", + "org.apache.activemq:activemq-client" : "5.14.3", + "org.apache.activemq:activemq-console" : "5.14.3", + "org.apache.activemq:activemq-http" : "5.14.3", + "org.apache.activemq:activemq-jaas" : "5.14.3", + "org.apache.activemq:activemq-jdbc-store" : "5.14.3", + "org.apache.activemq:activemq-jms-pool" : "5.14.3", + "org.apache.activemq:activemq-kahadb-store" : "5.14.3", + "org.apache.activemq:activemq-karaf" : "5.14.3", + "org.apache.activemq:activemq-leveldb-store" : "5.14.3", + "org.apache.activemq:activemq-log4j-appender" : "5.14.3", + "org.apache.activemq:activemq-mqtt" : "5.14.3", + "org.apache.activemq:activemq-openwire-generator" : "5.14.3", + "org.apache.activemq:activemq-openwire-legacy" : "5.14.3", + "org.apache.activemq:activemq-osgi" : "5.14.3", + "org.apache.activemq:activemq-partition" : "5.14.3", + "org.apache.activemq:activemq-pool" : "5.14.3", + "org.apache.activemq:activemq-ra" : "5.14.3", + "org.apache.activemq:activemq-run" : "5.14.3", + "org.apache.activemq:activemq-runtime-config" : "5.14.3", + "org.apache.activemq:activemq-shiro" : "5.14.3", + "org.apache.activemq:activemq-spring" : "5.14.3", + "org.apache.activemq:activemq-stomp" : "5.14.3", + "org.apache.activemq:activemq-web" : "5.14.3", + "org.apache.activemq:artemis-amqp-protocol" : "1.5.2", + "org.apache.activemq:artemis-commons" : "1.5.2", + "org.apache.activemq:artemis-core-client" : "1.5.2", + "org.apache.activemq:artemis-jms-client" : "1.5.2", + "org.apache.activemq:artemis-jms-server" : "1.5.2", + "org.apache.activemq:artemis-journal" : "1.5.2", + "org.apache.activemq:artemis-native" : "1.5.2", + "org.apache.activemq:artemis-selector" : "1.5.2", + "org.apache.activemq:artemis-server" : "1.5.2", + "org.apache.activemq:artemis-service-extensions" : "1.5.2", + "org.apache.commons:commons-dbcp2" : "2.1.1", + "org.apache.commons:commons-lang3" : "3.5", + "org.apache.commons:commons-pool2" : "2.4.2", + "org.apache.curator:curator-recipes" : "2.11.1", + "org.apache.derby:derby" : "10.13.1.1", + "org.apache.derby:derbyclient" : "10.13.1.1", + "org.apache.directory.server:apacheds-core" : "1.5.5", + "org.apache.directory.server:apacheds-core-entry" : "1.5.5", + "org.apache.directory.server:apacheds-protocol-ldap" : "1.5.5", + "org.apache.directory.server:apacheds-protocol-shared" : "1.5.5", + "org.apache.directory.server:apacheds-server-jndi" : "1.5.5", + "org.apache.directory.shared:shared-ldap" : "0.9.15", + "org.apache.httpcomponents:httpasyncclient" : "4.1.2", + "org.apache.httpcomponents:httpclient" : "4.5.2", + "org.apache.httpcomponents:httpcore" : "4.4.6", + "org.apache.httpcomponents:httpmime" : "4.5.2", + "org.apache.ibatis:ibatis-sqlmap" : "2.3.4.726", + "org.apache.kafka:kafka-clients" : "0.10.1.1", + "org.apache.kafka:kafka_2.11" : "0.10.1.1", + "org.apache.logging.log4j:log4j-1.2-api" : "2.7", + "org.apache.logging.log4j:log4j-api" : "2.7", + "org.apache.logging.log4j:log4j-api-scala_2.10" : "2.7", + "org.apache.logging.log4j:log4j-api-scala_2.11" : "2.7", + "org.apache.logging.log4j:log4j-core" : "2.7", + "org.apache.logging.log4j:log4j-flume-ng" : "2.7", + "org.apache.logging.log4j:log4j-iostreams" : "2.7", + "org.apache.logging.log4j:log4j-jcl" : "2.7", + "org.apache.logging.log4j:log4j-jmx-gui" : "2.7", + "org.apache.logging.log4j:log4j-jul" : "2.7", + "org.apache.logging.log4j:log4j-liquibase" : "2.7", + "org.apache.logging.log4j:log4j-nosql" : "2.7", + "org.apache.logging.log4j:log4j-slf4j-impl" : "2.7", + "org.apache.logging.log4j:log4j-taglib" : "2.7", + "org.apache.logging.log4j:log4j-web" : "2.7", + "org.apache.myfaces.core:myfaces-impl" : "2.2.11", + "org.apache.openjpa:openjpa" : "2.4.2", + "org.apache.openjpa:openjpa-persistence-jdbc" : "2.4.2", + "org.apache.poi:poi" : "3.15", + "org.apache.poi:poi-ooxml" : "3.15", + "org.apache.poi:poi-scratchpad" : "3.15", + "org.apache.solr:solr-core" : "5.5.3", + "org.apache.solr:solr-solrj" : "5.5.3", + "org.apache.taglibs:taglibs-standard-impl" : "1.2.5", + "org.apache.taglibs:taglibs-standard-jstlel" : "1.2.5", + "org.apache.taglibs:taglibs-standard-spec" : "1.2.5", + "org.apache.tiles:tiles-api" : "3.0.7", + "org.apache.tiles:tiles-core" : "3.0.7", + "org.apache.tiles:tiles-el" : "3.0.7", + "org.apache.tiles:tiles-extras" : "3.0.7", + "org.apache.tiles:tiles-jsp" : "3.0.7", + "org.apache.tiles:tiles-request-api" : "1.0.6", + "org.apache.tiles:tiles-servlet" : "3.0.7", + "org.apache.tomcat:tomcat-catalina" : "8.5.11", + "org.apache.tomcat:tomcat-dbcp" : "8.5.11", + "org.apache.tomcat:tomcat-jdbc" : "8.5.11", + "org.apache.tomcat:tomcat-jsp-api" : "8.5.11", + "org.apache.tomcat:tomcat-websocket" : "8.5.11", + "org.apache.tomcat.embed:tomcat-embed-core" : "8.5.11", + "org.apache.tomcat.embed:tomcat-embed-el" : "8.5.11", + "org.apache.tomcat.embed:tomcat-embed-jasper" : "8.5.11", + "org.apache.tomcat.embed:tomcat-embed-websocket" : "8.5.11", + "org.apache.velocity:velocity" : "1.7", + "org.apache.ws.commons.axiom:axiom-api" : "1.2.20", + "org.apache.ws.commons.axiom:axiom-impl" : "1.2.20", + "org.apache.ws.security:wss4j" : "1.6.19", + "org.apache.ws.xmlschema:xmlschema-core" : "2.2.1", + "org.apache.wss4j:wss4j-ws-security-common" : "2.1.8", + "org.apache.wss4j:wss4j-ws-security-dom" : "2.1.8", + "org.apache.xmlbeans:xmlbeans" : "2.6.0", + "org.aspectj:aspectjrt" : "1.8.9", + "org.aspectj:aspectjtools" : "1.8.9", + "org.aspectj:aspectjweaver" : "1.8.9", + "org.assertj:assertj-core" : "2.6.0", + "org.atteo:evo-inflector" : "1.2.2", + "org.beanshell:bsh" : "2.0b4", + "org.bouncycastle:bcpkix-jdk15on" : "1.56", + "org.codehaus.btm:btm" : "2.1.4", + "org.codehaus.castor:castor-xml" : "1.4.1", + "org.codehaus.fabric3.api:commonj" : "1.1.1", + "org.codehaus.groovy:groovy" : "2.4.7", + "org.codehaus.groovy:groovy-all" : "2.4.7", + "org.codehaus.groovy:groovy-ant" : "2.4.7", + "org.codehaus.groovy:groovy-bsf" : "2.4.7", + "org.codehaus.groovy:groovy-console" : "2.4.7", + "org.codehaus.groovy:groovy-docgenerator" : "2.4.7", + "org.codehaus.groovy:groovy-groovydoc" : "2.4.7", + "org.codehaus.groovy:groovy-groovysh" : "2.4.7", + "org.codehaus.groovy:groovy-jmx" : "2.4.7", + "org.codehaus.groovy:groovy-json" : "2.4.7", + "org.codehaus.groovy:groovy-jsr223" : "2.4.7", + "org.codehaus.groovy:groovy-nio" : "2.4.7", + "org.codehaus.groovy:groovy-servlet" : "2.4.7", + "org.codehaus.groovy:groovy-sql" : "2.4.7", + "org.codehaus.groovy:groovy-swing" : "2.4.7", + "org.codehaus.groovy:groovy-templates" : "2.4.7", + "org.codehaus.groovy:groovy-test" : "2.4.7", + "org.codehaus.groovy:groovy-testng" : "2.4.7", + "org.codehaus.groovy:groovy-xml" : "2.4.7", + "org.codehaus.jackson:jackson-core-asl" : "1.9.13", + "org.codehaus.jackson:jackson-mapper-asl" : "1.9.13", + "org.codehaus.janino:janino" : "2.7.8", + "org.codehaus.jettison:jettison" : "1.2", + "org.codehaus.woodstox:woodstox-core-asl" : "4.4.1", + "org.crashub:crash.cli" : "1.3.2", + "org.crashub:crash.connectors.ssh" : "1.3.2", + "org.crashub:crash.connectors.telnet" : "1.3.2", + "org.crashub:crash.embed.spring" : "1.3.2", + "org.crashub:crash.plugins.cron" : "1.3.2", + "org.crashub:crash.plugins.mail" : "1.3.2", + "org.crashub:crash.shell" : "1.3.2", + "org.eclipse.jetty:apache-jsp" : "9.4.1.v20170120", + "org.eclipse.jetty:apache-jstl" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-annotations" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-client" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-continuation" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-deploy" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-http" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-io" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-jmx" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-plus" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-proxy" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-security" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-server" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-servlet" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-servlets" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-util" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-webapp" : "9.4.1.v20170120", + "org.eclipse.jetty:jetty-xml" : "9.4.1.v20170120", + "org.eclipse.jetty.orbit:javax.servlet.jsp" : "2.2.0.v201112011158", + "org.eclipse.jetty.websocket:javax-websocket-server-impl" : "9.4.1.v20170120", + "org.eclipse.jetty.websocket:websocket-client" : "9.4.1.v20170120", + "org.eclipse.jetty.websocket:websocket-server" : "9.4.1.v20170120", + "org.eclipse.paho:org.eclipse.paho.client.mqttv3" : "1.1.0", + "org.eclipse.persistence:javax.persistence" : "2.1.1", + "org.eclipse.persistence:org.eclipse.persistence.core" : "2.6.4", + "org.eclipse.persistence:org.eclipse.persistence.jpa" : "2.6.4", + "org.ehcache:ehcache" : "3.2.0", + "org.ehcache:ehcache-clustered" : "3.2.0", + "org.ehcache:ehcache-transactions" : "3.2.0", + "org.elasticsearch:elasticsearch" : "2.4.4", + "org.firebirdsql.jdbc:jaybird-jdk16" : "2.2.12", + "org.firebirdsql.jdbc:jaybird-jdk17" : "2.2.12", + "org.firebirdsql.jdbc:jaybird-jdk18" : "2.2.12", + "org.flywaydb:flyway-core" : "3.2.1", + "org.freemarker:freemarker" : "2.3.25-incubating", + "org.glassfish:javax.el" : "3.0.0", + "org.glassfish.jersey.containers:jersey-container-servlet" : "2.25.1", + "org.glassfish.jersey.containers:jersey-container-servlet-core" : "2.25.1", + "org.glassfish.jersey.core:jersey-server" : "2.25.1", + "org.glassfish.jersey.ext:jersey-bean-validation" : "2.25.1", + "org.glassfish.jersey.ext:jersey-spring3" : "2.25.1", + "org.glassfish.jersey.media:jersey-media-json-jackson" : "2.25.1", + "org.glassfish.tyrus:tyrus-container-servlet" : "1.3.5", + "org.glassfish.tyrus:tyrus-core" : "1.3.5", + "org.glassfish.tyrus:tyrus-server" : "1.3.5", + "org.glassfish.tyrus:tyrus-spi" : "1.3.5", + "org.hamcrest:hamcrest-all" : "1.3", + "org.hamcrest:hamcrest-core" : "1.3", + "org.hamcrest:hamcrest-library" : "1.3", + "org.hibernate:hibernate-core" : "5.0.11.Final", + "org.hibernate:hibernate-ehcache" : "5.0.11.Final", + "org.hibernate:hibernate-entitymanager" : "5.0.11.Final", + "org.hibernate:hibernate-envers" : "5.0.11.Final", + "org.hibernate:hibernate-java8" : "5.0.11.Final", + "org.hibernate:hibernate-jpamodelgen" : "5.0.11.Final", + "org.hibernate:hibernate-validator" : "5.3.4.Final", + "org.hibernate:hibernate-validator-annotation-processor" : "5.3.4.Final", + "org.hsqldb:hsqldb" : "2.3.3", + "org.igniterealtime.smack:smack-extensions" : "4.1.9", + "org.igniterealtime.smack:smack-java7" : "4.1.9", + "org.igniterealtime.smack:smack-resolver-javax" : "4.1.9", + "org.igniterealtime.smack:smack-tcp" : "4.1.9", + "org.infinispan:infinispan-jcache" : "8.2.5.Final", + "org.infinispan:infinispan-spring4-common" : "8.2.5.Final", + "org.infinispan:infinispan-spring4-embedded" : "8.2.5.Final", + "org.jasig.cas.client:cas-client-core" : "3.4.1", + "org.javassist:javassist" : "3.21.0-GA", + "org.jboss:jboss-transaction-spi" : "7.5.0.Final", + "org.jboss.logging:jboss-logging" : "3.3.0.Final", + "org.jboss.narayana.jta:jdbc" : "5.5.1.Final", + "org.jboss.narayana.jta:jms" : "5.5.1.Final", + "org.jboss.narayana.jta:jta" : "5.5.1.Final", + "org.jboss.narayana.jts:narayana-jts-integration" : "5.5.1.Final", + "org.jdom:jdom2" : "2.0.6", + "org.jibx:jibx-run" : "1.3.1", + "org.jolokia:jolokia-core" : "1.3.5", + "org.jooq:jooq" : "3.9.1", + "org.jooq:jooq-codegen" : "3.9.1", + "org.jooq:jooq-meta" : "3.9.1", + "org.jredis:jredis-core-api" : "06052013", + "org.jredis:jredis-core-ri" : "06052013", + "org.jruby:jruby" : "1.7.26", + "org.json:json" : "20140107", + "org.liquibase:liquibase-core" : "3.5.3", + "org.mariadb.jdbc:mariadb-java-client" : "1.5.7", + "org.mockito:mockito-core" : "1.10.19", + "org.mongodb:mongo-java-driver" : "3.4.1", + "org.mongodb:mongodb-driver" : "3.4.1", + "org.mortbay.jasper:apache-el" : "8.0.33", + "org.neo4j:neo4j-ogm-api" : "2.1.1", + "org.neo4j:neo4j-ogm-compiler" : "2.1.1", + "org.neo4j:neo4j-ogm-core" : "2.1.1", + "org.neo4j:neo4j-ogm-http-driver" : "2.1.1", + "org.objenesis:objenesis" : "2.5.1", + "org.openid4java:openid4java-nodeps" : "0.9.6", + "org.postgresql:postgresql" : "9.4.1212.jre7", + "org.projectlombok:lombok" : "1.16.12", + "org.quartz-scheduler:quartz" : "2.2.3", + "org.reactivestreams:reactive-streams" : "1.0.0", + "org.seleniumhq.selenium:htmlunit-driver" : "2.21", + "org.seleniumhq.selenium:selenium-api" : "2.53.1", + "org.seleniumhq.selenium:selenium-chrome-driver" : "2.53.1", + "org.seleniumhq.selenium:selenium-firefox-driver" : "2.53.1", + "org.seleniumhq.selenium:selenium-ie-driver" : "2.53.1", + "org.seleniumhq.selenium:selenium-java" : "2.53.1", + "org.seleniumhq.selenium:selenium-remote-driver" : "2.53.1", + "org.seleniumhq.selenium:selenium-safari-driver" : "2.53.1", + "org.seleniumhq.selenium:selenium-support" : "2.53.1", + "org.skyscreamer:jsonassert" : "1.4.0", + "org.slf4j:jcl-over-slf4j" : "1.7.22", + "org.slf4j:jul-to-slf4j" : "1.7.22", + "org.slf4j:log4j-over-slf4j" : "1.7.22", + "org.slf4j:slf4j-api" : "1.7.22", + "org.slf4j:slf4j-jdk14" : "1.7.22", + "org.slf4j:slf4j-log4j12" : "1.7.22", + "org.slf4j:slf4j-simple" : "1.7.22", + "org.spockframework:spock-core" : "1.0-groovy-2.4", + "org.spockframework:spock-spring" : "1.0-groovy-2.4", + "org.springframework:spring-aop" : "4.3.6.RELEASE", + "org.springframework:spring-aspects" : "4.3.6.RELEASE", + "org.springframework:spring-beans" : "4.3.6.RELEASE", + "org.springframework:spring-context" : "4.3.6.RELEASE", + "org.springframework:spring-context-support" : "4.3.6.RELEASE", + "org.springframework:spring-core" : "4.3.6.RELEASE", + "org.springframework:spring-expression" : "4.3.6.RELEASE", + "org.springframework:spring-instrument" : "4.3.6.RELEASE", + "org.springframework:spring-instrument-tomcat" : "4.3.6.RELEASE", + "org.springframework:spring-jdbc" : "4.3.6.RELEASE", + "org.springframework:spring-jms" : "4.3.6.RELEASE", + "org.springframework:spring-messaging" : "4.3.6.RELEASE", + "org.springframework:spring-orm" : "4.3.6.RELEASE", + "org.springframework:spring-oxm" : "4.3.6.RELEASE", + "org.springframework:spring-test" : "4.3.6.RELEASE", + "org.springframework:spring-tx" : "4.3.6.RELEASE", + "org.springframework:spring-web" : "4.3.6.RELEASE", + "org.springframework:spring-webmvc" : "4.3.6.RELEASE", + "org.springframework:spring-webmvc-portlet" : "4.3.6.RELEASE", + "org.springframework:spring-websocket" : "4.3.6.RELEASE", + "org.springframework:springloaded" : "1.2.6.RELEASE", + "org.springframework.amqp:spring-amqp" : "1.7.0.RELEASE", + "org.springframework.amqp:spring-rabbit" : "1.7.0.RELEASE", + "org.springframework.batch:spring-batch-core" : "3.0.7.RELEASE", + "org.springframework.batch:spring-batch-infrastructure" : "3.0.7.RELEASE", + "org.springframework.batch:spring-batch-integration" : "3.0.7.RELEASE", + "org.springframework.batch:spring-batch-test" : "3.0.7.RELEASE", + "org.springframework.boot:spring-boot" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-actuator" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-actuator-docs" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-autoconfigure" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-autoconfigure-processor" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-configuration-metadata" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-configuration-processor" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-devtools" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-loader" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-loader-tools" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-activemq" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-actuator" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-amqp" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-aop" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-artemis" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-batch" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-cache" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-cloud-connectors" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-cassandra" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-couchbase" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-elasticsearch" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-gemfire" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-jpa" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-ldap" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-mongodb" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-neo4j" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-redis" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-rest" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-data-solr" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-freemarker" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-groovy-templates" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-hateoas" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-integration" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jdbc" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jersey" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jetty" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jooq" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jta-atomikos" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jta-bitronix" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-jta-narayana" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-log4j2" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-logging" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-mail" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-mobile" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-mustache" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-remote-shell" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-security" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-social-facebook" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-social-linkedin" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-social-twitter" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-test" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-thymeleaf" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-tomcat" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-undertow" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-validation" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-web" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-web-services" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-starter-websocket" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-test" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-test-autoconfigure" : "1.5.1.RELEASE", + "org.springframework.boot:spring-boot-test-support" : "1.5.1.RELEASE", + "org.springframework.cloud:spring-cloud-cloudfoundry-connector" : "1.2.3.RELEASE", + "org.springframework.cloud:spring-cloud-core" : "1.2.3.RELEASE", + "org.springframework.cloud:spring-cloud-heroku-connector" : "1.2.3.RELEASE", + "org.springframework.cloud:spring-cloud-localconfig-connector" : "1.2.3.RELEASE", + "org.springframework.cloud:spring-cloud-spring-service-connector" : "1.2.3.RELEASE", + "org.springframework.data:spring-cql" : "1.5.0.RELEASE", + "org.springframework.data:spring-data-cassandra" : "1.5.0.RELEASE", + "org.springframework.data:spring-data-commons" : "1.13.0.RELEASE", + "org.springframework.data:spring-data-couchbase" : "2.2.0.RELEASE", + "org.springframework.data:spring-data-elasticsearch" : "2.1.0.RELEASE", + "org.springframework.data:spring-data-envers" : "1.1.0.RELEASE", + "org.springframework.data:spring-data-gemfire" : "1.9.0.RELEASE", + "org.springframework.data:spring-data-jpa" : "1.11.0.RELEASE", + "org.springframework.data:spring-data-keyvalue" : "1.2.0.RELEASE", + "org.springframework.data:spring-data-ldap" : "1.0.0.RELEASE", + "org.springframework.data:spring-data-mongodb" : "1.10.0.RELEASE", + "org.springframework.data:spring-data-mongodb-cross-store" : "1.10.0.RELEASE", + "org.springframework.data:spring-data-mongodb-log4j" : "1.10.0.RELEASE", + "org.springframework.data:spring-data-neo4j" : "4.2.0.RELEASE", + "org.springframework.data:spring-data-redis" : "1.8.0.RELEASE", + "org.springframework.data:spring-data-rest-core" : "2.6.0.RELEASE", + "org.springframework.data:spring-data-rest-hal-browser" : "2.6.0.RELEASE", + "org.springframework.data:spring-data-rest-webmvc" : "2.6.0.RELEASE", + "org.springframework.data:spring-data-solr" : "2.1.0.RELEASE", + "org.springframework.hateoas:spring-hateoas" : "0.23.0.RELEASE", + "org.springframework.integration:spring-integration-amqp" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-core" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-event" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-feed" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-file" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-flow" : "1.0.0.RELEASE", + "org.springframework.integration:spring-integration-ftp" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-gemfire" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-groovy" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-http" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-ip" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-java-dsl" : "1.2.1.RELEASE", + "org.springframework.integration:spring-integration-jdbc" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-jms" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-jmx" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-jpa" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-kafka" : "2.1.0.RELEASE", + "org.springframework.integration:spring-integration-mail" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-mongodb" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-mqtt" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-redis" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-rmi" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-scripting" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-security" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-sftp" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-splunk" : "1.1.0.RELEASE", + "org.springframework.integration:spring-integration-stomp" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-stream" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-syslog" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-test" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-twitter" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-websocket" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-ws" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-xml" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-xmpp" : "4.3.7.RELEASE", + "org.springframework.integration:spring-integration-zookeeper" : "4.3.7.RELEASE", + "org.springframework.kafka:spring-kafka" : "1.1.2.RELEASE", + "org.springframework.kafka:spring-kafka-test" : "1.1.2.RELEASE", + "org.springframework.ldap:spring-ldap-core" : "2.3.1.RELEASE", + "org.springframework.ldap:spring-ldap-core-tiger" : "2.3.1.RELEASE", + "org.springframework.ldap:spring-ldap-ldif-batch" : "2.3.1.RELEASE", + "org.springframework.ldap:spring-ldap-ldif-core" : "2.3.1.RELEASE", + "org.springframework.ldap:spring-ldap-odm" : "2.3.1.RELEASE", + "org.springframework.ldap:spring-ldap-test" : "2.3.1.RELEASE", + "org.springframework.mobile:spring-mobile-device" : "1.1.5.RELEASE", + "org.springframework.plugin:spring-plugin-core" : "1.2.0.RELEASE", + "org.springframework.plugin:spring-plugin-metadata" : "1.2.0.RELEASE", + "org.springframework.restdocs:spring-restdocs-core" : "1.1.2.RELEASE", + "org.springframework.restdocs:spring-restdocs-mockmvc" : "1.1.2.RELEASE", + "org.springframework.restdocs:spring-restdocs-restassured" : "1.1.2.RELEASE", + "org.springframework.retry:spring-retry" : "1.2.0.RELEASE", + "org.springframework.security:spring-security-acl" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-aspects" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-cas" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-config" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-core" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-crypto" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-data" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-jwt" : "1.0.7.RELEASE", + "org.springframework.security:spring-security-ldap" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-messaging" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-openid" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-remoting" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-taglibs" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-test" : "4.2.1.RELEASE", + "org.springframework.security:spring-security-web" : "4.2.1.RELEASE", + "org.springframework.security.oauth:spring-security-oauth" : "2.0.12.RELEASE", + "org.springframework.security.oauth:spring-security-oauth2" : "2.0.12.RELEASE", + "org.springframework.session:spring-session" : "1.3.0.RELEASE", + "org.springframework.session:spring-session-data-gemfire" : "1.3.0.RELEASE", + "org.springframework.session:spring-session-data-mongo" : "1.3.0.RELEASE", + "org.springframework.session:spring-session-data-redis" : "1.3.0.RELEASE", + "org.springframework.session:spring-session-hazelcast" : "1.3.0.RELEASE", + "org.springframework.session:spring-session-jdbc" : "1.3.0.RELEASE", + "org.springframework.shell:spring-shell" : "1.1.0.RELEASE", + "org.springframework.social:spring-social-config" : "1.1.4.RELEASE", + "org.springframework.social:spring-social-core" : "1.1.4.RELEASE", + "org.springframework.social:spring-social-facebook" : "2.0.3.RELEASE", + "org.springframework.social:spring-social-facebook-web" : "2.0.3.RELEASE", + "org.springframework.social:spring-social-linkedin" : "1.0.2.RELEASE", + "org.springframework.social:spring-social-security" : "1.1.4.RELEASE", + "org.springframework.social:spring-social-twitter" : "1.1.2.RELEASE", + "org.springframework.social:spring-social-web" : "1.1.4.RELEASE", + "org.springframework.webflow:spring-binding" : "2.4.4.RELEASE", + "org.springframework.webflow:spring-faces" : "2.4.4.RELEASE", + "org.springframework.webflow:spring-js" : "2.4.4.RELEASE", + "org.springframework.webflow:spring-js-resources" : "2.4.4.RELEASE", + "org.springframework.webflow:spring-webflow" : "2.4.4.RELEASE", + "org.springframework.ws:spring-ws-core" : "2.4.0.RELEASE", + "org.springframework.ws:spring-ws-security" : "2.4.0.RELEASE", + "org.springframework.ws:spring-ws-support" : "2.4.0.RELEASE", + "org.springframework.ws:spring-ws-test" : "2.4.0.RELEASE", + "org.springframework.ws:spring-xml" : "2.4.0.RELEASE", + "org.testng:testng" : "6.10", + "org.threeten:threetenbp" : "1.3.3", + "org.thymeleaf:thymeleaf" : "2.1.5.RELEASE", + "org.thymeleaf:thymeleaf-spring4" : "2.1.5.RELEASE", + "org.thymeleaf.extras:thymeleaf-extras-conditionalcomments" : "2.1.2.RELEASE", + "org.thymeleaf.extras:thymeleaf-extras-java8time" : "2.1.0.RELEASE", + "org.thymeleaf.extras:thymeleaf-extras-springsecurity4" : "2.1.3.RELEASE", + "org.webjars:bootstrap" : "2.3.2", + "org.webjars:hal-browser" : "9f96c74", + "org.webjars:html5shiv" : "3.7.3", + "org.webjars:json-editor" : "0.7.21", + "org.webjars:knockout" : "2.3.0", + "org.webjars:sockjs-client" : "0.3.4", + "org.webjars:stomp-websocket" : "2.3.0", + "org.webjars:webjars-locator" : "0.32", + "org.webjars:webjars-taglib" : "0.3", + "org.xerial:sqlite-jdbc" : "3.15.1", + "org.xerial.snappy:snappy-java" : "1.1.2.6", + "org.xmlbeam:xmlprojector" : "1.4.9", + "org.yaml:snakeyaml" : "1.17", + "org.zeromq:jeromq" : "0.3.4", + "redis.clients:jedis" : "2.9.0", + "velocity-tools:velocity-tools-view" : "1.4", + "wsdl4j:wsdl4j" : "1.6.3", + "xml-apis:xml-apis" : "1.4.01", + "xmlunit:xmlunit" : "1.6", + "xom:xom" : "1.2.5", + "org.gebish:geb-spock" : "1.1.1", + "org.powermock:powermock-core" : "1.6.2", + "org.powermock:powermock-api-support" : "1.6.2", + "org.powermock:powermock-module-junit4-common" : "1.6.2", + "org.powermock:powermock-module-junit4" : "1.6.2", + "org.powermock:powermock-api-mockito" : "1.6.2", + "org.powermock:powermock-reflect" : "1.6.2" +] + +configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + def id = "${details.requested.group}:${details.requested.name}" + + if(deps[id]) { + details.useVersion deps[id] + } + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle new file mode 100644 index 00000000000..91d6d7e151f --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'io.spring.convention.docs' + id 'java' +} + +version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle new file mode 100644 index 00000000000..7378d4a3494 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'simple' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000000..ba2fbbe84b0 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc @@ -0,0 +1,3 @@ += Example Manual + +This will fail due to {missing} attribute \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle new file mode 100644 index 00000000000..91d6d7e151f --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'io.spring.convention.docs' + id 'java' +} + +version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle new file mode 100644 index 00000000000..1731930901e --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'missing-include' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000000..2a41dd8a8c7 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc @@ -0,0 +1,3 @@ += Example Manual + +This will fail due to <<missing>> cross reference \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle new file mode 100644 index 00000000000..91d6d7e151f --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'io.spring.convention.docs' + id 'java' +} + +version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle new file mode 100644 index 00000000000..1731930901e --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'missing-include' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000000..ecf2133e9ab --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc @@ -0,0 +1,5 @@ += Example Manual + +This will fail due to missing include + +include::missing.adoc[] \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/build.gradle b/buildSrc/src/test/resources/samples/docs/simple/build.gradle new file mode 100644 index 00000000000..9deefc7e1ae --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/build.gradle @@ -0,0 +1,13 @@ +plugins { + id 'io.spring.convention.docs' + id 'java' +} + +version = '1.0.0.BUILD-SNAPSHOT' + +asciidoctorj { + attributes \ + 'build-gradle': project.buildFile, + 'sourcedir': project.sourceSets.main.java.srcDirs[0], + 'endpoint-url': 'https://example.org' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/settings.gradle b/buildSrc/src/test/resources/samples/docs/simple/settings.gradle new file mode 100644 index 00000000000..7378d4a3494 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'simple' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html new file mode 100644 index 00000000000..f399886516e --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html @@ -0,0 +1 @@ +<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script> diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg new file mode 100644 index 00000000000..48c9129b301 Binary files /dev/null and b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg differ diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000000..ea4087750aa --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc @@ -0,0 +1,60 @@ += Example Manual +Doc Writer <doc.writer@example.org> +2014-09-09 +:example-caption!: +ifndef::imagesdir[:imagesdir: images] +ifndef::sourcedir[:sourcedir: ../java] + +This is a user manual for an example project. + +== Introduction + +This project does something. +We just haven't decided what that is yet. + +== Source Code + +[source,java] +.Java code from project +---- +include::{sourcedir}/example/StringUtils.java[tags=contains,indent=0] +---- + +This page was built by the following command: + + $ ./gradlew asciidoctor + +== Images + +[.thumb] +image::sunset.jpg[scaledwidth=75%] + +== Attributes + +.Built-in +asciidoctor-version:: {asciidoctor-version} +safe-mode-name:: {safe-mode-name} +docdir:: {docdir} +docfile:: {docfile} +imagesdir:: {imagesdir} +revnumber:: {revnumber} + +.Custom +sourcedir:: {sourcedir} +endpoint-url:: {endpoint-url} + +== Includes + +.include::subdir/_b.adoc[] +==== +include::subdir/_b.adoc[] +==== + +WARNING: Includes can be tricky! + +== build.gradle + +[source,groovy] +---- +include::{build-gradle}[] +---- diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc new file mode 100644 index 00000000000..422eb5ee040 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc @@ -0,0 +1,7 @@ +content from _src/docs/asciidoc/subdir/_b.adoc_. + +.include::_c.adoc[] +[example] +-- +include::_c.adoc[] +-- diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc new file mode 100644 index 00000000000..3aca1736362 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc @@ -0,0 +1 @@ +content from _src/docs/asciidoc/subdir/c.adoc_. diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java b/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java new file mode 100644 index 00000000000..3884f862bb6 --- /dev/null +++ b/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java @@ -0,0 +1,9 @@ +package example; + +public class StringUtils { + // tag::contains[] + public boolean contains(String haystack, String needle) { + return haystack.contains(needle); + } + // end::contains[] +} diff --git a/buildSrc/src/test/resources/samples/integrationtest/withgroovy/build.gradle b/buildSrc/src/test/resources/samples/integrationtest/withgroovy/build.gradle new file mode 100644 index 00000000000..d5dd6923374 --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withgroovy/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'io.spring.convention.integration-test' +} + +apply plugin: 'java' +apply plugin: 'groovy' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' + integrationTestCompile 'org.springframework:spring-core:4.3.7.RELEASE' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/integrationtest/withgroovy/src/integration-test/groovy/sample/TheTest.groovy b/buildSrc/src/test/resources/samples/integrationtest/withgroovy/src/integration-test/groovy/sample/TheTest.groovy new file mode 100644 index 00000000000..d3a9898b03e --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withgroovy/src/integration-test/groovy/sample/TheTest.groovy @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * 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 + * + * https://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 sample; + +import org.springframework.core.Ordered; +import spock.lang.Specification; + +class TheTest extends Specification { + def "has Ordered"() { + expect: 'Loads Ordered fine' + Ordered ordered = new Ordered() { + @Override + int getOrder() { + return 0 + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/integrationtest/withjava/build.gradle b/buildSrc/src/test/resources/samples/integrationtest/withjava/build.gradle new file mode 100644 index 00000000000..69fe9fa8f63 --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withjava/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'io.spring.convention.integration-test' +} + +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + integrationTestCompile 'org.springframework:spring-core:4.3.7.RELEASE' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/integrationtest/withjava/src/integration-test/java/sample/TheTest.java b/buildSrc/src/test/resources/samples/integrationtest/withjava/src/integration-test/java/sample/TheTest.java new file mode 100644 index 00000000000..dc82588cc72 --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withjava/src/integration-test/java/sample/TheTest.java @@ -0,0 +1,16 @@ +package sample; + +import org.junit.Test; +import org.springframework.core.Ordered; + +public class TheTest { + @Test + public void compilesAndRuns() { + Ordered ordered = new Ordered() { + @Override + public int getOrder() { + return 0; + } + }; + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/build.gradle b/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/build.gradle new file mode 100644 index 00000000000..af2027d522c --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'io.spring.convention.integration-test' +} + +apply plugin: 'java' +apply plugin: 'propdeps' + +repositories { + mavenCentral() +} + +dependencies { + optional 'javax.servlet:javax.servlet-api:3.1.0' + testCompile 'junit:junit:4.12' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/src/integration-test/java/sample/TheTest.java b/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/src/integration-test/java/sample/TheTest.java new file mode 100644 index 00000000000..de492ca0e67 --- /dev/null +++ b/buildSrc/src/test/resources/samples/integrationtest/withpropdeps/src/integration-test/java/sample/TheTest.java @@ -0,0 +1,11 @@ +package sample; + +import org.junit.Test; +import javax.servlet.http.HttpServletRequest; + +public class TheTest { + @Test + public void compilesAndRuns() { + HttpServletRequest request = null; + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/jacoco/java/build.gradle b/buildSrc/src/test/resources/samples/jacoco/java/build.gradle new file mode 100644 index 00000000000..f70cc9a714c --- /dev/null +++ b/buildSrc/src/test/resources/samples/jacoco/java/build.gradle @@ -0,0 +1,13 @@ +plugins { + id 'io.spring.convention.jacoco' +} + +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/jacoco/java/src/main/java/sample/TheClass.java b/buildSrc/src/test/resources/samples/jacoco/java/src/main/java/sample/TheClass.java new file mode 100644 index 00000000000..cb7daa50011 --- /dev/null +++ b/buildSrc/src/test/resources/samples/jacoco/java/src/main/java/sample/TheClass.java @@ -0,0 +1,11 @@ +package sample; + +public class TheClass { + public boolean doStuff(boolean b) { + if(b) { + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/jacoco/java/src/test/java/sample/TheClassTest.java b/buildSrc/src/test/resources/samples/jacoco/java/src/test/java/sample/TheClassTest.java new file mode 100644 index 00000000000..5e1c64bbf6a --- /dev/null +++ b/buildSrc/src/test/resources/samples/jacoco/java/src/test/java/sample/TheClassTest.java @@ -0,0 +1,19 @@ +package sample; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TheClassTest { + TheClass theClass = new TheClass(); + + @Test + public void doStuffWhenTrueThenTrue() { + assertTrue(theClass.doStuff(true)); + } + + @Test + public void doStuffWhenTrueThenFalse() { + assertFalse(theClass.doStuff(false)); + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/build.gradle b/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/build.gradle new file mode 100644 index 00000000000..f9887e0719e --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/build.gradle @@ -0,0 +1 @@ +apply plugin: 'io.spring.convention.spring-module' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/src/main/java/sample/Api.java b/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/src/main/java/sample/Api.java new file mode 100644 index 00000000000..7177b85bcc9 --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/api/src/main/java/sample/Api.java @@ -0,0 +1,14 @@ +package sample; + +/** + * Testing this + * @author Rob Winch + * + */ +public class Api { + + /** + * This does stuff + */ + public void doStuff() {} +} diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/build.gradle b/buildSrc/src/test/resources/samples/javadocapi/multimodule/build.gradle new file mode 100644 index 00000000000..ce456f2d6cb --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'io.spring.convention.javadoc-api' + id 'io.spring.convention.spring-module' apply false +} diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/build.gradle b/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/build.gradle new file mode 100644 index 00000000000..f9887e0719e --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/build.gradle @@ -0,0 +1 @@ +apply plugin: 'io.spring.convention.spring-module' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/src/main/java/sample/Impl.java b/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/src/main/java/sample/Impl.java new file mode 100644 index 00000000000..bf906a54f20 --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/impl/src/main/java/sample/Impl.java @@ -0,0 +1,14 @@ +package sample; + +/** + * Testing this + * @author Rob Winch + * + */ +public class Impl { + + /** + * This does stuff + */ + public void otherThings() {} +} diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/build.gradle b/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/build.gradle new file mode 100644 index 00000000000..bbfeb03c223 --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/build.gradle @@ -0,0 +1 @@ +apply plugin: 'java' diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/src/main/java/sample/Sample.java b/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/src/main/java/sample/Sample.java new file mode 100644 index 00000000000..51b7f3f92ff --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/sample/src/main/java/sample/Sample.java @@ -0,0 +1,14 @@ +package sample; + +/** + * Testing this + * @author Rob Winch + * + */ +public class Sample { + + /** + * This does stuff + */ + public void doSample() {} +} diff --git a/buildSrc/src/test/resources/samples/javadocapi/multimodule/settings.gradle b/buildSrc/src/test/resources/samples/javadocapi/multimodule/settings.gradle new file mode 100644 index 00000000000..eb6441477e7 --- /dev/null +++ b/buildSrc/src/test/resources/samples/javadocapi/multimodule/settings.gradle @@ -0,0 +1,3 @@ +include ':api' +include ':impl' +include ':sample' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/install-with-springio/build.gradle b/buildSrc/src/test/resources/samples/maven/install-with-springio/build.gradle new file mode 100644 index 00000000000..b4784e44674 --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/install-with-springio/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'io.spring.convention.spring-module' +} + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + compile 'org.springframework:spring-core' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/install-with-springio/gradle/dependency-management.gradle b/buildSrc/src/test/resources/samples/maven/install-with-springio/gradle/dependency-management.gradle new file mode 100644 index 00000000000..3169bca25b7 --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/install-with-springio/gradle/dependency-management.gradle @@ -0,0 +1,5 @@ +dependencyManagement { + dependencies { + dependency 'org.springframework:spring-core:3.0.0.RELEASE' + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/install/build.gradle b/buildSrc/src/test/resources/samples/maven/install/build.gradle new file mode 100644 index 00000000000..c6cd7a97a70 --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/install/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'io.spring.convention.root' +} + +apply plugin: 'propdeps-maven' +apply plugin: 'io.spring.convention.maven' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + optional 'aopalliance:aopalliance:1.0' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/signing/build.gradle b/buildSrc/src/test/resources/samples/maven/signing/build.gradle new file mode 100644 index 00000000000..544f987f0ef --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/signing/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'io.spring.convention.root' +} + +version = "1.0.0.RELEASE" + +apply plugin: 'propdeps-maven' +apply plugin: 'io.spring.convention.maven' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + optional 'aopalliance:aopalliance:1.0' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/signing/settings.gradle b/buildSrc/src/test/resources/samples/maven/signing/settings.gradle new file mode 100644 index 00000000000..cf2aed0bac8 --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/signing/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'signing' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/maven/upload/build.gradle b/buildSrc/src/test/resources/samples/maven/upload/build.gradle new file mode 100644 index 00000000000..549c0551c35 --- /dev/null +++ b/buildSrc/src/test/resources/samples/maven/upload/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'io.spring.convention.root' +} + +apply plugin: 'propdeps-maven' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.12' + optional 'aopalliance:aopalliance:1.0' +} + +uploadArchives { + repositories { + mavenDeployer { + repository(url: "file:$buildDir/repo") + } + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/Jenkinsfile b/buildSrc/src/test/resources/samples/showcase/Jenkinsfile new file mode 100644 index 00000000000..ad9bfeaf7d8 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/Jenkinsfile @@ -0,0 +1,52 @@ +parallel check: { + stage('Check') { + node { + checkout scm + sh "./gradlew check --refresh-dependencies --no-daemon" + } + } +}, +sonar: { + stage('Sonar') { + node { + checkout scm + withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) { + sh "./gradlew sonarqube -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon" + } + } + } +}, +ossrh: { + stage('OSSRH Deploy') { + node { + checkout scm + withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) { + withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) { + withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) { + sh "./gradlew uploadArchives -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD --refresh-dependencies --no-daemon" + } + } + } + } + } +}, +docs: { + stage('Deploy Docs') { + node { + checkout scm + withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) { + sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + } + } + } +}, +schema: { + stage('Deploy Schema') { + node { + checkout scm + withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) { + sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace" + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/bom/bom.gradle b/buildSrc/src/test/resources/samples/showcase/bom/bom.gradle new file mode 100644 index 00000000000..19aa720c82b --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/bom/bom.gradle @@ -0,0 +1,2 @@ +apply plugin: 'io.spring.convention.bom' + diff --git a/buildSrc/src/test/resources/samples/showcase/build.gradle b/buildSrc/src/test/resources/samples/showcase/build.gradle new file mode 100644 index 00000000000..163e7b7428f --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'io.spring.convention.root' +} + +group = "org.springframework.build.test" +version = "1.0.0.BUILD-SNAPSHOT" diff --git a/buildSrc/src/test/resources/samples/showcase/etc/checkstyle/checkstyle.xml b/buildSrc/src/test/resources/samples/showcase/etc/checkstyle/checkstyle.xml new file mode 100644 index 00000000000..fbe98f23801 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/etc/checkstyle/checkstyle.xml @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" + "https://www.puppycrawl.com/dtds/configuration_1_3.dtd"> +<module name="Checker"> +</module> diff --git a/buildSrc/src/test/resources/samples/showcase/settings.gradle b/buildSrc/src/test/resources/samples/showcase/settings.gradle new file mode 100644 index 00000000000..1997a84cc30 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/settings.gradle @@ -0,0 +1,23 @@ +rootProject.name = 'spring-gradle-build-conventions-sample' + + +FileTree projects = fileTree(rootDir) { + include '**/*.gradle' + exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*' +} + +String rootDirPath = rootDir.absolutePath + File.separator +projects.each { File buildFile -> + String buildFilePath = buildFile.parentFile.absolutePath + + String projectPath = buildFilePath.replace(rootDirPath, '').replaceAll(File.separator, ':') + + include projectPath + + def project = findProject(":${projectPath}") + if(!'build.gradle'.equals(buildFile.name)) { + project.name = buildFile.name.replace('.gradle','') + project.buildFileName = buildFile.name + } + project.projectDir = buildFile.parentFile +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-api/sgbcs-api.gradle b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/sgbcs-api.gradle new file mode 100644 index 00000000000..a5fd4ea8967 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/sgbcs-api.gradle @@ -0,0 +1,9 @@ +apply plugin: 'io.spring.convention.spring-module' + +dependencies { + management platform('io.spring.platform:platform-bom:Brussels-RELEASE') + compile 'org.springframework:spring-web' + compile 'org.springframework:spring-core' + testCompile 'junit:junit' +} + diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/main/java/api/Api.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/main/java/api/Api.java new file mode 100644 index 00000000000..6ed5b9c60fe --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/main/java/api/Api.java @@ -0,0 +1,10 @@ +package api; + +/** + * + * @author Rob Winch + * + */ +public class Api { + +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/test/java/api/ApiTest.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/test/java/api/ApiTest.java new file mode 100644 index 00000000000..e1ede21b2cd --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-api/src/test/java/api/ApiTest.java @@ -0,0 +1,9 @@ +package api; + +import org.junit.Test; + +public class ApiTest { + + @Test + public void api() {} +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/sgbcs-core.gradle b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/sgbcs-core.gradle new file mode 100644 index 00000000000..e5f419b3375 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/sgbcs-core.gradle @@ -0,0 +1,7 @@ +apply plugin: 'io.spring.convention.spring-module' + +dependencies { + management platform('io.spring.platform:platform-bom:Brussels-RELEASE') + optional 'ch.qos.logback:logback-classic' + testCompile 'junit:junit' +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/CoreClass.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/CoreClass.java new file mode 100644 index 00000000000..4a0ecdb5260 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/CoreClass.java @@ -0,0 +1,13 @@ +package core; + +/** + * + * @author Rob Winch + * + */ +public class CoreClass { + + public void run() { + + } +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/HasOptional.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/HasOptional.java new file mode 100644 index 00000000000..8d1ace3c7ab --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/java/core/HasOptional.java @@ -0,0 +1,14 @@ +package core; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class HasOptional { + + public static void doStuffWithOptionalDependency() { + Logger logger = LoggerFactory.getLogger(HasOptional.class); + logger.debug("This is optional"); + } +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.handlers b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.handlers new file mode 100644 index 00000000000..6838fba4615 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/springgradlebuildsample=org.springframework.ldap.config.LdapNamespaceHandler \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.schemas b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.schemas new file mode 100644 index 00000000000..84285717f59 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,4 @@ +http\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd +http\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.0.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd +http\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.1.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd +http\://www.springframework.org/schema/springgradlebuildsample/spring-springgradlebuildsample-2.2.xsd=org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd new file mode 100644 index 00000000000..327f3a59c8b --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.0.xsd @@ -0,0 +1,468 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:ldap="http://www.springframework.org/schema/ldap" + xmlns:repository="http://www.springframework.org/schema/data/repository" + elementFormDefault="qualified" + targetNamespace="http://www.springframework.org/schema/springgradlebuildsample"> + + <xs:import namespace="http://www.springframework.org/schema/data/repository" + schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd" /> + + <xs:attributeGroup name="context-source.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="anonymous-read-only" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Defines whether read-only operations will be performed using an anonymous (unauthenticated) context. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will + be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-strategy-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy + will be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will + be relative to this DN. Default is an empty distinguished name (i.e. all operations will be + relative to the directory root). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="password" type="xs:string"> + <xs:annotation> + <xs:documentation> + The password to use for authentication. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="native-pooling" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specify whether native Java LDAP connection pooling should be used. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="referral"> + <xs:annotation> + <xs:documentation> + Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html. + Default is null. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="ignore" /> + <xs:enumeration value="follow" /> + <xs:enumeration value="throw" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can + be specified, separated using comma (,). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="username" type="xs:string"> + <xs:annotation> + <xs:documentation> + The username (principal) to use for authentication. This will normally be the distinguished name + of an admin user. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base-env-props-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Reference to a Map of custom environment properties that should supplied with the environment + sent to the DirContext on construction. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:attributeGroup name="pooling.attlist"> + <xs:attribute name="max-active" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) + that can be allocated from the pool at the same time, or non-positive for no limit. + Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-total" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The overall maximum number of active connections (for all types) that can be allocated from + this pool at the same time, or non-positive for no limit. Default is -1 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-idle" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, + without extra ones being released, or non-positive for no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-idle" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The minimum number of active connections of each type (read-only|read-write) that can remain + idle in the pool, without extra ones being created, or zero to create none. Default is 0. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-wait" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception, or non-positive to wait indefinitely. + Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="when-exhausted"> + <xs:annotation> + <xs:documentation> + Specifies the behaviour when the pool is exhausted. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="FAIL"> + <xs:annotation> + <xs:documentation> + Throw a NoSuchElementException when the pool is exhausted + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="BLOCK"> + <xs:annotation> + <xs:documentation> + Wait until a new object is available. If max-wait is positive a NoSuchElementException + is thrown if no new object is available after the maxWait time expires. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="GROW"> + <xs:annotation> + <xs:documentation> + Create and return a new object (essentially making maxActive meaningless). + </xs:documentation> + </xs:annotation> + </xs:enumeration> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="test-on-borrow" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-return" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being returned to the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-while-idle" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-run-interval-millis" type="xs:int"> + <xs:annotation> + <xs:documentation> + The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, + no idle object evictor thread will be run. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="tests-per-eviction-run" type="xs:int"> + <xs:annotation> + <xs:documentation> + The number of objects to examine during each run of the idle object evictor thread (if any). + Default is 3. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-evictable-time-millis" type="xs:int"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible + for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base dn to use for validation searches. Default is LdapUtils.emptyPath(). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-filter" type="xs:string"> + <xs:annotation> + <xs:documentation> + The filter to use for validation queries. Default is (objectclass=*). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-search-controls-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="non-transient-exceptions" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="context-source"> + <xs:annotation> + <xs:documentation> + Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence minOccurs="0" maxOccurs="1"> + <xs:element name="pooling"> + <xs:annotation> + <xs:documentation> + Defines the settings to use for the Spring LDAP connection pooling support. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:pooling.attlist" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attributeGroup ref="ldap:context-source.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="ldap-template.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + Default is "ldapTemplate". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. Default is "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="count-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default count limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="time-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default time limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="search-scope"> + <xs:annotation> + <xs:documentation> + The default search scope for searches. Default is SUBTREE. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="OBJECT" /> + <xs:enumeration value="ONELEVEL" /> + <xs:enumeration value="SUBTREE" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="ignore-name-not-found" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether NameNotFoundException should be ignored in searches. Setting this + attribute to true will cause errors caused by invalid search base to be silently swallowed. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ignore-partial-result" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether PartialResultException should be ignored in searches. Some LDAP servers + have problems with referrals; these should normally be followed automatically, but if this + doesn't work it will manifest itself with a PartialResultException. Setting this attribute + to true presents a work-around to this problem. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="odm-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="ldap-template"> + <xs:annotation> + <xs:documentation> + Creates an LdapTemplate instance. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:ldap-template.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="transaction-manager.attlist"> + <xs:attribute name="id" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of this instance. Default is "transactionManager". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="data-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DataSource instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="session-factory-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the Hibernate SessionFactory instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="transaction-manager"> + <xs:annotation> + <xs:documentation> + Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified, + a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be + created. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="1" maxOccurs="1"> + <xs:element name="default-renaming-strategy"> + <xs:annotation> + <xs:documentation> + The default (simplistic) TempEntryRenamingStrategy. Please note that this + strategy will not work for more advanced scenarios. See reference documentation + for details. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="temp-suffix" type="xs:string"> + <xs:annotation> + <xs:documentation> + The default suffix that will be added to modified entries. + Default is "_temp". + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="different-subtree-renaming-strategy"> + <xs:annotation> + <xs:documentation> + TempEntryRenamingStrategy that moves the entry to a different subtree than + the original entry. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="subtree-node" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The subtree base where changed entries should be moved. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attributeGroup ref="ldap:transaction-manager.attlist" /> + </xs:complexType> + </xs:element> + + <xs:element name="repositories"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="repository:repositories"> + <xs:attribute name="ldap-template-ref"> + <xs:annotation> + <xs:documentation> + The reference to an LdapTemplate. Will default to 'ldapTemplate'. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + </xs:element> +</xs:schema> \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd new file mode 100644 index 00000000000..3b5e71988b9 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.1.xsd @@ -0,0 +1,685 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:ldap="http://www.springframework.org/schema/ldap" + xmlns:repository="http://www.springframework.org/schema/data/repository" + elementFormDefault="qualified" + targetNamespace="http://www.springframework.org/schema/springgradlebuildsample"> + + <xs:import namespace="http://www.springframework.org/schema/data/repository" + schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd" /> + + <xs:attributeGroup name="context-source.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="anonymous-read-only" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Defines whether read-only operations will be performed using an anonymous (unauthenticated) context. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will + be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-strategy-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy + will be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will + be relative to this DN. Default is an empty distinguished name (i.e. all operations will be + relative to the directory root). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="password" type="xs:string"> + <xs:annotation> + <xs:documentation> + The password to use for authentication. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="native-pooling" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specify whether native Java LDAP connection pooling should be used. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="referral"> + <xs:annotation> + <xs:documentation> + Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html. + Default is null. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="ignore" /> + <xs:enumeration value="follow" /> + <xs:enumeration value="throw" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can + be specified, separated using comma (,). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="username" type="xs:string"> + <xs:annotation> + <xs:documentation> + The username (principal) to use for authentication. This will normally be the distinguished name + of an admin user. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base-env-props-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Reference to a Map of custom environment properties that should supplied with the environment + sent to the DirContext on construction. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:attributeGroup name="pooling.attlist"> + <xs:attribute name="max-active" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) + that can be allocated from the pool at the same time, or non-positive for no limit. + Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-total" type="xs:string"> + <xs:annotation> + <xs:documentation> + The overall maximum number of active connections (for all types) that can be allocated from + this pool at the same time, or non-positive for no limit. Default is -1 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-idle" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, + without extra ones being released, or non-positive for no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-idle" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum number of active connections of each type (read-only|read-write) that can remain + idle in the pool, without extra ones being created, or zero to create none. Default is 0. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-wait" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception, or non-positive to wait indefinitely. + Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="when-exhausted"> + <xs:annotation> + <xs:documentation> + Specifies the behaviour when the pool is exhausted. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="FAIL"> + <xs:annotation> + <xs:documentation> + Throw a NoSuchElementException when the pool is exhausted + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="BLOCK"> + <xs:annotation> + <xs:documentation> + Wait until a new object is available. If max-wait is positive a NoSuchElementException + is thrown if no new object is available after the maxWait time expires. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="GROW"> + <xs:annotation> + <xs:documentation> + Create and return a new object (essentially making maxActive meaningless). + </xs:documentation> + </xs:annotation> + </xs:enumeration> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="test-on-borrow" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-return" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being returned to the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-while-idle" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-run-interval-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, + no idle object evictor thread will be run. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="tests-per-eviction-run" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of objects to examine during each run of the idle object evictor thread (if any). + Default is 3. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-evictable-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible + for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base dn to use for validation searches. Default is LdapUtils.emptyPath(). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-filter" type="xs:string"> + <xs:annotation> + <xs:documentation> + The filter to use for validation queries. Default is (objectclass=*). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-search-controls-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="non-transient-exceptions" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:attributeGroup name="pooling2.attlist"> + <xs:attribute name="max-total" type="xs:string"> + <xs:annotation> + <xs:documentation> + The overall maximum number of active connections (for all types) that can be allocated from + this pool at the same time, or non-positive for no limit. Default is -1 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-total-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The limit on the number of object instances allocated by the pool (checked out or idle), + per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value + indicates no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-idle-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool, + without extra ones being released, or non-positive for no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-idle-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum number of active connections per type (read-only|read-write) that can remain + idle in the pool, without extra ones being created, or zero to create none. Default is 0. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-wait" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception, or non-positive to wait indefinitely. + Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="block-when-exhausted" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException + is thrown if no new object is available after the maxWait time expires.. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-create" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether objects created for the pool will be validated before borrowing. If the object + fails to validate, then borrowing will fail. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-borrow" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-return" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being returned to the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-while-idle" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-run-interval-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, + no idle object evictor thread will be run. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="tests-per-eviction-run" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of objects to examine during each run of the idle object evictor thread (if any). + Default is 3. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-evictable-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible + for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="soft-min-evictable-idle-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible for + eviction by the idle object evictor, with the extra condition that at least minimum number + of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if + it is set to a positive value. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-policy-class" type="xs:string"> + <xs:annotation> + <xs:documentation> + The name of the eviction policy implementation that is used by this pool. The Pool will + attempt to load the class using the thread context class loader. If that fails, the Pool + will attempt to load the class using the class loader that loaded this class. Default is + org.apache.commons.pool2.impl.DefaultEvictionPolicy. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="fairness" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether or not the pool serves threads waiting to borrow connections fairly. + True means that waiting threads are served as if waiting in a FIFO queue. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-enable" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether JMX will be enabled with the platform MBean server for the pool. Default + is true. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-name-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The value of the JMX name base that will be used as part of the name assigned + to JMX enabled pools. Default is null. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-name-prefix" type="xs:string"> + <xs:annotation> + <xs:documentation> + The value of the JMX name prefix that will be used as part of the name assigned + to JMX enabled pools. Default value is pool. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="lifo" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether the pool has LIFO (last in, first out) behaviour with + respect to idle objects - always returning the most recently used object + from the pool, or as a FIFO (first in, first out) queue, where the pool + always returns the oldest object in the idle object pool. Default is true. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base dn to use for validation searches. Default is LdapUtils.emptyPath(). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-filter" type="xs:string"> + <xs:annotation> + <xs:documentation> + The filter to use for validation queries. Default is (objectclass=*). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-search-controls-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="non-transient-exceptions" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="context-source"> + <xs:annotation> + <xs:documentation> + Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="1"> + <xs:sequence> + <xs:element name="pooling"> + <xs:annotation> + <xs:documentation> + Defines the settings to use for the Spring LDAP connection pooling support. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:pooling.attlist" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:sequence> + <xs:element name="pooling2"> + <xs:annotation> + <xs:documentation> + Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:pooling2.attlist" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:choice> + <xs:attributeGroup ref="ldap:context-source.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="ldap-template.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + Default is "ldapTemplate". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. Default is "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="count-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default count limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="time-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default time limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="search-scope"> + <xs:annotation> + <xs:documentation> + The default search scope for searches. Default is SUBTREE. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="OBJECT" /> + <xs:enumeration value="ONELEVEL" /> + <xs:enumeration value="SUBTREE" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="ignore-name-not-found" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether NameNotFoundException should be ignored in searches. Setting this + attribute to true will cause errors caused by invalid search base to be silently swallowed. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ignore-partial-result" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether PartialResultException should be ignored in searches. Some LDAP servers + have problems with referrals; these should normally be followed automatically, but if this + doesn't work it will manifest itself with a PartialResultException. Setting this attribute + to true presents a work-around to this problem. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="odm-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="ldap-template"> + <xs:annotation> + <xs:documentation> + Creates an LdapTemplate instance. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:ldap-template.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="transaction-manager.attlist"> + <xs:attribute name="id" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of this instance. Default is "transactionManager". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="data-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DataSource instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="session-factory-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the Hibernate SessionFactory instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="transaction-manager"> + <xs:annotation> + <xs:documentation> + Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified, + a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be + created. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="1" maxOccurs="1"> + <xs:element name="default-renaming-strategy"> + <xs:annotation> + <xs:documentation> + The default (simplistic) TempEntryRenamingStrategy. Please note that this + strategy will not work for more advanced scenarios. See reference documentation + for details. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="temp-suffix" type="xs:string"> + <xs:annotation> + <xs:documentation> + The default suffix that will be added to modified entries. + Default is "_temp". + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="different-subtree-renaming-strategy"> + <xs:annotation> + <xs:documentation> + TempEntryRenamingStrategy that moves the entry to a different subtree than + the original entry. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="subtree-node" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The subtree base where changed entries should be moved. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attributeGroup ref="ldap:transaction-manager.attlist" /> + </xs:complexType> + </xs:element> + + <xs:element name="repositories"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="repository:repositories"> + <xs:attribute name="ldap-template-ref"> + <xs:annotation> + <xs:documentation> + The reference to an LdapTemplate. Will default to 'ldapTemplate'. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd new file mode 100644 index 00000000000..e76d784674c --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/main/resources/org/springframework/springgradlebuildsample/config/spring-springgradlebuildsample-2.2.xsd @@ -0,0 +1,685 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:ldap="http://www.springframework.org/schema/ldap" + xmlns:repository="http://www.springframework.org/schema/data/repository" + elementFormDefault="qualified" + targetNamespace="http://www.springframework.org/schema/springgradlebuildsample"> + + <xs:import namespace="http://www.springframework.org/schema/data/repository" + schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd" /> + + <xs:attributeGroup name="context-source.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="anonymous-read-only" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Defines whether read-only operations will be performed using an anonymous (unauthenticated) context. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will + be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="authentication-strategy-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy + will be used. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will + be relative to this DN. Default is an empty distinguished name (i.e. all operations will be + relative to the directory root). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="password" type="xs:string"> + <xs:annotation> + <xs:documentation> + The password to use for authentication. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="native-pooling" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specify whether native Java LDAP connection pooling should be used. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="referral"> + <xs:annotation> + <xs:documentation> + Defines the strategy to handle referrals, as described on https://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html. + Default is null. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="ignore" /> + <xs:enumeration value="follow" /> + <xs:enumeration value="throw" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="url" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can + be specified, separated using comma (,). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="username" type="xs:string"> + <xs:annotation> + <xs:documentation> + The username (principal) to use for authentication. This will normally be the distinguished name + of an admin user. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="base-env-props-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Reference to a Map of custom environment properties that should supplied with the environment + sent to the DirContext on construction. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:attributeGroup name="pooling.attlist"> + <xs:attribute name="max-active" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) + that can be allocated from the pool at the same time, or non-positive for no limit. + Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-total" type="xs:string"> + <xs:annotation> + <xs:documentation> + The overall maximum number of active connections (for all types) that can be allocated from + this pool at the same time, or non-positive for no limit. Default is -1 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-idle" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool, + without extra ones being released, or non-positive for no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-idle" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum number of active connections of each type (read-only|read-write) that can remain + idle in the pool, without extra ones being created, or zero to create none. Default is 0. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-wait" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception, or non-positive to wait indefinitely. + Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="when-exhausted"> + <xs:annotation> + <xs:documentation> + Specifies the behaviour when the pool is exhausted. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="FAIL"> + <xs:annotation> + <xs:documentation> + Throw a NoSuchElementException when the pool is exhausted + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="BLOCK"> + <xs:annotation> + <xs:documentation> + Wait until a new object is available. If max-wait is positive a NoSuchElementException + is thrown if no new object is available after the maxWait time expires. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="GROW"> + <xs:annotation> + <xs:documentation> + Create and return a new object (essentially making maxActive meaningless). + </xs:documentation> + </xs:annotation> + </xs:enumeration> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="test-on-borrow" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-return" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being returned to the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-while-idle" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-run-interval-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, + no idle object evictor thread will be run. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="tests-per-eviction-run" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of objects to examine during each run of the idle object evictor thread (if any). + Default is 3. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-evictable-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible + for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base dn to use for validation searches. Default is LdapUtils.emptyPath(). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-filter" type="xs:string"> + <xs:annotation> + <xs:documentation> + The filter to use for validation queries. Default is (objectclass=*). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-search-controls-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="non-transient-exceptions" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:attributeGroup name="pooling2.attlist"> + <xs:attribute name="max-total" type="xs:string"> + <xs:annotation> + <xs:documentation> + The overall maximum number of active connections (for all types) that can be allocated from + this pool at the same time, or non-positive for no limit. Default is -1 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-total-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The limit on the number of object instances allocated by the pool (checked out or idle), + per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value + indicates no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-idle-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool, + without extra ones being released, or non-positive for no limit. Default is 8. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-idle-per-key" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum number of active connections per type (read-only|read-write) that can remain + idle in the pool, without extra ones being created, or zero to create none. Default is 0. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="max-wait" type="xs:string"> + <xs:annotation> + <xs:documentation> + The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception, or non-positive to wait indefinitely. + Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="block-when-exhausted" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException + is thrown if no new object is available after the maxWait time expires. Default is true. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-create" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether objects created for the pool will be validated before borrowing. If the object + fails to validate, then borrowing will fail. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-borrow" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-on-return" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated before being returned to the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="test-while-idle" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-run-interval-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, + no idle object evictor thread will be run. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="tests-per-eviction-run" type="xs:string"> + <xs:annotation> + <xs:documentation> + The number of objects to examine during each run of the idle object evictor thread (if any). + Default is 3. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="min-evictable-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible + for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="soft-min-evictable-idle-time-millis" type="xs:string"> + <xs:annotation> + <xs:documentation> + The minimum amount of time an object may sit idle in the pool before it is eligible for + eviction by the idle object evictor, with the extra condition that at least minimum number + of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if + it is set to a positive value. Default is -1. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="eviction-policy-class" type="xs:string"> + <xs:annotation> + <xs:documentation> + The name of the eviction policy implementation that is used by this pool. The Pool will + attempt to load the class using the thread context class loader. If that fails, the Pool + will attempt to load the class using the class loader that loaded this class. Default is + org.apache.commons.pool2.impl.DefaultEvictionPolicy. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="fairness" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether or not the pool serves threads waiting to borrow connections fairly. + True means that waiting threads are served as if waiting in a FIFO queue. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-enable" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether JMX will be enabled with the platform MBean server for the pool. Default + is true. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-name-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The value of the JMX name base that will be used as part of the name assigned + to JMX enabled pools. Default is null. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="jmx-name-prefix" type="xs:string"> + <xs:annotation> + <xs:documentation> + The value of the JMX name prefix that will be used as part of the name assigned + to JMX enabled pools. Default value is pool. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="lifo" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Sets whether the pool has LIFO (last in, first out) behaviour with + respect to idle objects - always returning the most recently used object + from the pool, or as a FIFO (first in, first out) queue, where the pool + always returns the oldest object in the idle object pool. Default is true. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-base" type="xs:string"> + <xs:annotation> + <xs:documentation> + The base dn to use for validation searches. Default is LdapUtils.emptyPath(). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-filter" type="xs:string"> + <xs:annotation> + <xs:documentation> + The filter to use for validation queries. Default is (objectclass=*). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="validation-query-search-controls-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="non-transient-exceptions" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE; + countLimit: 1; timeLimit: 500; returningAttributes: [objectclass]. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="context-source"> + <xs:annotation> + <xs:documentation> + Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="1"> + <xs:sequence> + <xs:element name="pooling"> + <xs:annotation> + <xs:documentation> + Defines the settings to use for the Spring LDAP connection pooling support. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:pooling.attlist" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:sequence> + <xs:element name="pooling2"> + <xs:annotation> + <xs:documentation> + Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:pooling2.attlist" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:choice> + <xs:attributeGroup ref="ldap:context-source.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="ldap-template.attlist"> + <xs:attribute name="id" type="xs:token"> + <xs:annotation> + <xs:documentation> + A bean identifier, used for referring to the bean elsewhere in the context. + Default is "ldapTemplate". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. Default is "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="count-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default count limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="time-limit" type="xs:integer"> + <xs:annotation> + <xs:documentation> + The default time limit for searches. Default is 0 (no limit). + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="search-scope"> + <xs:annotation> + <xs:documentation> + The default search scope for searches. Default is SUBTREE. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="OBJECT" /> + <xs:enumeration value="ONELEVEL" /> + <xs:enumeration value="SUBTREE" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="ignore-name-not-found" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether NameNotFoundException should be ignored in searches. Setting this + attribute to true will cause errors caused by invalid search base to be silently swallowed. + Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="ignore-partial-result" type="xs:boolean"> + <xs:annotation> + <xs:documentation> + Specifies whether PartialResultException should be ignored in searches. Some LDAP servers + have problems with referrals; these should normally be followed automatically, but if this + doesn't work it will manifest itself with a PartialResultException. Setting this attribute + to true presents a work-around to this problem. Default is false. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="odm-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="ldap-template"> + <xs:annotation> + <xs:documentation> + Creates an LdapTemplate instance. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attributeGroup ref="ldap:ldap-template.attlist" /> + </xs:complexType> + </xs:element> + + <xs:attributeGroup name="transaction-manager.attlist"> + <xs:attribute name="id" type="xs:string"> + <xs:annotation> + <xs:documentation> + Id of this instance. Default is "transactionManager". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="context-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the ContextSource instance to use. "contextSource". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="data-source-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the DataSource instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="session-factory-ref" type="xs:token"> + <xs:annotation> + <xs:documentation> + Id of the Hibernate SessionFactory instance to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:attributeGroup> + + <xs:element name="transaction-manager"> + <xs:annotation> + <xs:documentation> + Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified, + a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be + created. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="1" maxOccurs="1"> + <xs:element name="default-renaming-strategy"> + <xs:annotation> + <xs:documentation> + The default (simplistic) TempEntryRenamingStrategy. Please note that this + strategy will not work for more advanced scenarios. See reference documentation + for details. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="temp-suffix" type="xs:string"> + <xs:annotation> + <xs:documentation> + The default suffix that will be added to modified entries. + Default is "_temp". + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="different-subtree-renaming-strategy"> + <xs:annotation> + <xs:documentation> + TempEntryRenamingStrategy that moves the entry to a different subtree than + the original entry. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:attribute name="subtree-node" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The subtree base where changed entries should be moved. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attributeGroup ref="ldap:transaction-manager.attlist" /> + </xs:complexType> + </xs:element> + + <xs:element name="repositories"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="repository:repositories"> + <xs:attribute name="ldap-template-ref"> + <xs:annotation> + <xs:documentation> + The reference to an LdapTemplate. Will default to 'ldapTemplate'. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/CoreClassTest.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/CoreClassTest.java new file mode 100644 index 00000000000..3ec9b922d75 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/CoreClassTest.java @@ -0,0 +1,12 @@ +package core; + +import org.junit.Test; + +public class CoreClassTest { + + @Test + public void test() { + new CoreClass().run(); + } + +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/HasOptionalTest.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/HasOptionalTest.java new file mode 100644 index 00000000000..bded2c97afd --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-core/src/test/java/core/HasOptionalTest.java @@ -0,0 +1,12 @@ +package core; + +import org.junit.Test; + +public class HasOptionalTest { + + @Test + public void test() { + HasOptional.doStuffWithOptionalDependency(); + } + +} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle new file mode 100644 index 00000000000..f3d52b8c1f1 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle @@ -0,0 +1,11 @@ +apply plugin: 'java' +apply plugin: 'io.spring.convention.docs' + +version = "1.0.0.BUILD-SNAPSHOT" + +asciidoctorj { + attributes \ + 'build-gradle': project.buildFile, + 'sourcedir': project.sourceSets.main.java.srcDirs[0], + 'endpoint-url': 'https://example.org' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/docinfo.html b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/docinfo.html new file mode 100644 index 00000000000..f399886516e --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/docinfo.html @@ -0,0 +1 @@ +<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script> diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/images/sunset.jpg b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/images/sunset.jpg new file mode 100644 index 00000000000..48c9129b301 Binary files /dev/null and b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/images/sunset.jpg differ diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000000..ea4087750aa --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/index.adoc @@ -0,0 +1,60 @@ += Example Manual +Doc Writer <doc.writer@example.org> +2014-09-09 +:example-caption!: +ifndef::imagesdir[:imagesdir: images] +ifndef::sourcedir[:sourcedir: ../java] + +This is a user manual for an example project. + +== Introduction + +This project does something. +We just haven't decided what that is yet. + +== Source Code + +[source,java] +.Java code from project +---- +include::{sourcedir}/example/StringUtils.java[tags=contains,indent=0] +---- + +This page was built by the following command: + + $ ./gradlew asciidoctor + +== Images + +[.thumb] +image::sunset.jpg[scaledwidth=75%] + +== Attributes + +.Built-in +asciidoctor-version:: {asciidoctor-version} +safe-mode-name:: {safe-mode-name} +docdir:: {docdir} +docfile:: {docfile} +imagesdir:: {imagesdir} +revnumber:: {revnumber} + +.Custom +sourcedir:: {sourcedir} +endpoint-url:: {endpoint-url} + +== Includes + +.include::subdir/_b.adoc[] +==== +include::subdir/_b.adoc[] +==== + +WARNING: Includes can be tricky! + +== build.gradle + +[source,groovy] +---- +include::{build-gradle}[] +---- diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_b.adoc b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_b.adoc new file mode 100644 index 00000000000..422eb5ee040 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_b.adoc @@ -0,0 +1,7 @@ +content from _src/docs/asciidoc/subdir/_b.adoc_. + +.include::_c.adoc[] +[example] +-- +include::_c.adoc[] +-- diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_c.adoc b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_c.adoc new file mode 100644 index 00000000000..3aca1736362 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/docs/asciidoc/subdir/_c.adoc @@ -0,0 +1 @@ +content from _src/docs/asciidoc/subdir/c.adoc_. diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/main/java/example/StringUtils.java b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/main/java/example/StringUtils.java new file mode 100644 index 00000000000..3884f862bb6 --- /dev/null +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/src/main/java/example/StringUtils.java @@ -0,0 +1,9 @@ +package example; + +public class StringUtils { + // tag::contains[] + public boolean contains(String haystack, String needle) { + return haystack.contains(needle); + } + // end::contains[] +} diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/build.gradle b/buildSrc/src/test/resources/samples/testsconfiguration/build.gradle new file mode 100644 index 00000000000..364b3bd00c1 --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/build.gradle @@ -0,0 +1,4 @@ +plugins { + id 'io.spring.convention.tests-configuration' + id 'java' +} diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/core/build.gradle b/buildSrc/src/test/resources/samples/testsconfiguration/core/build.gradle new file mode 100644 index 00000000000..3cad4d41a1a --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/core/build.gradle @@ -0,0 +1,2 @@ +apply plugin: 'io.spring.convention.tests-configuration' +apply plugin: 'java' // second to test ordering diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/core/src/test/java/sample/Dependency.java b/buildSrc/src/test/resources/samples/testsconfiguration/core/src/test/java/sample/Dependency.java new file mode 100644 index 00000000000..e0dcbbe4024 --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/core/src/test/java/sample/Dependency.java @@ -0,0 +1,3 @@ +package sample; + +public class Dependency {} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/settings.gradle b/buildSrc/src/test/resources/samples/testsconfiguration/settings.gradle new file mode 100644 index 00000000000..eadf90b2949 --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/settings.gradle @@ -0,0 +1,2 @@ +include ':core' +include ':web' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/web/build.gradle b/buildSrc/src/test/resources/samples/testsconfiguration/web/build.gradle new file mode 100644 index 00000000000..44d134d9b4d --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/web/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'java' + +repositories { + mavenCentral() +} + +dependencies { + testCompile project(path: ':core', configuration: 'tests') + testCompile 'junit:junit:4.12' +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/testsconfiguration/web/src/test/java/sample/DependencyTest.java b/buildSrc/src/test/resources/samples/testsconfiguration/web/src/test/java/sample/DependencyTest.java new file mode 100644 index 00000000000..8c378643209 --- /dev/null +++ b/buildSrc/src/test/resources/samples/testsconfiguration/web/src/test/java/sample/DependencyTest.java @@ -0,0 +1,10 @@ +package sample; + +import org.junit.*; + +public class DependencyTest { + @Test + public void findsDependencyOnClasspath() { + new Dependency(); + } +} \ No newline at end of file diff --git a/buildSrc/src/test/resources/test-private.pgp b/buildSrc/src/test/resources/test-private.pgp new file mode 100644 index 00000000000..b56a134d675 --- /dev/null +++ b/buildSrc/src/test/resources/test-private.pgp @@ -0,0 +1,83 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQWGBF7sxWEBDADekNzn2KDfbb8QGPTHZLzqvNgfYVGXIWLFfEWsA0wTPn3YQh78 +vhsK+nq598R5Rjt2s4lm/L8y5eQ4GWpok9wu5gna4s+nHbdwDjJKoXA/GVN8Y/oi +g37CafIqWACdzGpN5fjvblsfrNjVmwLdgq2kYoYqduOtiFeeQDivRJdZp3417e3C +xAdarksMkWOuDKD7JQU46ofxoMX5+WsvZ0DYuKybXAiVhNTpn3rl/4MIvu6XlMBS +EdLCdQkrdiSs6dBt8iQWkSDmwl4qaFyOmtlwjtzpJ+mPYpTWWWN3ukBRCM/AJRqm +IgLXnYnrvTmEmuZwfcZrLGwPDa30aaCK5Jjq4WLD1lwpFBuZMT46saLA+y8CvcSW +8vPAvQFzSrbBaEGu4ZIpU2aWBRW4JYpN+l90RFoWfdPyxrQ4rmPp4BIoEG25+zxI +8XMJNwr4T/t8C4I6YQfCKqnyeeR94ZF8JjGJh3Ts6nLHDN84IZbeJRypvYpcTPAD +HZiWnk4QREuSyhEAEQEAAf4HAwLsRiB8Oo4Nef+LrgBEtQ86fdfs5mTbxPv+XNGb +Wugl7HtIbgjhcmDw1zaWt9B7PKhSn2FQwJQduijE/Ae7OmNkFBQoeUeN0QADlarA +Xb5dlANIbfEJk/9KR769YL9HTy6y25LxrfgH3mrV2848dA/ilgv/WGmAz0SGER1t +OKvgOfGrmQECKbw7+U9EyEPl7nWRgJrkRK8/zgMnAA1TuXG3Rr3FvuUlw51MnBxx +sYOj6A7Xk0ijvh1szMwcUwVtZWDAiRFVhMcgopf7jCKs1UlrWJ98sa7WNXje25Mo +WXZ8VRA1/FbM/ifzuICOmKc7C+rNff7H3U7PBwRCGJrG6m0EJ/Cwrko5uHVq+fG5 +s2Vl7817ztM3/rkmNkC9jmE/sKHlkPlVc9hZGPTbqBC6BThpbBzNLUoTPmAgzbqU +hgW2ES938D7ipt7IzhRxTitQN8a1JRk8v710g/D2skLhTx9T3SkJyJkXbmOVJJXc +IN/mtlzRykR/c/TGOsMQR6v+nz42jZ1AY406AKMjd1R15wamscg4d457qVxn89T0 +wne+eShON8vPNpu1bAaN8soHxC5a4eVnLNVguxs+Abc0x/qwpwvd0VOMd86Aukte +YsS+fzGSSpX9PqHVNThK9Z1dU13J7RONeO+2xZnThhiMyGvpqL5PQ3lE3P4zkccB +jlQuuHW/6jD0/W1tlPPcsTcWDS4Ku7xxXg+ZSR3LfC4ukBWQLGiYNBWA2Zi1zW0A +xXB9kdP9MJcxdBlsUa+xVrI71LcWaREGA/O5KGMmTXQaJJNvp/2bCauAIE8AJEN9 +MrGLe+SBu3n0PJxpCB3iqLXNe9nebf0I4E8uisKB6zcQMFMEWxI5R3oRweVKYIBo +8Le5tlJ9DSwvwTI0tyDMW7Y9KCrAcIpz7tx0cIn4RzMGzCtDaLpwDk973jUQcFeE +OQBLCWPm2pIuoKO4RkIHaqhYXc9SYG3E+qQ481DohAoB/Gr3Vf80wQyIGAorg4wM +OhYmFp5pl8XDDpa7Sth8w/0flA08IRA466IYaBAhuP0ug+rhMjNfLwy+anurUd9f +M3UD21WqIUEscN5SkPI2OhGCXf5rbAQ+PkyrbgHXZpW29fGeUrAEP7FBWnCxylvL +ijP/camoJLlOzr5CKi1TTkhitbfSMH1jQosw6JeiHtbnvCyrqNbPYw57gkpRH4FD +Iql0TGAiHRe8l4s0xW79oiqN6vWKICCkekwcuc3NdNFsyxjtPdWI3ni0FulkHX0Y +O+R0Ge+uwoPKiwCT6POzn1sPP6q8kbcvP0QmkNAx7b/JKRPEoe4u9oUNc5qS9gEC +GxzvbXqq7Zv7UmYcTfNSjEWluIy0SYkVB7QkU3ByaW5nICh0ZXN0aW5nKSA8bm9y +ZXBseUBzcHJpbmcuaW8+iQHOBBMBCgA4FiEEkkystQ8WdKVRsEMQjGaXsjMvWqAF +Al7sxWECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQjGaXsjMvWqCNTAwA +pg4/XCj0vqdeVNepw4p4mU/62yu2pKXh5usU2BcEvD8dEidUpGRJkyt5n1vWzvC6 +8pbmn+Qa/6QKyL300RHeb3lpKQXbrtx3WWAMIO/JXh5lLtNaytw/N1IpqIVpg85G +L27sjeGwInQ4MleLzKykxygIsqZYUE3KWCb75JmgtEQiyaivAmDxlFClaav35LQp +aAyX2jrRPxPcD3qEL8JwDRpIlYwi1rcJgun9HIPAdma4AIarBBlEmEcXUus4rNAl +5vVgzB+v6dH3V+TiBL2QpepSoj4snA0iVyxGmLhhuuLf5gdb5sQgBY+BVxmqEji/ +is9/Steis5d25JG5G2qaflv5H00aZBJeCsARKkhBMKqBILHAAkBdr611gGLcHusq +T4GdhYx4BJK1mHjgY08pDV+My4xnwz1wcsS3iAl7efCu8X2lfe9ju+sV1EhXAtj0 +XAIaHz1k8bN4dPZDZG8Lk+2kH6WDLr7EOIsJCCsBp4HUFrxUUEdYaUsNDFxKxmmA +nQWGBF7sxWEBDADRHc69S/XH4yDv7Msg7OWW2eEBKkFV7i6lMHCp+lNBqNtxx83Z +ww8BRzqJZqvPRw219hwsVw7XN9YB1c8k5bw14mPx/VMK84oChPKRF58K9Ak8hyV5 +3BSrd4N+DKPYut0l+WhHTfkPIguFGeHp8Kg/GphAlK2eieE8vzrwrUZdNqWCBbuF ++JqU84g9XIMUtfPSIwbnaRh6DkU59cbEaHYl4ltr5+ZExhHypMKt4G0sJu3Vo1iq +nA4NiB1rUwzsvB5NUvTErLDKHscdeKSfbf7VeMe/Oqaa5EHWQuDVWBMDhyqRt4bV +kdHnHogjtNT8Ose2eYgqY+8rzJ1vs84DUuo6Osd/ATc0K6jodZpueYN1NQEP9l2s +VsjJzT4rFb6RUglnEyp4LANrz4ogkx9Y9wnvti0Z3vtLQyAf/DmXBKXzos5lRLBw +Ju+zqUUe6fww11LaftKdSI9yzhAP2ZnZFZ1FtVvuypLBrhZVsiYyRnDN6XhPxJgq +2A6yIoTNaq/xIA0AEQEAAf4HAwJBDiLQbcpQ8v9Dq9OdYRBZQxhMbhpVB45r8BeR +obnNmZWHRrZcQ7xqaqqWLEb8dtqvAR6lo7c6uZNSpzW59s72HCCvNLm8W8J6iMIb +oPD5Idbk1a0YXETUQOO5MP4NGzb/g6MInXhMM3TeC66YeZecXHiWwdDYk9nSiH7Q +207vKvDYCxNC1wIV8UqPI7ck0ekAczJB9PlpFDXD2W0JX/JB0hnBAIKKEsVshPEv +FKlDXOrjx2fKV1kzrB9fSewfaKW5MrqWEEElikPwoo5o+mv57ClkhE6TmGqPSA+t +4kuTOlQwtVwlnLn0n0uyagRDs1eFUFuNXhFlAPAHbSMQeEVboYn7m9h7MYlVJwIT +4N/8w2cEnjAd/xX/O1maTxI/7MXTboCCE6j9NmLHWTX3MM/xhO248zhXbttwUkxf +2amL6QhsQiGtoyFKjhQtVTH8VVik05caEkWBOzKfldEVBrky8bacemW2EQp0uNAt +7IOwzv9uUuWOUd8yAyDb7rI4+JrYsUwFLFk49zFZrYwrn30WvaWTEkRRgL+xGfjC +W4ZLa8OmOO8O6C2sbTZyyCN0noG2IUGdIsIEhVFPIoqyqGlZ8IxF6EovZniz4sf8 +Lu0Fo8YN+5LMiS9hCXs2Guv0jaSQ3vJtxU00/hT1zCc7/x9P1G6ks79r4aKJYCwS +c/nAnT87YrXfQS3Zqa50HkbGqey7TwzwyrexC+eXgYvNhvXElG78hpPDhs/cyMcT +ApQNy3jQcXbdXeVL9AfEl6DmDR/XWtWOpz9Cpxz321IT8t6j8ZOdan1F5rgVfNbv +z7lx6AmI5GVZlTA2DBQGjmSHcfHbvo9EPcodALdSvAqwsSmuF72bJBYnkdyiO/6A +/ElYpcHv3X0NRyC93hkipnyUkoBkyWEeDJb69w9tUCleqA4Rd6TuicEIep010lMT +tbzy8wHAciN716PzfJPsnZBB2Mv9sXCzpbbZjT8TNmz6/O0d/a+LNaSCiTvjwqNd +ERvWTgp6f6kcBQZFDSzZIMN9SHNaQAHAFOFVBA0IQfelcSz/bFz21Z9f0ZQ8b3ML +FUMhx59D5Fcrvqn+5D6m8bAXw9gElRmUzi8BKyQay7JkHsQauS66yUrj2EEp94ch +8WdbE/zTNxbUWkIJxYg36EIYgrH8zf7/M00+tXk6HMZRy0wLbbJqQEh5tDV9Ht8z ++Eu3x6/VKGIFjhtIhVY7ZDCM9wFZjsbq/kQDT8PB9X9wt5o/quDo8wg8Zm0qnJkD +msPMXLh2ZVvKXiFM7WkhUYTOpxApOt+/jMGSqP2peCBqVIwQTdtQQ+wJZjx5sBmz +2eXKwEu+pmsRAsM3dtPuqXpJWUzrrcuI+okBtgQYAQoAIBYhBJJMrLUPFnSlUbBD +EIxml7IzL1qgBQJe7MVhAhsMAAoJEIxml7IzL1qg9YsMAJXUf1+CJd5mVkOZ551+ +INV3eIf+r5wXO7KoiK8CBUEnAqSNMrQ7QHTXwo9pSjuWR1O5JcRJumvZg/dj4Vox +tb/l26Y5gdyYVkzwjKA6OnuHmICB3Y6xZVNrq1FUMiDThytHbuJz7mZZugJ69lMV +ITA0iKJV+nFNP7slthTSpfP0XkQ74hnteWf+HadXU11MHFEw2Doi2xANMxzoQgy9 +8uY6tp1/07Ll/Te5Y6YB1dlXrHiuJcX7/nUvmNa13y1cq28W2fqVsNVmZPZB7/SK +DNyv6OT0iSIOBH/6AwoTzWo+Rcwr9PDKnII2fxizc3Jq75zjA+7F/Ol8fyaVrIbn +8oxh88rujnORKevIextgcrAlu+Q8dnspZ9oACqoKQM/W+mVb5ISr9Xf+qnicXWem +uGi6ofVHUZzzJsVRdrASSA6B2+Aup+PP+SuxFSok02/DkxkD2zgT8Xt/T+/usBsy +c9LeCkYBwNlcZZc7jWAJZ6Tt514F4wmJgKFgiuZ6MqBrRQ== +=1LV7 +-----END PGP PRIVATE KEY BLOCK----- \ No newline at end of file diff --git a/cas/spring-security-cas.gradle b/cas/spring-security-cas.gradle index b1358856901..68e88389940 100644 --- a/cas/spring-security-cas.gradle +++ b/cas/spring-security-cas.gradle @@ -1,18 +1,19 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-web') - compile 'org.jasig.cas.client:cas-client-core' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-web') + api 'org.jasig.cas.client:cas-client-core' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-web' optional 'com.fasterxml.jackson.core:jackson-databind' optional 'net.sf.ehcache:ehcache' provided 'javax.servlet:javax.servlet-api' - testCompile 'org.skyscreamer:jsonassert' + testImplementation 'org.skyscreamer:jsonassert' } diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index d641eda0ab3..fd44249b0f5 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -4,17 +4,23 @@ apply plugin: 'io.spring.convention.spring-module' apply plugin: 'trang' apply plugin: 'kotlin' +repositories { + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } +} + dependencies { + management platform(project(":spring-security-dependencies")) // NB: Don't add other compile time dependencies to the config module as this breaks tooling - compile project(':spring-security-core') - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' + api project(':spring-security-core') + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' optional project(':spring-security-ldap') optional project(':spring-security-messaging') - optional project(':spring-security-saml2-service-provider') + optional project(':saml2-service-provider-opensaml3') + optional project(':saml2-service-provider-opensaml4') optional project(':spring-security-oauth2-client') optional project(':spring-security-oauth2-jose') optional project(':spring-security-oauth2-resource-server') @@ -32,53 +38,56 @@ dependencies { optional'org.springframework:spring-websocket' optional 'org.jetbrains.kotlin:kotlin-reflect' optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + optional 'javax.annotation:jsr250-api' provided 'javax.servlet:javax.servlet-api' - testCompile project(':spring-security-aspects') - testCompile project(':spring-security-cas') - testCompile project(':spring-security-test') - testCompile project(path : ':spring-security-core', configuration : 'tests') - testCompile project(path : ':spring-security-oauth2-client', configuration : 'tests') - testCompile project(path : ':spring-security-oauth2-resource-server', configuration : 'tests') - testCompile project(path : ':spring-security-saml2-service-provider', configuration : 'tests') - testCompile project(path : ':spring-security-web', configuration : 'tests') - testCompile apachedsDependencies - testCompile powerMock2Dependencies - testCompile 'com.squareup.okhttp3:mockwebserver' - testCompile 'ch.qos.logback:logback-classic' - testCompile 'io.projectreactor.netty:reactor-netty' - testCompile 'io.rsocket:rsocket-transport-netty' - testCompile 'javax.annotation:jsr250-api:1.0' - testCompile 'javax.xml.bind:jaxb-api' - testCompile 'ldapsdk:ldapsdk:4.1' - testCompile('net.sourceforge.htmlunit:htmlunit') { + testImplementation project(':spring-security-aspects') + testImplementation project(':spring-security-cas') + testImplementation project(':spring-security-test') + testImplementation project(path : ':spring-security-core', configuration : 'tests') + testImplementation project(path : ':spring-security-ldap', configuration : 'tests') + testImplementation project(path : ':spring-security-oauth2-client', configuration : 'tests') + testImplementation project(path : ':spring-security-oauth2-resource-server', configuration : 'tests') + testImplementation project(path : ':saml2-service-provider-core', configuration : 'tests') + testImplementation project(path : ':saml2-service-provider-opensaml4', configuration : 'tests') + testImplementation project(path : ':spring-security-web', configuration : 'tests') + testImplementation apachedsDependencies + testImplementation powerMock2Dependencies + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation 'ch.qos.logback:logback-classic' + testImplementation 'io.projectreactor.netty:reactor-netty' + testImplementation 'io.rsocket:rsocket-transport-netty' + testImplementation 'javax.annotation:jsr250-api:1.0' + testImplementation 'javax.xml.bind:jaxb-api' + testImplementation 'ldapsdk:ldapsdk:4.1' + testImplementation('net.sourceforge.htmlunit:htmlunit') { exclude group: 'commons-logging', module: 'commons-logging' } - testCompile 'org.eclipse.persistence:javax.persistence' - testCompile 'org.hibernate:hibernate-entitymanager' - testCompile 'org.hsqldb:hsqldb' - testCompile ('org.openid4java:openid4java-nodeps') { + testImplementation 'org.eclipse.persistence:javax.persistence' + testImplementation 'org.hibernate:hibernate-entitymanager' + testImplementation 'org.hsqldb:hsqldb' + testImplementation ('org.openid4java:openid4java-nodeps') { exclude group: 'com.google.code.guice', module: 'guice' } - testCompile('org.seleniumhq.selenium:htmlunit-driver') { + testImplementation('org.seleniumhq.selenium:htmlunit-driver') { exclude group: 'commons-logging', module: 'commons-logging' } - testCompile('org.seleniumhq.selenium:selenium-java') { + testImplementation('org.seleniumhq.selenium:selenium-java') { exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'io.netty', module: 'netty' } - testCompile 'org.slf4j:jcl-over-slf4j' - testCompile 'org.springframework.ldap:spring-ldap-core' - testCompile 'org.springframework:spring-expression' - testCompile 'org.springframework:spring-jdbc' - testCompile 'org.springframework:spring-orm' - testCompile 'org.springframework:spring-tx' - testCompile ('org.springframework.data:spring-data-jpa') { + testImplementation 'org.slf4j:jcl-over-slf4j' + testImplementation 'org.springframework.ldap:spring-ldap-core' + testImplementation 'org.springframework:spring-expression' + testImplementation 'org.springframework:spring-jdbc' + testImplementation 'org.springframework:spring-orm' + testImplementation 'org.springframework:spring-tx' + testImplementation ('org.springframework.data:spring-data-jpa') { exclude group: 'org.aspectj', module: 'aspectjrt' } - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.java new file mode 100644 index 00000000000..c64a4355be7 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.annotation.method.configuration; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.Ordered; +import org.springframework.security.access.annotation.Secured; + +/** + * Enables Spring Security Method Security. + * @author Evgeniy Cheban + * @since 5.5 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(MethodSecuritySelector.class) +@Configuration +public @interface EnableMethodSecurity { + + /** + * Determines if Spring Security's {@link Secured} annotation should be enabled. + * Default is false. + * @return true if {@link Secured} annotation should be enabled false otherwise + */ + boolean securedEnabled() default false; + + /** + * Determines if JSR-250 annotations should be enabled. Default is false. + * @return true if JSR-250 should be enabled false otherwise + */ + boolean jsr250Enabled() default false; + + /** + * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to + * standard Java interface-based proxies. The default is {@code false}. <strong> + * Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>. + * <p> + * Note that setting this attribute to {@code true} will affect <em>all</em> + * Spring-managed beans requiring proxying, not just those marked with + * {@code @Cacheable}. For example, other beans marked with Spring's + * {@code @Transactional} annotation will be upgraded to subclass proxying at the same + * time. This approach has no negative impact in practice unless one is explicitly + * expecting one type of proxy vs another, e.g. in tests. + * @return true if subclass-based (CGLIB) proxies are to be created + */ + boolean proxyTargetClass() default false; + + /** + * Indicate how security advice should be applied. The default is + * {@link AdviceMode#PROXY}. + * @see AdviceMode + * @return the {@link AdviceMode} to use + */ + AdviceMode mode() default AdviceMode.PROXY; + + /** + * Indicate the ordering of the execution of the security advisor when multiple + * advices are applied at a specific joinpoint. The default is + * {@link Ordered#LOWEST_PRECEDENCE}. + * @return the order the security advisor should be applied + */ + int order() default Ordered.LOWEST_PRECEDENCE; + +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfiguration.java new file mode 100644 index 00000000000..d9af0aa132f --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfiguration.java @@ -0,0 +1,195 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.annotation.method.configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportAware; +import org.springframework.context.annotation.Role; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.authorization.method.AuthorizationMethodInterceptor; +import org.springframework.security.authorization.method.AuthorizationMethodInterceptors; +import org.springframework.security.authorization.method.DelegatingAuthorizationMethodInterceptor; +import org.springframework.security.authorization.method.Jsr250AuthorizationManager; +import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager; +import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor; +import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager; +import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor; +import org.springframework.security.authorization.method.SecuredAuthorizationManager; +import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.util.Assert; + +/** + * Base {@link Configuration} for enabling Spring Security Method Security. + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @see EnableMethodSecurity + * @since 5.5 + */ +@Configuration(proxyBeanMethods = false) +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) +final class MethodSecurityConfiguration implements ImportAware, InitializingBean { + + private MethodSecurityExpressionHandler methodSecurityExpressionHandler; + + private GrantedAuthorityDefaults grantedAuthorityDefaults; + + private AuthorizationMethodInterceptor interceptor; + + private AnnotationAttributes enableMethodSecurity; + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + DefaultPointcutAdvisor methodSecurityAdvisor() { + AuthorizationMethodInterceptor interceptor = getInterceptor(); + DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(interceptor.getPointcut(), interceptor); + advisor.setOrder(order()); + return advisor; + } + + private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() { + if (this.methodSecurityExpressionHandler == null) { + DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler(); + if (this.grantedAuthorityDefaults != null) { + methodSecurityExpressionHandler.setDefaultRolePrefix(this.grantedAuthorityDefaults.getRolePrefix()); + } + this.methodSecurityExpressionHandler = methodSecurityExpressionHandler; + } + return this.methodSecurityExpressionHandler; + } + + @Autowired(required = false) + void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) { + this.methodSecurityExpressionHandler = methodSecurityExpressionHandler; + } + + @Autowired(required = false) + void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { + this.grantedAuthorityDefaults = grantedAuthorityDefaults; + } + + private AuthorizationMethodInterceptor getInterceptor() { + if (this.interceptor != null) { + return this.interceptor; + } + List<AuthorizationMethodInterceptor> interceptors = new ArrayList<>(); + interceptors.addAll(createDefaultAuthorizationMethodBeforeAdvice()); + interceptors.addAll(createDefaultAuthorizationMethodAfterAdvice()); + return new DelegatingAuthorizationMethodInterceptor(interceptors); + } + + private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodBeforeAdvice() { + List<AuthorizationMethodInterceptor> beforeAdvices = new ArrayList<>(); + beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice()); + beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice()); + if (securedEnabled()) { + beforeAdvices.add(getSecuredAuthorizationMethodBeforeAdvice()); + } + if (jsr250Enabled()) { + beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice()); + } + return beforeAdvices; + } + + private PreFilterAuthorizationMethodInterceptor getPreFilterAuthorizationMethodBeforeAdvice() { + PreFilterAuthorizationMethodInterceptor interceptor = new PreFilterAuthorizationMethodInterceptor(); + interceptor.setExpressionHandler(getMethodSecurityExpressionHandler()); + return interceptor; + } + + private AuthorizationMethodInterceptor getPreAuthorizeAuthorizationMethodBeforeAdvice() { + PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager(); + authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); + return AuthorizationMethodInterceptors.preAuthorize(authorizationManager); + } + + private AuthorizationMethodInterceptor getSecuredAuthorizationMethodBeforeAdvice() { + return AuthorizationMethodInterceptors.secured(new SecuredAuthorizationManager()); + } + + private AuthorizationMethodInterceptor getJsr250AuthorizationMethodBeforeAdvice() { + Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager(); + if (this.grantedAuthorityDefaults != null) { + authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix()); + } + return AuthorizationMethodInterceptors.jsr250(authorizationManager); + } + + @Autowired(required = false) + void setAuthorizationMethodInterceptor(AuthorizationMethodInterceptor interceptor) { + this.interceptor = interceptor; + } + + private List<AuthorizationMethodInterceptor> createDefaultAuthorizationMethodAfterAdvice() { + List<AuthorizationMethodInterceptor> afterAdvices = new ArrayList<>(); + afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice()); + afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice()); + return afterAdvices; + } + + private AuthorizationMethodInterceptor getPostFilterAuthorizationMethodAfterAdvice() { + PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor(); + interceptor.setExpressionHandler(getMethodSecurityExpressionHandler()); + return interceptor; + } + + private AuthorizationMethodInterceptor getPostAuthorizeAuthorizationMethodAfterAdvice() { + PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager(); + authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler()); + return AuthorizationMethodInterceptors.postAuthorize(authorizationManager); + } + + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName()); + this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes); + } + + @Override + public void afterPropertiesSet() throws Exception { + if (!securedEnabled() && !jsr250Enabled()) { + return; + } + Assert.isNull(this.interceptor, + "You have specified your own advice, meaning that the annotation attributes securedEnabled and jsr250Enabled will be ignored. Please choose one or the other."); + } + + private boolean securedEnabled() { + return this.enableMethodSecurity.getBoolean("securedEnabled"); + } + + private boolean jsr250Enabled() { + return this.enableMethodSecurity.getBoolean("jsr250Enabled"); + } + + private int order() { + return this.enableMethodSecurity.getNumber("order"); + } + +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java new file mode 100644 index 00000000000..ed0df62454a --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.annotation.method.configuration; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.AdviceModeImportSelector; +import org.springframework.context.annotation.AutoProxyRegistrar; + +/** + * Dynamically determines which imports to include using the {@link EnableMethodSecurity} + * annotation. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> { + + @Override + protected String[] selectImports(AdviceMode adviceMode) { + if (adviceMode == AdviceMode.PROXY) { + return getProxyImports(); + } + throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported"); + } + + private String[] getProxyImports() { + List<String> result = new ArrayList<>(); + result.add(AutoProxyRegistrar.class.getName()); + result.add(MethodSecurityConfiguration.class.getName()); + return result.toArray(new String[0]); + } + +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index df32a9e6442..0b11a22e905 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -1530,7 +1530,7 @@ public HttpSecurity servletApi(Customizer<ServletApiConfigurer<HttpSecurity>> se } /** - * Adds CSRF support. This is activated by default when using + * Enables CSRF protection. This is activated by default when using * {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it * using: * @@ -1556,7 +1556,7 @@ public CsrfConfigurer<HttpSecurity> csrf() throws Exception { } /** - * Adds CSRF support. This is activated by default when using + * Enables CSRF protection. This is activated by default when using * {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it * using: * diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java index e49c99e0021..454272e19b6 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java @@ -176,13 +176,11 @@ public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> ob @Autowired(required = false) void setFilterChains(List<SecurityFilterChain> securityFilterChains) { - securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE); this.securityFilterChains = securityFilterChains; } @Autowired(required = false) void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) { - webSecurityCustomizers.sort(AnnotationAwareOrderComparator.INSTANCE); this.webSecurityCustomizers = webSecurityCustomizers; } @@ -214,7 +212,7 @@ public void setBeanClassLoader(ClassLoader classLoader) { } /** - * A custom verision of the Spring provided AnnotationAwareOrderComparator that uses + * A custom version of the Spring provided AnnotationAwareOrderComparator that uses * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class * instances for the {@link Order} annotation. * diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java index c27a779ea39..62bb660bbdb 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider; @@ -51,6 +52,7 @@ import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; import org.springframework.security.web.util.matcher.NegatedRequestMatcher; @@ -78,6 +80,8 @@ * authentication failures are handled * <li>{@link #bearerTokenResolver(BearerTokenResolver)} - customizes how to resolve a * bearer token from the request</li> + * <li>{@link #authenticationConverter(AuthenticationConverter)}</li> - customizes how to + * convert a bearer token authentication from the request * <li>{@link #jwt(Customizer)} - enables Jwt-encoded bearer token support</li> * <li>{@link #opaqueToken(Customizer)} - enables opaque bearer token support</li> * </ul> @@ -159,6 +163,8 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder< private BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher(); + private AuthenticationConverter authenticationConverter; + public OAuth2ResourceServerConfigurer(ApplicationContext context) { Assert.notNull(context, "context cannot be null"); this.context = context; @@ -189,6 +195,12 @@ public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver return this; } + public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) { + Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); + this.authenticationConverter = authenticationConverter; + return this; + } + public JwtConfigurer jwt() { if (this.jwtConfigurer == null) { this.jwtConfigurer = new JwtConfigurer(this.context); @@ -252,8 +264,11 @@ public void configure(H http) { AuthenticationManager authenticationManager = getAuthenticationManager(http); resolver = (request) -> authenticationManager; } + + this.authenticationConverter = getAuthenticationConverter(); + BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver); - filter.setBearerTokenResolver(bearerTokenResolver); + filter.setAuthenticationConverter(this.authenticationConverter); filter.setAuthenticationEntryPoint(this.authenticationEntryPoint); filter = postProcess(filter); http.addFilter(filter); @@ -347,6 +362,20 @@ BearerTokenResolver getBearerTokenResolver() { return this.bearerTokenResolver; } + AuthenticationConverter getAuthenticationConverter() { + if (this.authenticationConverter == null) { + if (this.context.getBeanNamesForType(BearerTokenAuthenticationConverter.class).length > 0) { + this.authenticationConverter = this.context.getBean(BearerTokenAuthenticationConverter.class); + } + else { + BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); + converter.setBearerTokenResolver(getBearerTokenResolver()); + this.authenticationConverter = converter; + } + } + return this.authenticationConverter; + } + public class JwtConfigurer { private final ApplicationContext context; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java index 4adad193d64..196878c5787 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java @@ -21,15 +21,20 @@ import javax.servlet.Filter; +import org.opensaml.core.Version; + import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider; import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory; @@ -190,7 +195,7 @@ protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingU * <li>The {@code loginProcessingUrl} is set</li> * <li>A custom login page is configured, <b>or</b></li> * <li>A default login page with all SAML 2.0 Identity Providers is configured</li> - * <li>An {@link OpenSamlAuthenticationProvider} is configured</li> + * <li>An {@link AuthenticationProvider} is configured</li> * </ul> */ @Override @@ -256,8 +261,12 @@ private AuthenticationConverter getAuthenticationConverter(B http) { } private void registerDefaultAuthenticationProvider(B http) { - OpenSamlAuthenticationProvider provider = postProcess(new OpenSamlAuthenticationProvider()); - http.authenticationProvider(provider); + if (Version.getVersion().startsWith("4")) { + http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider())); + } + else { + http.authenticationProvider(postProcess(new OpenSamlAuthenticationProvider())); + } } private void registerDefaultCsrfOverride(B http) { @@ -337,7 +346,10 @@ private Filter build(B http) { private Saml2AuthenticationRequestFactory getResolver(B http) { Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class); if (resolver == null) { - resolver = new OpenSamlAuthenticationRequestFactory(); + if (Version.getVersion().startsWith("4")) { + return new OpenSaml4AuthenticationRequestFactory(); + } + return new OpenSamlAuthenticationRequestFactory(); } return resolver; } diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java index 89e650530b7..79ccc95cd2e 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,6 @@ import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.config.Customizer; import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; @@ -107,7 +106,6 @@ import org.springframework.security.web.server.MatcherSecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.ServerAuthenticationEntryPoint; -import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter; import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; @@ -3780,30 +3778,6 @@ public ServerHttpSecurity and() { return ServerHttpSecurity.this; } - private class BearerTokenAuthenticationWebFilter extends AuthenticationWebFilter { - - private ServerAuthenticationFailureHandler authenticationFailureHandler; - - BearerTokenAuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) { - super(authenticationManager); - } - - @Override - public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { - WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain); - return super.filter(exchange, chain).onErrorResume(AuthenticationException.class, - (e) -> this.authenticationFailureHandler.onAuthenticationFailure(webFilterExchange, e)); - } - - @Override - public void setAuthenticationFailureHandler( - ServerAuthenticationFailureHandler authenticationFailureHandler) { - super.setAuthenticationFailureHandler(authenticationFailureHandler); - this.authenticationFailureHandler = authenticationFailureHandler; - } - - } - /** * Configures JWT Resource Server Support */ @@ -3880,7 +3854,7 @@ public OAuth2ResourceServerSpec and() { protected void configure(ServerHttpSecurity http) { ReactiveAuthenticationManager authenticationManager = getAuthenticationManager(); - AuthenticationWebFilter oauth2 = new BearerTokenAuthenticationWebFilter(authenticationManager); + AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager); oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter); oauth2.setAuthenticationFailureHandler( new ServerAuthenticationEntryPointFailureHandler(OAuth2ResourceServerSpec.this.entryPoint)); @@ -3985,7 +3959,7 @@ protected ReactiveOpaqueTokenIntrospector getIntrospector() { protected void configure(ServerHttpSecurity http) { ReactiveAuthenticationManager authenticationManager = getAuthenticationManager(); - AuthenticationWebFilter oauth2 = new BearerTokenAuthenticationWebFilter(authenticationManager); + AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager); oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter); oauth2.setAuthenticationFailureHandler( new ServerAuthenticationEntryPointFailureHandler(OAuth2ResourceServerSpec.this.entryPoint)); diff --git a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt index effedad6f7a..e7b485e2bbe 100644 --- a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt @@ -35,8 +35,8 @@ import org.springframework.web.server.WebFilter * fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { * return http { * authorizeExchange { - * exchange("/public", permitAll) - * exchange(anyExchange, authenticated) + * authorize("/public", permitAll) + * authorize(anyExchange, authenticated) * } * } * } @@ -207,8 +207,8 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in * fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { * return http { * authorizeExchange { - * exchange("/public", permitAll) - * exchange(anyExchange, authenticated) + * authorize("/public", permitAll) + * authorize(anyExchange, authenticated) * } * } * } diff --git a/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt index 137d459ce1c..f368522abf9 100644 --- a/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt @@ -644,6 +644,33 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer) } + /** + * Configures Remember Me authentication. + * + * Example: + * + * ``` + * @EnableWebSecurity + * class SecurityConfig : WebSecurityConfigurerAdapter() { + * + * override fun configure(http: HttpSecurity) { + * http { + * rememberMe { + * tokenValiditySeconds = 604800 + * } + * } + * } + * } + * ``` + * + * @param rememberMeConfiguration custom configuration to configure remember me + * @see [RememberMeDsl] + */ + fun rememberMe(rememberMeConfiguration: RememberMeDsl.() -> Unit) { + val rememberMeCustomizer = RememberMeDsl().apply(rememberMeConfiguration).get() + this.http.rememberMe(rememberMeCustomizer) + } + /** * Adds the [Filter] at the location of the specified [Filter] class. * diff --git a/config/src/main/kotlin/org/springframework/security/config/web/servlet/RememberMeDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/servlet/RememberMeDsl.kt new file mode 100644 index 00000000000..db69d5d4f69 --- /dev/null +++ b/config/src/main/kotlin/org/springframework/security/config/web/servlet/RememberMeDsl.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.web.servlet + +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.web.authentication.AuthenticationSuccessHandler +import org.springframework.security.web.authentication.RememberMeServices +import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository + +/** + * A Kotlin DSL to configure [HttpSecurity] Remember me using idiomatic Kotlin code. + * + * @author Ivan Pavlov + * @property authenticationSuccessHandler the [AuthenticationSuccessHandler] used after + * authentication success + * @property key the key to identify tokens + * @property rememberMeServices the [RememberMeServices] to use + * @property rememberMeParameter the HTTP parameter used to indicate to remember + * the user at time of login. Defaults to 'remember-me' + * @property rememberMeCookieName the name of cookie which store the token for + * remember me authentication. Defaults to 'remember-me' + * @property rememberMeCookieDomain the domain name within which the remember me cookie + * is visible + * @property tokenRepository the [PersistentTokenRepository] to use. Defaults to + * [org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices] instead + * @property userDetailsService the [UserDetailsService] used to look up the UserDetails + * when a remember me token is valid + * @property tokenValiditySeconds how long (in seconds) a token is valid for. + * Defaults to 2 weeks + * @property useSecureCookie whether the cookie should be flagged as secure or not + * @property alwaysRemember whether the cookie should always be created even if + * the remember-me parameter is not set. Defaults to `false` + */ +@SecurityMarker +class RememberMeDsl { + var authenticationSuccessHandler: AuthenticationSuccessHandler? = null + var key: String? = null + var rememberMeServices: RememberMeServices? = null + var rememberMeParameter: String? = null + var rememberMeCookieName: String? = null + var rememberMeCookieDomain: String? = null + var tokenRepository: PersistentTokenRepository? = null + var userDetailsService: UserDetailsService? = null + var tokenValiditySeconds: Int? = null + var useSecureCookie: Boolean? = null + var alwaysRemember: Boolean? = null + + internal fun get(): (RememberMeConfigurer<HttpSecurity>) -> Unit { + return { rememberMe -> + authenticationSuccessHandler?.also { rememberMe.authenticationSuccessHandler(authenticationSuccessHandler) } + key?.also { rememberMe.key(key) } + rememberMeServices?.also { rememberMe.rememberMeServices(rememberMeServices) } + rememberMeParameter?.also { rememberMe.rememberMeParameter(rememberMeParameter) } + rememberMeCookieName?.also { rememberMe.rememberMeCookieName(rememberMeCookieName) } + rememberMeCookieDomain?.also { rememberMe.rememberMeCookieDomain(rememberMeCookieDomain) } + tokenRepository?.also { rememberMe.tokenRepository(tokenRepository) } + userDetailsService?.also { rememberMe.userDetailsService(userDetailsService) } + tokenValiditySeconds?.also { rememberMe.tokenValiditySeconds(tokenValiditySeconds!!) } + useSecureCookie?.also { rememberMe.useSecureCookie(useSecureCookie!!) } + alwaysRemember?.also { rememberMe.alwaysRemember(alwaysRemember!!) } + } + } +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfigurationTests.java new file mode 100644 index 00000000000..ea8c88597cf --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfigurationTests.java @@ -0,0 +1,416 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.annotation.method.configuration; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.JdkRegexpMethodPointcut; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.access.annotation.BusinessService; +import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; +import org.springframework.security.authorization.method.AuthorizationMethodInterceptor; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.security.core.Authentication; +import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/** + * Tests for {@link MethodSecurityConfiguration}. + * + * @author Evgeniy Cheban + */ +@RunWith(SpringRunner.class) +@SecurityTestExecutionListeners +public class MethodSecurityConfigurationTests { + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired(required = false) + MethodSecurityService methodSecurityService; + + @Autowired(required = false) + BusinessService businessService; + + @WithMockUser(roles = "ADMIN") + @Test + public void preAuthorizeWhenRoleAdminThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize) + .withMessage("Access Denied"); + } + + @WithAnonymousUser + @Test + public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + String result = this.methodSecurityService.preAuthorizePermitAll(); + assertThat(result).isNull(); + } + + @WithAnonymousUser + @Test + public void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous).withMessage("Access Denied"); + } + + @WithMockUser + @Test + public void preAuthorizeNotAnonymousWhenRoleUserThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + this.methodSecurityService.preAuthorizeNotAnonymous(); + } + + @WithMockUser + @Test + public void securedWhenRoleUserThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured) + .withMessage("Access Denied"); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void securedWhenRoleAdminThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + String result = this.methodSecurityService.secured(); + assertThat(result).isNull(); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void securedUserWhenRoleAdminThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) + .withMessage("Access Denied"); + } + + @WithMockUser + @Test + public void securedUserWhenRoleUserThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + String result = this.methodSecurityService.securedUser(); + assertThat(result).isNull(); + } + + @WithMockUser + @Test + public void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin) + .withMessage("Access Denied"); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void preAuthorizeAdminWhenRoleAdminThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + this.methodSecurityService.preAuthorizeAdmin(); + } + + @WithMockUser + @Test + public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() { + this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(() -> this.methodSecurityService.postHasPermission("deny")).withMessage("Access Denied"); + } + + @WithMockUser + @Test + public void postHasPermissionWhenParameterIsGrantThenPasses() { + this.spring.register(CustomPermissionEvaluatorConfig.class, MethodSecurityServiceConfig.class).autowire(); + String result = this.methodSecurityService.postHasPermission("grant"); + assertThat(result).isNull(); + } + + @WithMockUser + @Test + public void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(() -> this.methodSecurityService.postAnnotation("deny")).withMessage("Access Denied"); + } + + @WithMockUser + @Test + public void postAnnotationWhenParameterIsGrantThenPasses() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + String result = this.methodSecurityService.postAnnotation("grant"); + assertThat(result).isNull(); + } + + @WithMockUser("bob") + @Test + public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() { + this.spring.register(BusinessServiceConfig.class).autowire(); + List<String> names = new ArrayList<>(); + names.add("bob"); + names.add("joe"); + names.add("sam"); + List<?> result = this.businessService.methodReturningAList(names); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo("bob"); + } + + @WithMockUser("bob") + @Test + public void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() { + this.spring.register(BusinessServiceConfig.class).autowire(); + List<String> names = new ArrayList<>(); + names.add("bob"); + names.add("joe"); + names.add("sam"); + Object[] result = this.businessService.methodReturningAnArray(names.toArray()); + assertThat(result).hasSize(1); + assertThat(result[0]).isEqualTo("bob"); + } + + @WithMockUser("bob") + @Test + public void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() { + this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class) + .autowire(); + String result = this.methodSecurityService.securedUser(); + assertThat(result).isNull(); + } + + @WithMockUser("joe") + @Test + public void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() { + this.spring.register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceConfig.class) + .autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) + .withMessage("Access Denied"); + } + + @WithMockUser("bob") + @Test + public void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() { + this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class) + .autowire(); + String result = this.methodSecurityService.securedUser(); + assertThat(result).isEqualTo("granted"); + } + + @WithMockUser("joe") + @Test + public void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() { + this.spring.register(CustomAuthorizationManagerAfterAdviceConfig.class, MethodSecurityServiceConfig.class) + .autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser) + .withMessage("Access Denied for User 'joe'"); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void jsr250WhenRoleAdminThenAccessDeniedException() { + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250) + .withMessage("Access Denied"); + } + + @WithAnonymousUser + @Test + public void jsr250PermitAllWhenRoleAnonymousThenPasses() { + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + String result = this.methodSecurityService.jsr250PermitAll(); + assertThat(result).isNull(); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() { + this.spring.register(BusinessServiceConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser) + .withMessage("Access Denied"); + } + + @WithMockUser + @Test + public void rolesAllowedUserWhenRoleUserThenPasses() { + this.spring.register(BusinessServiceConfig.class).autowire(); + this.businessService.rolesAllowedUser(); + } + + @WithMockUser(roles = { "ADMIN", "USER" }) + @Test + public void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception { + List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo"); + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names)); + assertThat(filtered).hasSize(2); + assertThat(filtered).containsExactly("harold", "jonathan"); + } + + @WithMockUser + @Test + public void manyAnnotationsWhenUserThenFails() { + List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo"); + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names))); + } + + @WithMockUser + @Test + public void manyAnnotationsWhenShortListThenFails() { + List<String> names = Arrays.asList("harold", "jonathan", "pete"); + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names))); + } + + @WithMockUser(roles = "ADMIN") + @Test + public void manyAnnotationsWhenAdminThenFails() { + List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo"); + this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire(); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names))); + } + + @Test + public void configureWhenCustomAdviceAndSecureEnabledThenException() { + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring + .register(CustomAuthorizationManagerBeforeAdviceConfig.class, MethodSecurityServiceEnabledConfig.class) + .autowire()); + } + + @EnableMethodSecurity + static class MethodSecurityServiceConfig { + + @Bean + MethodSecurityService methodSecurityService() { + return new MethodSecurityServiceImpl(); + } + + } + + @EnableMethodSecurity(jsr250Enabled = true) + static class BusinessServiceConfig { + + @Bean + BusinessService businessService() { + return new ExpressionProtectedBusinessServiceImpl(); + } + + } + + @EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true) + static class MethodSecurityServiceEnabledConfig { + + @Bean + MethodSecurityService methodSecurityService() { + return new MethodSecurityServiceImpl(); + } + + } + + @EnableMethodSecurity + static class CustomPermissionEvaluatorConfig { + + @Bean + MethodSecurityExpressionHandler methodSecurityExpressionHandler() { + DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + expressionHandler.setPermissionEvaluator(new PermissionEvaluator() { + @Override + public boolean hasPermission(Authentication authentication, Object targetDomainObject, + Object permission) { + return "grant".equals(targetDomainObject); + } + + @Override + public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, + Object permission) { + throw new UnsupportedOperationException(); + } + }); + return expressionHandler; + } + + } + + @EnableMethodSecurity + static class CustomAuthorizationManagerBeforeAdviceConfig { + + @Bean + AuthorizationMethodInterceptor customBeforeAdvice() { + JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); + pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser"); + AuthorizationManager<MethodInvocation> authorizationManager = (a, + o) -> new AuthorizationDecision("bob".equals(a.get().getName())); + return new AuthorizationManagerBeforeMethodInterceptor(pointcut, authorizationManager); + } + + } + + @EnableMethodSecurity + static class CustomAuthorizationManagerAfterAdviceConfig { + + @Bean + + AuthorizationMethodInterceptor customAfterAdvice() { + JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); + pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser"); + AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor() { + @Override + public Pointcut getPointcut() { + return pointcut; + } + + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) { + Authentication auth = authentication.get(); + if ("bob".equals(auth.getName())) { + return "granted"; + } + throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'"); + } + }; + return interceptor; + } + + } + +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java index 525ce2a4771..4aab407ea29 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java @@ -16,12 +16,16 @@ package org.springframework.security.config.annotation.method.configuration; +import java.util.List; + import javax.annotation.security.DenyAll; import javax.annotation.security.PermitAll; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.access.prepost.PostFilter; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.access.prepost.PreFilter; import org.springframework.security.core.Authentication; import org.springframework.security.core.parameters.P; @@ -69,4 +73,11 @@ public interface MethodSecurityService { @PostAuthorize("#o?.contains('grant')") String postAnnotation(@P("o") String object); + @PreFilter("filterObject.length > 3") + @PreAuthorize("hasRole('ADMIN')") + @Secured("ROLE_USER") + @PostFilter("filterObject.length > 5") + @PostAuthorize("returnObject.size > 1") + List<String> manyAnnotations(List<String> array); + } diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java index 94a05216bc1..6bf562cfebb 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java @@ -16,6 +16,8 @@ package org.springframework.security.config.annotation.method.configuration; +import java.util.List; + import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -86,4 +88,9 @@ public String postAnnotation(String object) { return null; } + @Override + public List<String> manyAnnotations(List<String> object) { + return object; + } + } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java index 14a7b3adbfe..d877bc222e0 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java @@ -19,8 +19,12 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.List; +import javax.servlet.Filter; +import javax.servlet.http.HttpServletRequest; + import org.junit.Rule; import org.junit.Test; @@ -131,6 +135,19 @@ public void loadConfigWhenSecurityFilterChainsHaveOrderThenFilterChainsOrdered() assertThat(filterChains.get(3).matches(request)).isTrue(); } + @Test + public void loadConfigWhenSecurityFilterChainsHaveOrderOnBeanDefinitionsThenFilterChainsOrdered() { + this.spring.register(OrderOnBeanDefinitionsSecurityFilterChainConfig.class).autowire(); + FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class); + List<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains(); + assertThat(filterChains).hasSize(2); + MockHttpServletRequest request = new MockHttpServletRequest("GET", ""); + request.setServletPath("/role1/**"); + assertThat(filterChains.get(0).matches(request)).isTrue(); + request.setServletPath("/role2/**"); + assertThat(filterChains.get(1).matches(request)).isTrue(); + } + @Test public void loadConfigWhenWebSecurityConfigurersHaveSameOrderThenThrowBeanCreationException() { assertThatExceptionOfType(BeanCreationException.class) @@ -487,6 +504,45 @@ SecurityFilterChain filterChain4(HttpSecurity http) throws Exception { } + @EnableWebSecurity + @Import(AuthenticationTestConfiguration.class) + static class OrderOnBeanDefinitionsSecurityFilterChainConfig { + + @Bean + @Order(1) + SecurityFilterChain securityFilterChain1(HttpSecurity http) throws Exception { + // @formatter:off + return http + .antMatcher("/role1/**") + .authorizeRequests((authorize) -> authorize + .anyRequest().hasRole("1") + ) + .build(); + // @formatter:on + } + + @Bean + TestSecurityFilterChain securityFilterChain2(HttpSecurity http) throws Exception { + return new TestSecurityFilterChain(); + } + + @Order(2) + static class TestSecurityFilterChain implements SecurityFilterChain { + + @Override + public boolean matches(HttpServletRequest request) { + return true; + } + + @Override + public List<Filter> getFilters() { + return new ArrayList<>(); + } + + } + + } + @EnableWebSecurity @Import(AuthenticationTestConfiguration.class) static class DuplicateOrderConfig { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index c36a2712b6b..d19091d64e6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import java.util.stream.Collectors; import javax.annotation.PreDestroy; +import javax.servlet.http.HttpServletRequest; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; @@ -78,6 +79,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManagerResolver; import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -104,11 +106,12 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.security.oauth2.jwt.JwtTimestampValidator; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.jwt.TestJwts; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver; @@ -260,26 +263,24 @@ public void getWhenUsingDefaultsWithExpiredBearerTokenThenInvalidToken() throws } @Test - public void getWhenUsingDefaultsWithBadJwkEndpointThenInvalidToken() throws Exception { + public void getWhenUsingDefaultsWithBadJwkEndpointThen500() throws Exception { this.spring.register(RestOperationsConfig.class, DefaultConfig.class).autowire(); mockRestOperations("malformed"); String token = this.token("ValidNoScopes"); // @formatter:off - this.mvc.perform(get("/").with(bearerToken(token))) - .andExpect(status().isUnauthorized()) - .andExpect(header().string("WWW-Authenticate", "Bearer")); + assertThatExceptionOfType(AuthenticationServiceException.class) + .isThrownBy(() -> this.mvc.perform(get("/").with(bearerToken(token)))); // @formatter:on } @Test - public void getWhenUsingDefaultsWithUnavailableJwkEndpointThenInvalidToken() throws Exception { + public void getWhenUsingDefaultsWithUnavailableJwkEndpointThen500() throws Exception { this.spring.register(WebServerConfig.class, JwkSetUriConfig.class).autowire(); this.web.shutdown(); String token = this.token("ValidNoScopes"); // @formatter:off - this.mvc.perform(get("/").with(bearerToken(token))) - .andExpect(status().isUnauthorized()) - .andExpect(header().string("WWW-Authenticate", "Bearer")); + assertThatExceptionOfType(AuthenticationServiceException.class) + .isThrownBy(() -> this.mvc.perform(get("/").with(bearerToken(token)))); // @formatter:on } @@ -722,6 +723,71 @@ public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class); } + @Test + public void getBearerTokenAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() { + BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter(); + BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); + GenericWebApplicationContext context = new GenericWebApplicationContext(); + context.registerBean("converterOne", BearerTokenAuthenticationConverter.class, () -> converterBean); + context.registerBean("converterTwo", BearerTokenAuthenticationConverter.class, () -> converterBean); + this.spring.context(context).autowire(); + OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context); + oauth2.authenticationConverter(converter); + assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter); + } + + @Test + public void getBearerTokenAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() { + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring + .register(MultipleBearerTokenAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class) + .autowire()).withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class); + } + + @Test + public void getBearerTokenAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() { + BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); + BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter(); + GenericWebApplicationContext context = new GenericWebApplicationContext(); + context.registerBean(BearerTokenAuthenticationConverter.class, () -> converterBean); + this.spring.context(context).autowire(); + OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context); + oauth2.authenticationConverter(converter); + assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter); + } + + @Test + public void getBearerTokenAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() { + ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext(); + OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context); + assertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class); + } + + @Test + public void getBearerTokenAuthenticationConverterWhenConverterBeanRegisteredThenBeanIsUsed() { + BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter(); + GenericWebApplicationContext context = new GenericWebApplicationContext(); + context.registerBean(BearerTokenAuthenticationConverter.class, () -> converterBean); + this.spring.context(context).autowire(); + OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context); + assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converterBean); + + } + + @Test + public void getBearerTokenAuthenticationConverterWhenOnlyResolverBeanRegisteredThenUseTheResolver() { + HttpServletRequest servletRequest = mock(HttpServletRequest.class); + BearerTokenResolver resolverBean = (request) -> "bearer customToken"; + GenericWebApplicationContext context = new GenericWebApplicationContext(); + context.registerBean(BearerTokenResolver.class, () -> resolverBean); + this.spring.context(context).autowire(); + OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context); + BearerTokenAuthenticationToken bearerTokenAuthenticationToken = (BearerTokenAuthenticationToken) oauth2 + .getAuthenticationConverter().convert(servletRequest); + String token = bearerTokenAuthenticationToken.getToken(); + assertThat(token).isEqualTo("bearer customToken"); + + } + @Test public void requestWhenCustomJwtDecoderWiredOnDslThenUsed() throws Exception { this.spring.register(CustomJwtDecoderOnDsl.class, BasicController.class).autowire(); @@ -825,7 +891,7 @@ public void getJwtDecoderWhenTwoJwtDecoderBeansThenThrowsException() { public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception { this.spring.register(RealmNameConfiguredOnEntryPoint.class, JwtDecoderConfig.class).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - given(decoder.decode(anyString())).willThrow(JwtException.class); + given(decoder.decode(anyString())).willThrow(BadJwtException.class); // @formatter:off this.mvc.perform(get("/authenticated").with(bearerToken("invalid_token"))) .andExpect(status().isUnauthorized()) @@ -1092,7 +1158,7 @@ public void getIntrospectionClientWhenDslAndBeanWiredThenDslTakesPrecedence() { public void requestWhenBasicAndResourceServerEntryPointsThenMatchedByRequest() throws Exception { this.spring.register(BasicAndResourceServerConfig.class, JwtDecoderConfig.class).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - given(decoder.decode(anyString())).willThrow(JwtException.class); + given(decoder.decode(anyString())).willThrow(BadJwtException.class); // @formatter:off this.mvc.perform(get("/authenticated").with(httpBasic("some", "user"))) .andExpect(status().isUnauthorized()) @@ -1110,7 +1176,7 @@ public void requestWhenBasicAndResourceServerEntryPointsThenMatchedByRequest() t public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedByRequest() throws Exception { this.spring.register(FormAndResourceServerConfig.class, JwtDecoderConfig.class).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - given(decoder.decode(anyString())).willThrow(JwtException.class); + given(decoder.decode(anyString())).willThrow(BadJwtException.class); // @formatter:off MvcResult result = this.mvc.perform(get("/authenticated") .header("Accept", "text/html")) @@ -1873,6 +1939,32 @@ BearerTokenResolver resolverTwo() { } + @EnableWebSecurity + static class MultipleBearerTokenAuthenticationConverterBeansConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .oauth2ResourceServer() + .jwt(); + // @formatter:on + } + + @Bean + BearerTokenAuthenticationConverter converterOne() { + BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); + return converter; + } + + @Bean + BearerTokenAuthenticationConverter converterTwo() { + BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); + return converter; + } + + } + @EnableWebSecurity static class CustomJwtDecoderOnDsl extends WebSecurityConfigurerAdapter { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java index bad65f790c1..a04c2bbb22d 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,26 +16,24 @@ package org.springframework.security.config.annotation.web.configurers.saml2; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.Arrays; +import java.time.Instant; import java.util.Base64; import java.util.Collection; import java.util.Collections; -import java.util.zip.Inflater; -import java.util.zip.InflaterOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.opensaml.saml.saml2.core.Assertion; import org.opensaml.saml.saml2.core.AuthnRequest; @@ -61,11 +59,14 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.core.Saml2Utils; import org.springframework.security.saml2.core.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider; -import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; @@ -78,6 +79,7 @@ import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.context.HttpRequestResponseHolder; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; @@ -106,10 +108,10 @@ public class Saml2LoginConfigurerTests { private static final Converter<Assertion, Collection<? extends GrantedAuthority>> AUTHORITIES_EXTRACTOR = ( - a) -> Arrays.asList(new SimpleGrantedAuthority("TEST")); + a) -> Collections.singletonList(new SimpleGrantedAuthority("TEST")); - private static final GrantedAuthoritiesMapper AUTHORITIES_MAPPER = (authorities) -> Arrays - .asList(new SimpleGrantedAuthority("TEST CONVERTED")); + private static final GrantedAuthoritiesMapper AUTHORITIES_MAPPER = (authorities) -> Collections + .singletonList(new SimpleGrantedAuthority("TEST CONVERTED")); private static final Duration RESPONSE_TIME_VALIDATION_SKEW = Duration.ZERO; @@ -188,7 +190,7 @@ public void authenticationRequestWhenAuthnRequestContextConverterThenUses() thro UriComponents components = UriComponentsBuilder.fromHttpUrl(result.getResponse().getRedirectedUrl()).build(); String samlRequest = components.getQueryParams().getFirst("SAMLRequest"); String decoded = URLDecoder.decode(samlRequest, "UTF-8"); - String inflated = samlInflate(samlDecode(decoded)); + String inflated = Saml2Utils.samlInflate(Saml2Utils.samlDecode(decoded)); assertThat(inflated).contains("ForceAuthn=\"true\""); } @@ -199,7 +201,7 @@ public void authenticateWhenCustomAuthenticationConverterThenUses() throws Excep .assertingPartyDetails((party) -> party.verificationX509Credentials( (c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) .build(); - String response = new String(samlDecode(SIGNED_RESPONSE)); + String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE)); given(CustomAuthenticationConverter.authenticationConverter.convert(any(HttpServletRequest.class))) .willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response)); // @formatter:off @@ -210,6 +212,24 @@ public void authenticateWhenCustomAuthenticationConverterThenUses() throws Excep verify(CustomAuthenticationConverter.authenticationConverter).convert(any(HttpServletRequest.class)); } + @Test + public void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses() throws Exception { + this.spring.register(CustomAuthenticationFailureHandler.class).autowire(); + byte[] invalidDeflated = "invalid".getBytes(); + String encoded = Saml2Utils.samlEncode(invalidDeflated); + MockHttpServletRequestBuilder request = get("/login/saml2/sso/registration-id").queryParam("SAMLResponse", + encoded); + this.mvc.perform(request); + ArgumentCaptor<Saml2AuthenticationException> captor = ArgumentCaptor + .forClass(Saml2AuthenticationException.class); + verify(CustomAuthenticationFailureHandler.authenticationFailureHandler).onAuthenticationFailure( + any(HttpServletRequest.class), any(HttpServletResponse.class), captor.capture()); + Saml2AuthenticationException exception = captor.getValue(); + assertThat(exception.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE); + assertThat(exception.getSaml2Error().getDescription()).isEqualTo("Unable to inflate string"); + assertThat(exception.getCause()).isInstanceOf(IOException.class); + } + private void validateSaml2WebSsoAuthenticationFilterConfiguration() { // get the OpenSamlAuthenticationProvider Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain); @@ -217,11 +237,8 @@ private void validateSaml2WebSsoAuthenticationFilterConfiguration() { "authenticationManager"); ProviderManager pm = (ProviderManager) manager; AuthenticationProvider provider = pm.getProviders().stream() - .filter((p) -> p instanceof OpenSamlAuthenticationProvider).findFirst().get(); - Assert.assertSame(AUTHORITIES_EXTRACTOR, ReflectionTestUtils.getField(provider, "authoritiesExtractor")); - Assert.assertSame(AUTHORITIES_MAPPER, ReflectionTestUtils.getField(provider, "authoritiesMapper")); - Assert.assertSame(RESPONSE_TIME_VALIDATION_SKEW, - ReflectionTestUtils.getField(provider, "responseTimeValidationSkew")); + .filter((p) -> p instanceof OpenSaml4AuthenticationProvider).findFirst().get(); + assertThat(provider).isNotNull(); } private Saml2WebSsoAuthenticationFilter getSaml2SsoFilter(FilterChainProxy chain) { @@ -244,26 +261,6 @@ private void performSaml2Login(String expected) throws IOException, ServletExcep .hasToString(expected); } - private static org.apache.commons.codec.binary.Base64 BASE64 = new org.apache.commons.codec.binary.Base64(0, - new byte[] { '\n' }); - - private static byte[] samlDecode(String s) { - return BASE64.decode(s); - } - - private static String samlInflate(byte[] b) { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - InflaterOutputStream iout = new InflaterOutputStream(out, new Inflater(true)); - iout.write(b); - iout.finish(); - return new String(out.toByteArray(), StandardCharsets.UTF_8); - } - catch (IOException ex) { - throw new Saml2Exception("Unable to inflate string", ex); - } - } - private static AuthenticationManager getAuthenticationManagerMock(String role) { return new AuthenticationManager() { @Override @@ -314,6 +311,21 @@ public <O extends OpenSamlAuthenticationProvider> O postProcess(O provider) { } + @EnableWebSecurity + @Import(Saml2LoginConfigBeans.class) + static class CustomAuthenticationFailureHandler extends WebSecurityConfigurerAdapter { + + static final AuthenticationFailureHandler authenticationFailureHandler = mock( + AuthenticationFailureHandler.class); + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests((authz) -> authz.anyRequest().authenticated()) + .saml2Login((saml2) -> saml2.failureHandler(authenticationFailureHandler)); + } + + } + @EnableWebSecurity @Import(Saml2LoginConfigBeans.class) static class CustomAuthenticationRequestContextResolver extends WebSecurityConfigurerAdapter { @@ -357,9 +369,10 @@ protected void configure(HttpSecurity http) throws Exception { @Bean Saml2AuthenticationRequestFactory authenticationRequestFactory() { - OpenSamlAuthenticationRequestFactory authenticationRequestFactory = new OpenSamlAuthenticationRequestFactory(); + OpenSaml4AuthenticationRequestFactory authenticationRequestFactory = new OpenSaml4AuthenticationRequestFactory(); authenticationRequestFactory.setAuthenticationRequestContextConverter((context) -> { AuthnRequest authnRequest = TestOpenSamlObjects.authnRequest(); + authnRequest.setIssueInstant(Instant.now()); authnRequest.setForceAuthn(true); return authnRequest; }); diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java index 498d8f80bef..d0529ae17de 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,6 +69,7 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManagerResolver; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.JwtBeanDefinitionParser; import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.OpaqueTokenBeanDefinitionParser; import org.springframework.security.config.test.SpringTestRule; @@ -76,10 +77,10 @@ import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.security.oauth2.jose.TestKeys; +import org.springframework.security.oauth2.jwt.BadJwtException; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.jwt.TestJwts; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; @@ -168,26 +169,24 @@ public void getWhenExpiredBearerTokenThenInvalidToken() throws Exception { } @Test - public void getWhenBadJwkEndpointThenInvalidToken() throws Exception { + public void getWhenBadJwkEndpointThen500() throws Exception { this.spring.configLocations(xml("JwtRestOperations"), xml("Jwt")).autowire(); mockRestOperations("malformed"); String token = this.token("ValidNoScopes"); // @formatter:off - this.mvc.perform(get("/").header("Authorization", "Bearer " + token)) - .andExpect(status().isUnauthorized()) - .andExpect(header().string("WWW-Authenticate", "Bearer")); + assertThatExceptionOfType(AuthenticationServiceException.class) + .isThrownBy(() -> this.mvc.perform(get("/").header("Authorization", "Bearer " + token))); // @formatter:on } @Test - public void getWhenUnavailableJwkEndpointThenInvalidToken() throws Exception { + public void getWhenUnavailableJwkEndpointThen500() throws Exception { this.spring.configLocations(xml("WebServer"), xml("JwkSetUri")).autowire(); this.web.shutdown(); String token = this.token("ValidNoScopes"); // @formatter:off - this.mvc.perform(get("/").header("Authorization", "Bearer " + token)) - .andExpect(status().isUnauthorized()) - .andExpect(header().string("WWW-Authenticate", "Bearer")); + assertThatExceptionOfType(AuthenticationServiceException.class) + .isThrownBy(() -> this.mvc.perform(get("/").header("Authorization", "Bearer " + token))); // @formatter:on } @@ -529,7 +528,7 @@ public void configureWhenDecoderAndJwkSetUriThenException() { public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception { this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - Mockito.when(decoder.decode(anyString())).thenThrow(JwtException.class); + Mockito.when(decoder.decode(anyString())).thenThrow(BadJwtException.class); // @formatter:off this.mvc.perform(get("/authenticated").header("Authorization", "Bearer invalid_token")) .andExpect(status().isUnauthorized()) @@ -736,7 +735,7 @@ public void requestWhenBasicAndResourceServerEntryPointsThenBearerTokenPresides( // different from DSL this.spring.configLocations(xml("MockJwtDecoder"), xml("BasicAndResourceServer")).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - given(decoder.decode(anyString())).willThrow(JwtException.class); + given(decoder.decode(anyString())).willThrow(BadJwtException.class); // @formatter:off this.mvc.perform(get("/authenticated").with(httpBasic("some", "user"))) .andExpect(status().isUnauthorized()) @@ -755,7 +754,7 @@ public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedBy // different from DSL this.spring.configLocations(xml("MockJwtDecoder"), xml("FormAndResourceServer")).autowire(); JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class); - given(decoder.decode(anyString())).willThrow(JwtException.class); + given(decoder.decode(anyString())).willThrow(BadJwtException.class); MvcResult result = this.mvc.perform(get("/authenticated")).andExpect(status().isUnauthorized()).andReturn(); assertThat(result.getRequest().getSession(false)).isNotNull(); // @formatter:off diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java index 09c8b4ae451..0ac36558c93 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java @@ -615,7 +615,7 @@ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) { @EnableWebFluxSecurity static class PlaceholderConfig { - @Value("${classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub}") + @Value("classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub") RSAPublicKey key; @Bean diff --git a/config/src/test/kotlin/org/springframework/security/config/web/servlet/RememberMeDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/web/servlet/RememberMeDslTests.kt new file mode 100644 index 00000000000..38fa02c26a0 --- /dev/null +++ b/config/src/test/kotlin/org/springframework/security/config/web/servlet/RememberMeDslTests.kt @@ -0,0 +1,564 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.config.web.servlet + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.jupiter.api.fail +import org.mockito.BDDMockito.given +import org.mockito.Mockito.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.core.annotation.Order +import org.springframework.mock.web.MockHttpSession +import org.springframework.security.authentication.RememberMeAuthenticationToken +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter +import org.springframework.security.config.test.SpringTestRule +import org.springframework.security.core.Authentication +import org.springframework.security.core.authority.AuthorityUtils +import org.springframework.security.core.userdetails.PasswordEncodedUser +import org.springframework.security.core.userdetails.User +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf +import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers +import org.springframework.security.web.authentication.AuthenticationSuccessHandler +import org.springframework.security.web.authentication.RememberMeServices +import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices +import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken +import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository +import org.springframework.security.web.util.matcher.AntPathRequestMatcher +import org.springframework.test.web.servlet.MockHttpServletRequestDsl +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.get +import org.springframework.test.web.servlet.post +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse + +/** + * Tests for [RememberMeDsl] + * + * @author Ivan Pavlov + */ +internal class RememberMeDslTests { + @Rule + @JvmField + val spring = SpringTestRule() + + @Autowired + lateinit var mockMvc: MockMvc + + @Test + fun `Remember Me login when remember me true then responds with remember me cookie`() { + this.spring.register(RememberMeConfig::class.java).autowire() + mockMvc.post("/login") + { + loginRememberMeRequest() + }.andExpect { + cookie { + exists("remember-me") + } + } + } + + @Test + fun `Remember Me get when remember me cookie then authentication is remember me authentication token`() { + this.spring.register(RememberMeConfig::class.java).autowire() + val mvcResult = mockMvc.post("/login") + { + loginRememberMeRequest() + }.andReturn() + val rememberMeCookie = mvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in login response" } + mockMvc.get("/abc") + { + cookie(rememberMeCookie) + }.andExpect { + val rememberMeAuthentication = SecurityMockMvcResultMatchers.authenticated() + .withAuthentication { assertThat(it).isInstanceOf(RememberMeAuthenticationToken::class.java) } + match(rememberMeAuthentication) + } + } + + @Test + fun `Remember Me logout when remember me cookie then authentication is remember me cookie expired`() { + this.spring.register(RememberMeConfig::class.java).autowire() + val mvcResult = mockMvc.post("/login") + { + loginRememberMeRequest() + }.andReturn() + val rememberMeCookie = mvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in login response" } + val mockSession = mvcResult.request.session as MockHttpSession + mockMvc.post("/logout") + { + with(csrf()) + cookie(rememberMeCookie) + session = mockSession + }.andExpect { + status { isFound() } + redirectedUrl("/login?logout") + cookie { + maxAge("remember-me", 0) + } + } + } + + @Test + fun `Remember Me get when remember me cookie and logged out then redirects to login`() { + this.spring.register(RememberMeConfig::class.java).autowire() + mockMvc.perform(formLogin()) + val mvcResult = mockMvc.post("/login") + { + loginRememberMeRequest() + }.andReturn() + val rememberMeCookie = mvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in login request" } + val mockSession = mvcResult.request.session as MockHttpSession + val logoutMvcResult = mockMvc.post("/logout") + { + with(csrf()) + cookie(rememberMeCookie) + session = mockSession + }.andReturn() + val expiredRememberMeCookie = logoutMvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in logout response" } + mockMvc.get("/abc") + { + with(csrf()) + cookie(expiredRememberMeCookie) + }.andExpect { + status { isFound() } + redirectedUrl("http://localhost/login") + } + } + + @Test + fun `Remember Me login when remember me domain then remember me cookie has domain`() { + this.spring.register(RememberMeDomainConfig::class.java).autowire() + mockMvc.post("/login") + { + loginRememberMeRequest() + }.andExpect { + cookie { + domain("remember-me", "spring.io") + } + } + } + + @Test + fun `Remember Me when remember me services then uses`() { + RememberMeServicesRefConfig.REMEMBER_ME_SERVICES = mock(RememberMeServices::class.java) + this.spring.register(RememberMeServicesRefConfig::class.java).autowire() + mockMvc.get("/") + verify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES).autoLogin(any(HttpServletRequest::class.java), + any(HttpServletResponse::class.java)) + mockMvc.post("/login") { + with(csrf()) + } + verify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES).loginFail(any(HttpServletRequest::class.java), + any(HttpServletResponse::class.java)) + mockMvc.post("/login") { + loginRememberMeRequest() + } + verify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES).loginSuccess(any(HttpServletRequest::class.java), + any(HttpServletResponse::class.java), any(Authentication::class.java)) + } + + @Test + fun `Remember Me when authentication success handler then uses`() { + RememberMeSuccessHandlerConfig.SUCCESS_HANDLER = mock(AuthenticationSuccessHandler::class.java) + this.spring.register(RememberMeSuccessHandlerConfig::class.java).autowire() + val mvcResult = mockMvc.post("/login") { + loginRememberMeRequest() + }.andReturn() + verifyNoInteractions(RememberMeSuccessHandlerConfig.SUCCESS_HANDLER) + val rememberMeCookie = mvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in login response" } + mockMvc.get("/abc") { + cookie(rememberMeCookie) + } + verify(RememberMeSuccessHandlerConfig.SUCCESS_HANDLER).onAuthenticationSuccess( + any(HttpServletRequest::class.java), any(HttpServletResponse::class.java), + any(Authentication::class.java)) + } + + @Test + fun `Remember Me when key then remember me works only for matching routes`() { + this.spring.register(WithoutKeyConfig::class.java, KeyConfig::class.java).autowire() + val withoutKeyMvcResult = mockMvc.post("/without-key/login") { + loginRememberMeRequest() + }.andReturn() + val withoutKeyRememberMeCookie = withoutKeyMvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in without key login response" } + mockMvc.get("/abc") { + cookie(withoutKeyRememberMeCookie) + }.andExpect { + status { isFound() } + redirectedUrl("http://localhost/login") + } + val keyMvcResult = mockMvc.post("/login") { + loginRememberMeRequest() + }.andReturn() + val keyRememberMeCookie = keyMvcResult.response.getCookie("remember-me") + ?: fail { "Missing remember-me cookie in key login response" } + mockMvc.get("/abc") { + cookie(keyRememberMeCookie) + }.andExpect { + status { isNotFound() } + } + } + + @Test + fun `Remember Me when token repository then uses`() { + RememberMeTokenRepositoryConfig.TOKEN_REPOSITORY = mock(PersistentTokenRepository::class.java) + this.spring.register(RememberMeTokenRepositoryConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + } + verify(RememberMeTokenRepositoryConfig.TOKEN_REPOSITORY).createNewToken( + any(PersistentRememberMeToken::class.java)) + } + + @Test + fun `Remember Me when token validity seconds then cookie max age`() { + this.spring.register(RememberMeTokenValidityConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + }.andExpect { + cookie { + maxAge("remember-me", 42) + } + } + } + + @Test + fun `Remember Me when using defaults then cookie max age`() { + this.spring.register(RememberMeConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + }.andExpect { + cookie { + maxAge("remember-me", AbstractRememberMeServices.TWO_WEEKS_S) + } + } + } + + @Test + fun `Remember Me when use secure cookie then cookie secure`() { + this.spring.register(RememberMeUseSecureCookieConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + }.andExpect { + cookie { + secure("remember-me", true) + } + } + } + + @Test + fun `Remember Me when using defaults then cookie secure`() { + this.spring.register(RememberMeConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + secure = true + }.andExpect { + cookie { + secure("remember-me", true) + } + } + } + + @Test + fun `Remember Me when parameter then responds with remember me cookie`() { + this.spring.register(RememberMeParameterConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest("rememberMe") + }.andExpect { + cookie { + exists("remember-me") + } + } + } + + @Test + fun `Remember Me when cookie name then responds with remember me cookie with such name`() { + this.spring.register(RememberMeCookieNameConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + }.andExpect { + cookie { + exists("rememberMe") + } + } + } + + @Test + fun `Remember Me when global user details service then uses`() { + RememberMeDefaultUserDetailsServiceConfig.USER_DETAIL_SERVICE = mock(UserDetailsService::class.java) + this.spring.register(RememberMeDefaultUserDetailsServiceConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest() + } + verify(RememberMeDefaultUserDetailsServiceConfig.USER_DETAIL_SERVICE).loadUserByUsername("user") + } + + @Test + fun `Remember Me when user details service then uses`() { + RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE = mock(UserDetailsService::class.java) + this.spring.register(RememberMeUserDetailsServiceConfig::class.java).autowire() + val user = User("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER")) + given(RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE.loadUserByUsername("user")).willReturn(user) + mockMvc.post("/login") { + loginRememberMeRequest() + } + verify(RememberMeUserDetailsServiceConfig.USER_DETAIL_SERVICE).loadUserByUsername("user") + } + + @Test + fun `Remember Me when always remember then remembers without HTTP parameter`() { + this.spring.register(RememberMeAlwaysRememberConfig::class.java).autowire() + mockMvc.post("/login") { + loginRememberMeRequest(rememberMeValue = null) + }.andExpect { + cookie { + exists("remember-me") + } + } + } + + private fun MockHttpServletRequestDsl.loginRememberMeRequest(rememberMeParameter: String = "remember-me", + rememberMeValue: Boolean? = true) { + with(csrf()) + param("username", "user") + param("password", "password") + rememberMeValue?.also { + param(rememberMeParameter, rememberMeValue.toString()) + } + } + + abstract class DefaultUserConfig : WebSecurityConfigurerAdapter() { + @Autowired + open fun configureGlobal(auth: AuthenticationManagerBuilder) { + auth.inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()) + } + } + + @EnableWebSecurity + open class RememberMeConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, hasRole("USER")) + } + formLogin {} + rememberMe {} + } + } + } + + @EnableWebSecurity + open class RememberMeDomainConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, hasRole("USER")) + } + formLogin {} + rememberMe { + rememberMeCookieDomain = "spring.io" + } + } + } + } + + @EnableWebSecurity + open class RememberMeServicesRefConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + rememberMeServices = REMEMBER_ME_SERVICES + } + } + } + + companion object { + lateinit var REMEMBER_ME_SERVICES: RememberMeServices + } + } + + @EnableWebSecurity + open class RememberMeSuccessHandlerConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + authenticationSuccessHandler = SUCCESS_HANDLER + } + } + } + + companion object { + lateinit var SUCCESS_HANDLER: AuthenticationSuccessHandler + } + } + + @EnableWebSecurity + @Order(0) + open class WithoutKeyConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + securityMatcher(AntPathRequestMatcher("/without-key/**")) + formLogin { + loginProcessingUrl = "/without-key/login" + } + rememberMe {} + } + } + } + + @EnableWebSecurity + open class KeyConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + formLogin {} + rememberMe { + key = "RememberMeKey" + } + } + } + } + + @EnableWebSecurity + open class RememberMeTokenRepositoryConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + tokenRepository = TOKEN_REPOSITORY + } + } + } + + companion object { + lateinit var TOKEN_REPOSITORY: PersistentTokenRepository + } + } + + @EnableWebSecurity + open class RememberMeTokenValidityConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + tokenValiditySeconds = 42 + } + } + } + } + + @EnableWebSecurity + open class RememberMeUseSecureCookieConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + useSecureCookie = true + } + } + } + } + + @EnableWebSecurity + open class RememberMeParameterConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + rememberMeParameter = "rememberMe" + } + } + } + } + + @EnableWebSecurity + open class RememberMeCookieNameConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + rememberMeCookieName = "rememberMe" + } + } + } + } + + @EnableWebSecurity + open class RememberMeDefaultUserDetailsServiceConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe {} + } + } + + override fun configure(auth: AuthenticationManagerBuilder) { + auth.userDetailsService(USER_DETAIL_SERVICE) + } + + companion object { + lateinit var USER_DETAIL_SERVICE: UserDetailsService + } + } + + @EnableWebSecurity + open class RememberMeUserDetailsServiceConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + userDetailsService = USER_DETAIL_SERVICE + } + } + } + + companion object { + lateinit var USER_DETAIL_SERVICE: UserDetailsService + } + } + + @EnableWebSecurity + open class RememberMeAlwaysRememberConfig : DefaultUserConfig() { + override fun configure(http: HttpSecurity) { + http { + formLogin {} + rememberMe { + alwaysRemember = true + } + } + } + } + +} diff --git a/core/spring-security-core.gradle b/core/spring-security-core.gradle index 19a2b42aec6..e85c287d66c 100644 --- a/core/spring-security-core.gradle +++ b/core/spring-security-core.gradle @@ -6,15 +6,16 @@ def includeProject = project(':spring-security-crypto') configurations { included - compile.extendsFrom included + api.extendsFrom included } dependencies { - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-expression' + management platform(project(":spring-security-dependencies")) + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-expression' included includeProject @@ -26,14 +27,14 @@ dependencies { optional 'org.springframework:spring-jdbc' optional 'org.springframework:spring-tx' - testCompile powerMock2Dependencies - testCompile 'commons-collections:commons-collections' - testCompile 'io.projectreactor:reactor-test' - testCompile 'org.skyscreamer:jsonassert' - testCompile 'org.slf4j:jcl-over-slf4j' - testCompile 'org.springframework:spring-test' + testImplementation powerMock2Dependencies + testImplementation 'commons-collections:commons-collections' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'org.skyscreamer:jsonassert' + testImplementation 'org.slf4j:jcl-over-slf4j' + testImplementation 'org.springframework:spring-test' - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' } task springVersion(type: org.gradle.api.tasks.WriteProperties) { @@ -59,6 +60,6 @@ configure(project.tasks.withType(Test)) { } Callable<String> springVersion() { - return (Callable<String>) { project.configurations.compile.resolvedConfiguration.resolvedArtifacts + return (Callable<String>) { project.configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts .find { it.name == 'spring-core' }.moduleVersion.id.version } } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java index ea6f3d80272..7d245aed6ed 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,9 +75,22 @@ public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority * @return the new instance */ public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) { + return hasAnyRole(ROLE_PREFIX, roles); + } + + /** + * Creates an instance of {@link AuthorityAuthorizationManager} with the provided + * authorities. + * @param rolePrefix the role prefix for <code>roles</code> + * @param roles the authorities to check for prefixed with <code>rolePrefix</code> + * @param <T> the type of object being authorized + * @return the new instance + */ + public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String rolePrefix, String[] roles) { + Assert.notNull(rolePrefix, "rolePrefix cannot be null"); Assert.notEmpty(roles, "roles cannot be empty"); Assert.noNullElements(roles, "roles cannot contain null values"); - return hasAnyAuthority(toNamedRolesArray(roles)); + return hasAnyAuthority(toNamedRolesArray(rolePrefix, roles)); } /** @@ -93,10 +106,10 @@ public static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... aut return new AuthorityAuthorizationManager<>(authorities); } - private static String[] toNamedRolesArray(String... roles) { + private static String[] toNamedRolesArray(String rolePrefix, String[] roles) { String[] result = new String[roles.length]; for (int i = 0; i < roles.length; i++) { - result[i] = ROLE_PREFIX + roles[i]; + result[i] = rolePrefix + roles[i]; } return result; } diff --git a/core/src/main/java/org/springframework/security/authorization/method/AbstractAuthorizationManagerRegistry.java b/core/src/main/java/org/springframework/security/authorization/method/AbstractAuthorizationManagerRegistry.java new file mode 100644 index 00000000000..47dad9dee3c --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AbstractAuthorizationManagerRegistry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.core.MethodClassKey; +import org.springframework.lang.NonNull; +import org.springframework.security.authorization.AuthorizationManager; + +/** + * For internal use only, as this contract is likely to change + * + * @author Evgeniy Cheban + */ +abstract class AbstractAuthorizationManagerRegistry { + + static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null; + + private final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>(); + + /** + * Returns an {@link AuthorizationManager} for the + * {@link AuthorizationMethodInvocation}. + * @param methodInvocation the {@link AuthorizationMethodInvocation} to use + * @return an {@link AuthorizationManager} to use + */ + final AuthorizationManager<MethodInvocation> getManager(AuthorizationMethodInvocation methodInvocation) { + Method method = methodInvocation.getMethod(); + Class<?> targetClass = methodInvocation.getTargetClass(); + MethodClassKey cacheKey = new MethodClassKey(method, targetClass); + return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass)); + } + + /** + * Subclasses should implement this method to provide the non-null + * {@link AuthorizationManager} for the method and the target class. + * @param method the method + * @param targetClass the target class + * @return the non-null {@link AuthorizationManager} + */ + @NonNull + abstract AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass); + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java b/core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java new file mode 100644 index 00000000000..0dd24407acf --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.core.MethodClassKey; +import org.springframework.lang.NonNull; + +/** + * For internal use only, as this contract is likely to change + * + * @author Evgeniy Cheban + */ +abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> { + + private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>(); + + /** + * Returns an {@link ExpressionAttribute} for the + * {@link AuthorizationMethodInvocation}. + * @param mi the {@link AuthorizationMethodInvocation} to use + * @return the {@link ExpressionAttribute} to use + */ + final T getAttribute(AuthorizationMethodInvocation mi) { + Method method = mi.getMethod(); + Class<?> targetClass = mi.getTargetClass(); + return getAttribute(method, targetClass); + } + + /** + * Returns an {@link ExpressionAttribute} for the method and the target class. + * @param method the method + * @param targetClass the target class + * @return the {@link ExpressionAttribute} to use + */ + final T getAttribute(Method method, Class<?> targetClass) { + MethodClassKey cacheKey = new MethodClassKey(method, targetClass); + return this.cachedAttributes.computeIfAbsent(cacheKey, (k) -> resolveAttribute(method, targetClass)); + } + + /** + * Subclasses should implement this method to provide the non-null + * {@link ExpressionAttribute} for the method and the target class. + * @param method the method + * @param targetClass the target class + * @return the non-null {@link ExpressionAttribute} + */ + @NonNull + abstract T resolveAttribute(Method method, Class<?> targetClass); + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManager.java new file mode 100644 index 00000000000..8947510f55b --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManager.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.lang.Nullable; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.core.Authentication; + +/** + * An Authorization manager which can determine if an {@link Authentication} has access to + * a specific object and associated return object. Intended for use specifically to + * evaluate the returning state of a method invocation. + * + * @param <T> the type of object that the authorization check is being done one. + * @author Josh Cummings + * @author Evgeniy Cheban + * @since 5.5 + */ +public interface AfterMethodAuthorizationManager<T> { + + /** + * Determine if access should be granted for a specific authentication, object and + * returnedObject. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param object the {@link T} object to check, typically a + * {@link MethodInvocation} + * @param returnedObject the returnedObject from the method invocation to check + * @throws AccessDeniedException if access is not granted + */ + default void verify(Supplier<Authentication> authentication, T object, Object returnedObject) { + AuthorizationDecision decision = check(authentication, object, returnedObject); + if (decision != null && !decision.isGranted()) { + throw new AccessDeniedException("Access Denied"); + } + } + + /** + * Determine if access is granted for a specific authentication, object, and + * returnedObject. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param object the {@link T} object to check, typically a + * {@link MethodInvocation} + * @param returnedObject the returned object from the method invocation to check + * @return an {@link AuthorizationDecision} or null if no decision could be made + */ + @Nullable + AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject); + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManagerAdapter.java b/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManagerAdapter.java new file mode 100644 index 00000000000..00df11da6f3 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AfterMethodAuthorizationManagerAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; + +/** + * Adapts an {@link AuthorizationManager} into an {@link AfterMethodAuthorizationManager} + * + * @param <T> the {@code T} object to authorize, typically a {@link MethodInvocation} + * @author Josh Cummings + * @since 5.5 + */ +public final class AfterMethodAuthorizationManagerAdapter<T> implements AfterMethodAuthorizationManager<T> { + + private final AuthorizationManager<T> authorizationManager; + + /** + * Construct a {@link AfterMethodAuthorizationManagerAdapter} with the provided + * parameters + * @param authorizationManager the {@link AuthorizationManager} to adapt + */ + public AfterMethodAuthorizationManagerAdapter(AuthorizationManager<T> authorizationManager) { + this.authorizationManager = authorizationManager; + } + + /** + * Determine if access is granted for a specific authentication and {@code T} object. + * + * Note that the {@code returnedObject} parameter is ignored + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param object the {@code T} object to check, typically a {@link MethodInvocation} + * @param returnedObject the returned object from the method invocation, ignored in + * this implementation + * @return an {@link AuthorizationDecision} or null if no decision could be made + */ + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, T object, Object returnedObject) { + return this.authorizationManager.check(authentication, object); + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java new file mode 100644 index 00000000000..14861ca1410 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.Pointcut; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationMethodInterceptor} which can determine if an + * {@link Authentication} has access to the result of an {@link MethodInvocation} using an + * {@link AuthorizationManager} + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public final class AuthorizationManagerAfterMethodInterceptor implements AuthorizationMethodInterceptor { + + private final Pointcut pointcut; + + private final AfterMethodAuthorizationManager<MethodInvocation> authorizationManager; + + /** + * Creates an instance. + * @param pointcut the {@link Pointcut} to use + * @param authorizationManager the {@link AuthorizationManager} to use + */ + public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut, + AfterMethodAuthorizationManager<MethodInvocation> authorizationManager) { + Assert.notNull(pointcut, "pointcut cannot be null"); + Assert.notNull(authorizationManager, "authorizationManager cannot be null"); + this.pointcut = pointcut; + this.authorizationManager = authorizationManager; + } + + /** + * Determine if an {@link Authentication} has access to the {@link MethodInvocation} + * using the {@link AuthorizationManager}. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link MethodInvocation} to check + * @throws AccessDeniedException if access is not granted + */ + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable { + Object result = mi.proceed(); + this.authorizationManager.verify(authentication, mi, result); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java new file mode 100644 index 00000000000..05a25a3b58a --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.Pointcut; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationMethodInterceptor} which uses a {@link AuthorizationManager} to + * determine if an {@link Authentication} may invoke the given {@link MethodInvocation} + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public final class AuthorizationManagerBeforeMethodInterceptor implements AuthorizationMethodInterceptor { + + private final Pointcut pointcut; + + private final AuthorizationManager<MethodInvocation> authorizationManager; + + /** + * Creates an instance. + * @param pointcut the {@link Pointcut} to use + * @param authorizationManager the {@link AuthorizationManager} to use + */ + public AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut, + AuthorizationManager<MethodInvocation> authorizationManager) { + Assert.notNull(pointcut, "pointcut cannot be null"); + Assert.notNull(authorizationManager, "authorizationManager cannot be null"); + this.pointcut = pointcut; + this.authorizationManager = authorizationManager; + } + + /** + * Determine if an {@link Authentication} has access to the {@link MethodInvocation} + * using the configured {@link AuthorizationManager}. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link MethodInvocation} to check + * @throws AccessDeniedException if access is not granted + */ + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable { + this.authorizationManager.verify(authentication, mi); + return mi.proceed(); + } + + /** + * {@inheritDoc} + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptor.java new file mode 100644 index 00000000000..ac52e1b72ca --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptor.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.aop.Advice; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.PointcutAdvisor; +import org.springframework.aop.framework.AopInfrastructureBean; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * A {@link MethodInterceptor} which can determine if an {@link Authentication} has access + * to the {@link MethodInvocation}. {@link #getPointcut()} describes when the interceptor + * applies. + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public interface AuthorizationMethodInterceptor extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean { + + /** + * {@inheritDoc} + */ + @Override + default Advice getAdvice() { + return this; + } + + /** + * {@inheritDoc} + */ + @Override + default boolean isPerInstance() { + return true; + } + + /** + * Determine if an {@link Authentication} has access to the {@link MethodInvocation} + * @param mi the {@link MethodInvocation} to intercept and potentially invoke + * @return the result of the method invocation + * @throws Throwable if the interceptor or the target object throws an exception + */ + default Object invoke(MethodInvocation mi) throws Throwable { + Supplier<Authentication> supplier = () -> { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new AuthenticationCredentialsNotFoundException( + "An Authentication object was not found in the SecurityContext"); + } + return authentication; + }; + return invoke(supplier, new AuthorizationMethodInvocation(supplier, mi)); + } + + /** + * Determine if an {@link Authentication} has access to the {@link MethodInvocation} + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link MethodInvocation} to intercept and potentially invoke + * @return the result of the method invocation + * @throws Throwable if the interceptor or the target object throws an exception + */ + Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable; + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptors.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptors.java new file mode 100644 index 00000000000..1d963ff7bc1 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInterceptors.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.access.prepost.PreAuthorize; + +/** + * A static factory for constructing common {@link AuthorizationMethodInterceptor}s + * + * @author Josh Cummings + * @since 5.5 + * @see PreAuthorizeAuthorizationManager + * @see PostAuthorizeAuthorizationManager + * @see SecuredAuthorizationManager + * @see Jsr250AuthorizationManager + */ +public final class AuthorizationMethodInterceptors { + + public static AuthorizationMethodInterceptor preAuthorize() { + return preAuthorize(new PreAuthorizeAuthorizationManager()); + } + + public static AuthorizationMethodInterceptor preAuthorize(PreAuthorizeAuthorizationManager manager) { + return new AuthorizationManagerBeforeMethodInterceptor( + AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), manager); + } + + public static AuthorizationMethodInterceptor postAuthorize() { + return postAuthorize(new PostAuthorizeAuthorizationManager()); + } + + public static AuthorizationMethodInterceptor postAuthorize(PostAuthorizeAuthorizationManager manager) { + return new AuthorizationManagerAfterMethodInterceptor( + AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), manager); + } + + public static AuthorizationMethodInterceptor secured() { + return secured(new SecuredAuthorizationManager()); + } + + public static AuthorizationMethodInterceptor secured(SecuredAuthorizationManager manager) { + return new AuthorizationManagerBeforeMethodInterceptor( + AuthorizationMethodPointcuts.forAnnotations(Secured.class), manager); + } + + public static AuthorizationMethodInterceptor jsr250() { + return jsr250(new Jsr250AuthorizationManager()); + } + + public static AuthorizationMethodInterceptor jsr250(Jsr250AuthorizationManager manager) { + return new AuthorizationManagerBeforeMethodInterceptor( + AuthorizationMethodPointcuts.forAnnotations(DenyAll.class, PermitAll.class, RolesAllowed.class), + manager); + } + + private AuthorizationMethodInterceptors() { + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInvocation.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInvocation.java new file mode 100644 index 00000000000..d3976b406fe --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodInvocation.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AopUtils; +import org.springframework.core.log.LogMessage; +import org.springframework.security.core.Authentication; + +/** + * @author Josh Cummings + */ +class AuthorizationMethodInvocation implements MethodInvocation { + + private final Log logger = LogFactory.getLog(getClass()); + + private final Supplier<Authentication> authentication; + + private final MethodInvocation methodInvocation; + + private final Class<?> targetClass; + + private final List<AuthorizationMethodInterceptor> interceptors; + + private final int size; + + private int currentPosition = 0; + + AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation) { + this(authentication, methodInvocation, Collections.emptyList()); + } + + AuthorizationMethodInvocation(Supplier<Authentication> authentication, MethodInvocation methodInvocation, + List<AuthorizationMethodInterceptor> interceptors) { + this.authentication = authentication; + this.methodInvocation = methodInvocation; + this.interceptors = interceptors; + Object target = methodInvocation.getThis(); + this.targetClass = (target != null) ? AopUtils.getTargetClass(target) : null; + this.size = interceptors.size(); + } + + @Override + public Method getMethod() { + return this.methodInvocation.getMethod(); + } + + @Override + public Object[] getArguments() { + return this.methodInvocation.getArguments(); + } + + /** + * Return the target class. + * @return the target class + */ + Class<?> getTargetClass() { + return this.targetClass; + } + + @Override + public Object proceed() throws Throwable { + if (this.currentPosition == this.size) { + if (this.logger.isDebugEnabled()) { + this.logger.debug(LogMessage.of(() -> "Pre-Authorized " + this.methodInvocation.getMethod())); + } + return this.methodInvocation.proceed(); + } + AuthorizationMethodInterceptor interceptor = this.interceptors.get(this.currentPosition); + this.currentPosition++; + Pointcut pointcut = interceptor.getPointcut(); + if (!pointcut.getClassFilter().matches(getTargetClass())) { + return proceed(); + } + if (!pointcut.getMethodMatcher().matches(getMethod(), getTargetClass())) { + return proceed(); + } + if (this.logger.isTraceEnabled()) { + this.logger.trace(LogMessage.format("Applying %s (%d/%d)", interceptor.getClass().getSimpleName(), + this.currentPosition, this.size)); + } + Object result = interceptor.invoke(this.authentication, this); + if (this.logger.isDebugEnabled()) { + this.logger.debug(LogMessage.of(() -> "Post-Authorized " + this.methodInvocation.getMethod())); + } + return result; + } + + @Override + public Object getThis() { + return this.methodInvocation.getThis(); + } + + @Override + public AccessibleObject getStaticPart() { + return this.methodInvocation.getStaticPart(); + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodPointcuts.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodPointcuts.java new file mode 100644 index 00000000000..e764d95d838 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodPointcuts.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.annotation.Annotation; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.ComposablePointcut; +import org.springframework.aop.support.Pointcuts; +import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; + +/** + * @author Josh Cummings + */ +final class AuthorizationMethodPointcuts { + + @SafeVarargs + static Pointcut forAnnotations(Class<? extends Annotation>... annotations) { + ComposablePointcut pointcut = null; + for (Class<? extends Annotation> annotation : annotations) { + if (pointcut == null) { + pointcut = new ComposablePointcut(classOrMethod(annotation)); + } + else { + pointcut.union(classOrMethod(annotation)); + } + } + return pointcut; + } + + private static Pointcut classOrMethod(Class<? extends Annotation> annotation) { + return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true), + new AnnotationMatchingPointcut(annotation, true)); + } + + private AuthorizationMethodPointcuts() { + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptor.java new file mode 100644 index 00000000000..e00e8a9922c --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptor.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.ComposablePointcut; +import org.springframework.security.core.Authentication; + +/** + * Provides security interception of AOP Alliance based method invocations. + * + * Delegates to a collection of {@link AuthorizationMethodInterceptor}s + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public final class DelegatingAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor { + + private final List<AuthorizationMethodInterceptor> interceptors; + + private final Pointcut pointcut; + + /** + * Creates an instance using the provided parameters + * @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use + */ + public DelegatingAuthorizationMethodInterceptor(AuthorizationMethodInterceptor... interceptors) { + this(Arrays.asList(interceptors)); + } + + /** + * Creates an instance using the provided parameters + * @param interceptors the delegate {@link AuthorizationMethodInterceptor}s to use + */ + public DelegatingAuthorizationMethodInterceptor(List<AuthorizationMethodInterceptor> interceptors) { + ComposablePointcut pointcut = null; + for (AuthorizationMethodInterceptor interceptor : interceptors) { + if (pointcut == null) { + pointcut = new ComposablePointcut(interceptor.getPointcut()); + } + else { + pointcut.union(interceptor.getPointcut()); + } + } + this.pointcut = pointcut; + this.interceptors = interceptors; + } + + /** + * Enforce security on this {@link MethodInvocation}. + * @param mi the method being invoked which requires a security decision + * @return the returned value from the {@link MethodInvocation}, possibly altered by + * the configured {@link AuthorizationMethodInterceptor}s + */ + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable { + return new AuthorizationMethodInvocation(authentication, mi, this.interceptors).proceed(); + } + + /** + * {@inheritDoc} + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttribute.java b/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttribute.java new file mode 100644 index 00000000000..80e49360e9b --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/ExpressionAttribute.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import org.springframework.expression.Expression; + +/** + * An {@link Expression} attribute. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +class ExpressionAttribute { + + /** + * Represents an empty attribute with null {@link Expression}. + */ + static final ExpressionAttribute NULL_ATTRIBUTE = new ExpressionAttribute(null); + + private final Expression expression; + + /** + * Creates an instance. + * @param expression the {@link Expression} to use + */ + ExpressionAttribute(Expression expression) { + this.expression = expression; + } + + /** + * Returns the {@link Expression}. + * @return the {@link Expression} to use + */ + Expression getExpression() { + return this.expression; + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java new file mode 100644 index 00000000000..c099e4d70fc --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.lang.NonNull; +import org.springframework.security.authorization.AuthorityAuthorizationManager; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationManager} which can determine if an {@link Authentication} may + * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication} + * contains a specified authority from the JSR-250 security annotations. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> { + + private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>(); + + static { + JSR250_ANNOTATIONS.add(DenyAll.class); + JSR250_ANNOTATIONS.add(PermitAll.class); + JSR250_ANNOTATIONS.add(RolesAllowed.class); + } + + private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry(); + + private String rolePrefix = "ROLE_"; + + /** + * Sets the role prefix. Defaults to "ROLE_". + * @param rolePrefix the role prefix to use + */ + public void setRolePrefix(String rolePrefix) { + Assert.notNull(rolePrefix, "rolePrefix cannot be null"); + this.rolePrefix = rolePrefix; + } + + /** + * Determine if an {@link Authentication} has access to a method by evaluating the + * {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that + * {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param methodInvocation the {@link AuthorizationMethodInvocation} to check + * @return an {@link AuthorizationDecision} or null if the JSR-250 security + * annotations is not present + */ + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) { + AuthorizationManager<MethodInvocation> delegate = this.registry + .getManager((AuthorizationMethodInvocation) methodInvocation); + return delegate.check(authentication, methodInvocation); + } + + private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { + + @NonNull + @Override + AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) { + for (Annotation annotation : findJsr250Annotations(method, targetClass)) { + if (annotation instanceof DenyAll) { + return (a, o) -> new AuthorizationDecision(false); + } + if (annotation instanceof PermitAll) { + return (a, o) -> new AuthorizationDecision(true); + } + if (annotation instanceof RolesAllowed) { + RolesAllowed rolesAllowed = (RolesAllowed) annotation; + return AuthorityAuthorizationManager.hasAnyRole(Jsr250AuthorizationManager.this.rolePrefix, + rolesAllowed.value()); + } + } + return NULL_MANAGER; + } + + private Set<Annotation> findJsr250Annotations(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + Set<Annotation> annotations = findAnnotations(specificMethod); + return (annotations.isEmpty()) ? findAnnotations(specificMethod.getDeclaringClass()) : annotations; + } + + private Set<Annotation> findAnnotations(AnnotatedElement annotatedElement) { + return AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement, JSR250_ANNOTATIONS); + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java new file mode 100644 index 00000000000..9e486642b85 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import reactor.util.annotation.NonNull; + +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.security.access.expression.ExpressionUtils; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationManager} which can determine if an {@link Authentication} may + * return the result from an invoked {@link MethodInvocation} by evaluating an expression + * from the {@link PostAuthorize} annotation. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +public final class PostAuthorizeAuthorizationManager implements AfterMethodAuthorizationManager<MethodInvocation> { + + private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry(); + + private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + + /** + * Use this the {@link MethodSecurityExpressionHandler}. + * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use + */ + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { + Assert.notNull(expressionHandler, "expressionHandler cannot be null"); + this.expressionHandler = expressionHandler; + } + + /** + * Determine if an {@link Authentication} has access to the returned object by + * evaluating the {@link PostAuthorize} annotation that the + * {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link AuthorizationMethodInvocation} to check + * @param returnedObject the returned object to check + * @return an {@link AuthorizationDecision} or {@code null} if the + * {@link PostAuthorize} annotation is not present + */ + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi, + Object returnedObject) { + ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi); + if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { + return null; + } + EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi); + this.expressionHandler.setReturnObject(returnedObject, ctx); + boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx); + return new AuthorizationDecision(granted); + } + + private final class PostAuthorizeExpressionAttributeRegistry + extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { + + @NonNull + @Override + ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + PostAuthorize postAuthorize = findPostAuthorizeAnnotation(specificMethod); + if (postAuthorize == null) { + return ExpressionAttribute.NULL_ATTRIBUTE; + } + Expression postAuthorizeExpression = PostAuthorizeAuthorizationManager.this.expressionHandler + .getExpressionParser().parseExpression(postAuthorize.value()); + return new ExpressionAttribute(postAuthorizeExpression); + } + + private PostAuthorize findPostAuthorizeAnnotation(Method method) { + PostAuthorize postAuthorize = AnnotationUtils.findAnnotation(method, PostAuthorize.class); + return (postAuthorize != null) ? postAuthorize + : AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostAuthorize.class); + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java new file mode 100644 index 00000000000..40ec2b29f4d --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.lang.NonNull; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationMethodInterceptor} which filters a {@code returnedObject} from + * the {@link MethodInvocation} by evaluating an expression from the {@link PostFilter} + * annotation. + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public final class PostFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor { + + private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry(); + + private final Pointcut pointcut; + + private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + + /** + * Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided + * parameters + */ + public PostFilterAuthorizationMethodInterceptor() { + this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class); + } + + /** + * Use this {@link MethodSecurityExpressionHandler}. + * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use + */ + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { + Assert.notNull(expressionHandler, "expressionHandler cannot be null"); + this.expressionHandler = expressionHandler; + } + + /** + * {@inheritDoc} + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + /** + * Filter a {@code returnedObject} using the {@link PostFilter} annotation that the + * {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link AuthorizationMethodInvocation} to check check + * @return filtered {@code returnedObject} + */ + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable { + Object returnedObject = mi.proceed(); + ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi); + if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { + return returnedObject; + } + EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi); + return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx); + } + + private final class PostFilterExpressionAttributeRegistry + extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { + + @NonNull + @Override + ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + PostFilter postFilter = findPostFilterAnnotation(specificMethod); + if (postFilter == null) { + return ExpressionAttribute.NULL_ATTRIBUTE; + } + Expression postFilterExpression = PostFilterAuthorizationMethodInterceptor.this.expressionHandler + .getExpressionParser().parseExpression(postFilter.value()); + return new ExpressionAttribute(postFilterExpression); + } + + private PostFilter findPostFilterAnnotation(Method method) { + PostFilter postFilter = AnnotationUtils.findAnnotation(method, PostFilter.class); + return (postFilter != null) ? postFilter + : AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostFilter.class); + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java new file mode 100644 index 00000000000..9f7fc77cb29 --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import reactor.util.annotation.NonNull; + +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.security.access.expression.ExpressionUtils; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; + +/** + * An {@link AuthorizationManager} which can determine if an {@link Authentication} may + * invoke the {@link MethodInvocation} by evaluating an expression from the + * {@link PreAuthorize} annotation. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> { + + private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry(); + + private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + + /** + * Sets the {@link MethodSecurityExpressionHandler}. + * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use + */ + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { + Assert.notNull(expressionHandler, "expressionHandler cannot be null"); + this.expressionHandler = expressionHandler; + } + + /** + * Determine if an {@link Authentication} has access to a method by evaluating an + * expression from the {@link PreAuthorize} annotation that the + * {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link AuthorizationMethodInvocation} to check + * @return an {@link AuthorizationDecision} or {@code null} if the + * {@link PreAuthorize} annotation is not present + */ + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) { + ExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi); + if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) { + return null; + } + EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi); + boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx); + return new AuthorizationDecision(granted); + } + + private final class PreAuthorizeExpressionAttributeRegistry + extends AbstractExpressionAttributeRegistry<ExpressionAttribute> { + + @NonNull + @Override + ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + PreAuthorize preAuthorize = findPreAuthorizeAnnotation(specificMethod); + if (preAuthorize == null) { + return ExpressionAttribute.NULL_ATTRIBUTE; + } + Expression preAuthorizeExpression = PreAuthorizeAuthorizationManager.this.expressionHandler + .getExpressionParser().parseExpression(preAuthorize.value()); + return new ExpressionAttribute(preAuthorizeExpression); + } + + private PreAuthorize findPreAuthorizeAnnotation(Method method) { + PreAuthorize preAuthorize = AnnotationUtils.findAnnotation(method, PreAuthorize.class); + return (preAuthorize != null) ? preAuthorize + : AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreAuthorize.class); + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java new file mode 100644 index 00000000000..0e56e4d47ff --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java @@ -0,0 +1,153 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.lang.NonNull; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.prepost.PreFilter; +import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * An {@link AuthorizationMethodInterceptor} which filters a method argument by evaluating + * an expression from the {@link PreFilter} annotation. + * + * @author Evgeniy Cheban + * @author Josh Cummings + * @since 5.5 + */ +public final class PreFilterAuthorizationMethodInterceptor implements AuthorizationMethodInterceptor { + + private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry(); + + private final Pointcut pointcut; + + private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + + /** + * Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided + * parameters + */ + public PreFilterAuthorizationMethodInterceptor() { + this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class); + } + + /** + * Use this {@link MethodSecurityExpressionHandler} + * @param expressionHandler the {@link MethodSecurityExpressionHandler} to use + */ + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { + Assert.notNull(expressionHandler, "expressionHandler cannot be null"); + this.expressionHandler = expressionHandler; + } + + /** + * {@inheritDoc} + */ + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + /** + * Filter the method argument specified in the {@link PreFilter} annotation that + * {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link AuthorizationMethodInvocation} to check + */ + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) throws Throwable { + PreFilterExpressionAttribute attribute = this.registry.getAttribute((AuthorizationMethodInvocation) mi); + if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) { + return mi.proceed(); + } + EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi); + Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi); + this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx); + return mi.proceed(); + } + + private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) { + Object filterTarget; + if (StringUtils.hasText(filterTargetName)) { + filterTarget = ctx.lookupVariable(filterTargetName); + Assert.notNull(filterTarget, () -> "Filter target was null, or no argument with name '" + filterTargetName + + "' found in method."); + } + else { + Object[] arguments = methodInvocation.getArguments(); + Assert.state(arguments.length == 1, + "Unable to determine the method argument for filtering. Specify the filter target."); + filterTarget = arguments[0]; + Assert.notNull(filterTarget, + "Filter target was null. Make sure you passing the correct value in the method argument."); + } + Assert.state(!filterTarget.getClass().isArray(), + "Pre-filtering on array types is not supported. Using a Collection will solve this problem."); + return filterTarget; + } + + private final class PreFilterExpressionAttributeRegistry + extends AbstractExpressionAttributeRegistry<PreFilterExpressionAttribute> { + + @NonNull + @Override + PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + PreFilter preFilter = findPreFilterAnnotation(specificMethod); + if (preFilter == null) { + return PreFilterExpressionAttribute.NULL_ATTRIBUTE; + } + Expression preFilterExpression = PreFilterAuthorizationMethodInterceptor.this.expressionHandler + .getExpressionParser().parseExpression(preFilter.value()); + return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget()); + } + + private PreFilter findPreFilterAnnotation(Method method) { + PreFilter preFilter = AnnotationUtils.findAnnotation(method, PreFilter.class); + return (preFilter != null) ? preFilter + : AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreFilter.class); + } + + } + + private static final class PreFilterExpressionAttribute extends ExpressionAttribute { + + private static final PreFilterExpressionAttribute NULL_ATTRIBUTE = new PreFilterExpressionAttribute(null, null); + + private final String filterTarget; + + private PreFilterExpressionAttribute(Expression expression, String filterTarget) { + super(expression); + this.filterTarget = filterTarget; + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java new file mode 100644 index 00000000000..b05529d6f9b --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.NonNull; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.authorization.AuthorityAuthorizationManager; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; + +/** + * An {@link AuthorizationManager} which can determine if an {@link Authentication} may + * invoke the {@link MethodInvocation} by evaluating if the {@link Authentication} + * contains a specified authority from the Spring Security's {@link Secured} annotation. + * + * @author Evgeniy Cheban + * @since 5.5 + */ +public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> { + + private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry(); + + /** + * Determine if an {@link Authentication} has access to a method by evaluating the + * {@link Secured} annotation that {@link AuthorizationMethodInvocation} specifies. + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param mi the {@link AuthorizationMethodInvocation} to check + * @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation + * is not present + */ + @Override + public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) { + AuthorizationManager<MethodInvocation> delegate = this.registry.getManager((AuthorizationMethodInvocation) mi); + return delegate.check(authentication, mi); + } + + private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { + + @NonNull + @Override + AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) { + Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); + Secured secured = findSecuredAnnotation(specificMethod); + return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER; + } + + private Secured findSecuredAnnotation(Method method) { + Secured secured = AnnotationUtils.findAnnotation(method, Secured.class); + return (secured != null) ? secured + : AnnotationUtils.findAnnotation(method.getDeclaringClass(), Secured.class); + } + + } + +} diff --git a/core/src/main/java/org/springframework/security/core/ComparableVersion.java b/core/src/main/java/org/springframework/security/core/ComparableVersion.java index bce13b7ac17..7e01b042aa5 100644 --- a/core/src/main/java/org/springframework/security/core/ComparableVersion.java +++ b/core/src/main/java/org/springframework/security/core/ComparableVersion.java @@ -17,27 +17,26 @@ package org.springframework.security.core; import java.math.BigInteger; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Locale; import java.util.Properties; -import java.util.Stack; /** + * <p> * Generic implementation of version comparison. + * </p> * * NOTE: This is a copy from - * https://svn.apache.org/repos/asf/maven/maven-3/tags/maven-3.1.0 - * /maven-artifact/src/main/ - * java/org/apache/maven/artifact/versioning/ComparableVersion.java + * https://github.com/apache/maven/blob/maven-3.6.3/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java * - * <p> * Features: * <ul> - * <li>mixing of '<code>-</code>' (dash) and '<code>.</code>' (dot) separators,</li> + * <li>mixing of '<code>-</code>' (hyphen) and '<code>.</code>' (dot) separators,</li> * <li>transition between characters and digits also constitutes a separator: * <code>1.0alpha1 => [1, 0, alpha, 1]</code></li> * <li>unlimited number of version components,</li> @@ -55,19 +54,22 @@ * </ul> * Unknown qualifiers are considered after known qualifiers, with lexical order (always * case insensitive),</li> - * <li>a dash usually precedes a qualifier, and is always less important than something + * <li>a hyphen usually precedes a qualifier, and is always less important than something * preceded with a dot.</li> * </ul> - * </p> * - * @author Kenney Westerhof - * @author Hervé Boutemy * @see <a href= * "https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning" on Maven * Wiki</a> + * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a> + * @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a> */ class ComparableVersion implements Comparable<ComparableVersion> { + private static final int MAX_INTITEM_LENGTH = 9; + + private static final int MAX_LONGITEM_LENGTH = 18; + private String value; private String canonical; @@ -76,7 +78,11 @@ class ComparableVersion implements Comparable<ComparableVersion> { private interface Item { - int INTEGER_ITEM = 0; + int INT_ITEM = 3; + + int LONG_ITEM = 4; + + int BIGINTEGER_ITEM = 0; int STRING_ITEM = 1; @@ -91,43 +97,194 @@ private interface Item { } /** - * Represents a numeric item in the version item list. + * Represents a numeric item in the version item list that can be represented with an + * int. */ - private static class IntegerItem implements Item { + private static class IntItem implements Item { - private static final BigInteger BigInteger_ZERO = new BigInteger("0"); + private final int value; - private final BigInteger value; + public static final IntItem ZERO = new IntItem(); + + private IntItem() { + this.value = 0; + } + + IntItem(String str) { + this.value = Integer.parseInt(str); + } + + @Override + public int getType() { + return INT_ITEM; + } + + @Override + public boolean isNull() { + return value == 0; + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + switch (item.getType()) { + case INT_ITEM: + int itemValue = ((IntItem) item).value; + return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + IntItem intItem = (IntItem) o; + + return value == intItem.value; + + } + + @Override + public int hashCode() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } + + } + + /** + * Represents a numeric item in the version item list that can be represented with a + * long. + */ + private static class LongItem implements Item { + + private final long value; + + LongItem(String str) { + this.value = Long.parseLong(str); + } + + @Override + public int getType() { + return LONG_ITEM; + } + + @Override + public boolean isNull() { + return value == 0; + } + + @Override + public int compareTo(Item item) { + if (item == null) { + return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + switch (item.getType()) { + case INT_ITEM: + return 1; + case LONG_ITEM: + long itemValue = ((LongItem) item).value; + return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); + case BIGINTEGER_ITEM: + return -1; + + case STRING_ITEM: + return 1; // 1.1 > 1-sp - public static final IntegerItem ZERO = new IntegerItem(); + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } - private IntegerItem() { - this.value = BigInteger_ZERO; + LongItem longItem = (LongItem) o; + + return value == longItem.value; + + } + + @Override + public int hashCode() { + return (int) (value ^ (value >>> 32)); } - IntegerItem(String str) { + @Override + public String toString() { + return Long.toString(value); + } + + } + + /** + * Represents a numeric item in the version item list. + */ + private static class BigIntegerItem implements Item { + + private final BigInteger value; + + BigIntegerItem(String str) { this.value = new BigInteger(str); } @Override public int getType() { - return INTEGER_ITEM; + return BIGINTEGER_ITEM; } @Override public boolean isNull() { - return BigInteger_ZERO.equals(this.value); + return BigInteger.ZERO.equals(value); } @Override public int compareTo(Item item) { if (item == null) { - return BigInteger_ZERO.equals(this.value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 } switch (item.getType()) { - case INTEGER_ITEM: - return this.value.compareTo(((IntegerItem) item).value); + case INT_ITEM: + case LONG_ITEM: + return 1; + + case BIGINTEGER_ITEM: + return value.compareTo(((BigIntegerItem) item).value); case STRING_ITEM: return 1; // 1.1 > 1-sp @@ -136,13 +293,32 @@ public int compareTo(Item item) { return 1; // 1.1 > 1-1 default: - throw new RuntimeException("invalid item: " + item.getClass()); + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; } + + BigIntegerItem that = (BigIntegerItem) o; + + return value.equals(that.value); + } @Override + public int hashCode() { + return value.hashCode(); + } + public String toString() { - return this.value.toString(); + return value.toString(); } } @@ -152,14 +328,14 @@ public String toString() { */ private static class StringItem implements Item { - private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" }; - - private static final List<String> _QUALIFIERS = Arrays.asList(QUALIFIERS); + private static final List<String> QUALIFIERS = Arrays.asList("alpha", "beta", "milestone", "rc", "snapshot", "", + "sp"); private static final Properties ALIASES = new Properties(); static { ALIASES.put("ga", ""); ALIASES.put("final", ""); + ALIASES.put("release", ""); ALIASES.put("cr", "rc"); } @@ -168,9 +344,9 @@ private static class StringItem implements Item { * determine if a given qualifier makes the version older than one without a * qualifier, or more recent. */ - private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf("")); + private static final String RELEASE_VERSION_INDEX = String.valueOf(QUALIFIERS.indexOf("")); - private String value; + private final String value; StringItem(String value, boolean followedByDigit) { if (followedByDigit && value.length() == 1) { @@ -185,6 +361,7 @@ private static class StringItem implements Item { case 'm': value = "milestone"; break; + default: } } this.value = ALIASES.getProperty(value, value); @@ -197,7 +374,7 @@ public int getType() { @Override public boolean isNull() { - return (comparableQualifier(this.value).compareTo(RELEASE_VERSION_INDEX) == 0); + return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0); } /** @@ -214,35 +391,56 @@ public boolean isNull() { * @return an equivalent value that can be used with lexical comparison */ public static String comparableQualifier(String qualifier) { - int i = _QUALIFIERS.indexOf(qualifier); + int i = QUALIFIERS.indexOf(qualifier); - return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); + return i == -1 ? (QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i); } @Override public int compareTo(Item item) { if (item == null) { // 1-rc < 1, 1-ga > 1 - return comparableQualifier(this.value).compareTo(RELEASE_VERSION_INDEX); + return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); } switch (item.getType()) { - case INTEGER_ITEM: + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: return -1; // 1.any < 1.1 ? case STRING_ITEM: - return comparableQualifier(this.value).compareTo(comparableQualifier(((StringItem) item).value)); + return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); case LIST_ITEM: return -1; // 1.any < 1-1 default: - throw new RuntimeException("invalid item: " + item.getClass()); + throw new IllegalStateException("invalid item: " + item.getClass()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } + if (o == null || getClass() != o.getClass()) { + return false; + } + + StringItem that = (StringItem) o; + + return value.equals(that.value); + } @Override + public int hashCode() { + return value.hashCode(); + } + public String toString() { - return this.value; + return value; } } @@ -264,12 +462,14 @@ public boolean isNull() { } void normalize() { - for (ListIterator<Item> iterator = listIterator(size()); iterator.hasPrevious();) { - Item item = iterator.previous(); - if (item.isNull()) { - iterator.remove(); // remove null trailing items: 0, "", empty list + for (int i = size() - 1; i >= 0; i--) { + Item lastItem = get(i); + + if (lastItem.isNull()) { + // remove null trailing items: 0, "", empty list + remove(i); } - else { + else if (!(lastItem instanceof ListItem)) { break; } } @@ -285,7 +485,9 @@ public int compareTo(Item item) { return first.compareTo(null); } switch (item.getType()) { - case INTEGER_ITEM: + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: return -1; // 1-1 < 1.0.x case STRING_ITEM: @@ -300,7 +502,7 @@ public int compareTo(Item item) { Item r = right.hasNext() ? right.next() : null; // if this is shorter, then invert the compare and mul with -1 - int result = l == null ? -1 * r.compareTo(l) : l.compareTo(r); + int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); if (result != 0) { return result; @@ -310,39 +512,39 @@ public int compareTo(Item item) { return 0; default: - throw new RuntimeException("invalid item: " + item.getClass()); + throw new IllegalStateException("invalid item: " + item.getClass()); } } @Override public String toString() { - StringBuilder buffer = new StringBuilder("("); - for (Iterator<Item> iter = iterator(); iter.hasNext();) { - buffer.append(iter.next()); - if (iter.hasNext()) { - buffer.append(','); + StringBuilder buffer = new StringBuilder(); + for (Item item : this) { + if (buffer.length() > 0) { + buffer.append((item instanceof ListItem) ? '-' : '.'); } + buffer.append(item); } - buffer.append(')'); return buffer.toString(); } } - ComparableVersion(String version) { + public ComparableVersion(String version) { parseVersion(version); } + @SuppressWarnings("checkstyle:innerassignment") public final void parseVersion(String version) { this.value = version; - this.items = new ListItem(); + items = new ListItem(); version = version.toLowerCase(Locale.ENGLISH); - ListItem list = this.items; + ListItem list = items; - Stack<Item> stack = new Stack<>(); + Deque<Item> stack = new ArrayDeque<>(); stack.push(list); boolean isDigit = false; @@ -354,7 +556,7 @@ public final void parseVersion(String version) { if (c == '.') { if (i == startIndex) { - list.add(IntegerItem.ZERO); + list.add(IntItem.ZERO); } else { list.add(parseItem(isDigit, version.substring(startIndex, i))); @@ -363,30 +565,23 @@ public final void parseVersion(String version) { } else if (c == '-') { if (i == startIndex) { - list.add(IntegerItem.ZERO); + list.add(IntItem.ZERO); } else { list.add(parseItem(isDigit, version.substring(startIndex, i))); } startIndex = i + 1; - if (isDigit) { - list.normalize(); // 1.0-* = 1-* - - if ((i + 1 < version.length()) && Character.isDigit(version.charAt(i + 1))) { - // new ListItem only if previous were digits and new char is a - // digit, - // ie need to differentiate only 1.1 from 1-1 - list.add(list = new ListItem()); - - stack.push(list); - } - } + list.add(list = new ListItem()); + stack.push(list); } else if (Character.isDigit(c)) { if (!isDigit && i > startIndex) { list.add(new StringItem(version.substring(startIndex, i), true)); startIndex = i; + + list.add(list = new ListItem()); + stack.push(list); } isDigit = true; @@ -395,6 +590,9 @@ else if (Character.isDigit(c)) { if (isDigit && i > startIndex) { list.add(parseItem(true, version.substring(startIndex, i))); startIndex = i; + + list.add(list = new ListItem()); + stack.push(list); } isDigit = false; @@ -409,32 +607,101 @@ else if (Character.isDigit(c)) { list = (ListItem) stack.pop(); list.normalize(); } - - this.canonical = this.items.toString(); } private static Item parseItem(boolean isDigit, String buf) { - return isDigit ? new IntegerItem(buf) : new StringItem(buf, false); + if (isDigit) { + buf = stripLeadingZeroes(buf); + if (buf.length() <= MAX_INTITEM_LENGTH) { + // lower than 2^31 + return new IntItem(buf); + } + else if (buf.length() <= MAX_LONGITEM_LENGTH) { + // lower than 2^63 + return new LongItem(buf); + } + return new BigIntegerItem(buf); + } + return new StringItem(buf, false); + } + + private static String stripLeadingZeroes(String buf) { + if (buf == null || buf.isEmpty()) { + return "0"; + } + for (int i = 0; i < buf.length(); ++i) { + char c = buf.charAt(i); + if (c != '0') { + return buf.substring(i); + } + } + return buf; } @Override public int compareTo(ComparableVersion o) { - return this.items.compareTo(o.items); + return items.compareTo(o.items); } @Override public String toString() { - return this.value; + return value; + } + + public String getCanonical() { + if (canonical == null) { + canonical = items.toString(); + } + return canonical; } @Override public boolean equals(Object o) { - return (o instanceof ComparableVersion) && this.canonical.equals(((ComparableVersion) o).canonical); + return (o instanceof ComparableVersion) && items.equals(((ComparableVersion) o).items); } @Override public int hashCode() { - return this.canonical.hashCode(); + return items.hashCode(); + } + + // CHECKSTYLE_OFF: LineLength + /** + * Main to test version parsing and comparison. + * <p> + * To check how "1.2.7" compares to "1.2-SNAPSHOT", for example, you can issue + * <pre>java -jar ${maven.repo.local}/org/apache/maven/maven-artifact/${maven.version}/maven-artifact-${maven.version}.jar "1.2.7" "1.2-SNAPSHOT"</pre> + * command to command line. Result of given command will be something like this: <pre> + * Display parameters as parsed by Maven (in canonical form) and comparison result: + * 1. 1.2.7 == 1.2.7 + * 1.2.7 > 1.2-SNAPSHOT + * 2. 1.2-SNAPSHOT == 1.2-snapshot + * </pre> + * @param args the version strings to parse and compare. You can pass arbitrary number + * of version strings and always two adjacent will be compared + */ + // CHECKSTYLE_ON: LineLength + public static void main(String... args) { + System.out.println("Display parameters as parsed by Maven (in canonical form) and comparison result:"); + if (args.length == 0) { + return; + } + + ComparableVersion prev = null; + int i = 1; + for (String version : args) { + ComparableVersion c = new ComparableVersion(version); + + if (prev != null) { + int compare = prev.compareTo(c); + System.out.println(" " + prev.toString() + ' ' + ((compare == 0) ? "==" : ((compare < 0) ? "<" : ">")) + + ' ' + version); + } + + System.out.println(String.valueOf(i++) + ". " + version + " == " + c.getCanonical()); + + prev = c; + } } } diff --git a/core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java index ab0c41563c7..5c092d54d43 100644 --- a/core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,13 @@ public void hasAnyRoleWhenContainNullThenException() { .withMessage("roles cannot contain null values"); } + @Test + public void hasAnyRoleWhenCustomRolePrefixNullThenException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null, new String[] { "ADMIN", "USER" })) + .withMessage("rolePrefix cannot be null"); + } + @Test public void hasAnyAuthorityWhenNullThenException() { assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null)) @@ -147,6 +154,17 @@ public void hasAnyRoleWhenUserHasNotAnyRoleThenDeniedDecision() { assertThat(manager.check(authentication, object).isGranted()).isFalse(); } + @Test + public void hasAnyRoleWhenCustomRolePrefixProvidedThenUseCustomRolePrefix() { + AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("CUSTOM_", + new String[] { "USER" }); + Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", + "CUSTOM_USER"); + Object object = new Object(); + + assertThat(manager.check(authentication, object).isGranted()).isTrue(); + } + @Test public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() { AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER"); diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java new file mode 100644 index 00000000000..0c0519a3c39 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.Test; + +import org.springframework.aop.Pointcut; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.core.Authentication; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link AuthorizationManagerAfterMethodInterceptor}. + * + * @author Evgeniy Cheban + */ +public class AuthorizationManagerAfterMethodInterceptorTests { + + @Test + public void instantiateWhenMethodMatcherNullThenException() { + AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock( + AfterMethodAuthorizationManager.class); + assertThatIllegalArgumentException() + .isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager)) + .withMessage("pointcut cannot be null"); + } + + @Test + public void instantiateWhenAuthorizationManagerNullThenException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null)) + .withMessage("authorizationManager cannot be null"); + } + + @Test + public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() throws Throwable { + Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; + MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); + Object returnedObject = new Object(); + given(mockMethodInvocation.proceed()).willReturn(returnedObject); + AfterMethodAuthorizationManager<MethodInvocation> mockAuthorizationManager = mock( + AfterMethodAuthorizationManager.class); + AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor( + Pointcut.TRUE, mockAuthorizationManager); + Object result = advice.invoke(authentication, mockMethodInvocation); + assertThat(result).isEqualTo(returnedObject); + verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation, returnedObject); + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java new file mode 100644 index 00000000000..f205d75d431 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.Test; + +import org.springframework.aop.Pointcut; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link AuthorizationManagerBeforeMethodInterceptor}. + * + * @author Evgeniy Cheban + */ +public class AuthorizationManagerBeforeMethodInterceptorTests { + + @Test + public void instantiateWhenMethodMatcherNullThenException() { + assertThatIllegalArgumentException() + .isThrownBy( + () -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class))) + .withMessage("pointcut cannot be null"); + } + + @Test + public void instantiateWhenAuthorizationManagerNullThenException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null)) + .withMessage("authorizationManager cannot be null"); + } + + @Test + public void beforeWhenMockAuthorizationManagerThenVerify() throws Throwable { + Supplier<Authentication> authentication = TestAuthentication::authenticatedUser; + MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); + AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class); + AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor( + Pointcut.TRUE, mockAuthorizationManager); + advice.invoke(authentication, mockMethodInvocation); + verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation); + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationMethodPointcutsTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationMethodPointcutsTests.java new file mode 100644 index 00000000000..b2bb8de7b38 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationMethodPointcutsTests.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.Test; + +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AopUtils; +import org.springframework.security.access.prepost.PreAuthorize; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AuthorizationMethodPointcuts} + */ +public class AuthorizationMethodPointcutsTests { + + @Test + public void forAnnotationsWhenAnnotationThenClassBasedAnnotationPointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, ClassController.class)).isTrue(); + assertThat(AopUtils.canApply(preAuthorize, NoController.class)).isFalse(); + } + + @Test + public void forAnnotationsWhenAnnotationThenMethodBasedAnnotationPointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, MethodController.class)).isTrue(); + } + + @Test + public void forAnnotationsWhenAnnotationThenClassInheritancePointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, InterfacedClassController.class)).isTrue(); + } + + @Test + public void forAnnotationsWhenAnnotationThenMethodInheritancePointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, InterfacedMethodController.class)).isTrue(); + } + + @Test + public void forAnnotationsWhenAnnotationThenAnnotationClassInheritancePointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationClassController.class)).isTrue(); + } + + @Test + public void forAnnotationsWhenAnnotationThenAnnotationMethodInheritancePointcut() { + Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class); + assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationMethodController.class)).isTrue(); + } + + @PreAuthorize("hasAuthority('APP')") + public static class ClassController { + + String methodOne(String paramOne) { + return "value"; + } + + } + + public static class MethodController { + + @PreAuthorize("hasAuthority('APP')") + String methodOne(String paramOne) { + return "value"; + } + + } + + public static class NoController { + + String methodOne(String paramOne) { + return "value"; + } + + } + + @PreAuthorize("hasAuthority('APP')") + public interface ClassControllerInterface { + + String methodOne(String paramOne); + + } + + public static class InterfacedClassController implements ClassControllerInterface { + + public String methodOne(String paramOne) { + return "value"; + } + + } + + public interface MethodControllerInterface { + + @PreAuthorize("hasAuthority('APP')") + String methodOne(String paramOne); + + } + + public static class InterfacedMethodController implements MethodControllerInterface { + + public String methodOne(String paramOne) { + return "value"; + } + + } + + @Target({ ElementType.METHOD, ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + @PreAuthorize("hasAuthority('APP')") + @interface MyAnnotation { + + } + + @MyAnnotation + public interface ClassAnnotationControllerInterface { + + String methodOne(String paramOne); + + } + + public static class InterfacedAnnotationClassController implements ClassAnnotationControllerInterface { + + public String methodOne(String paramOne) { + return "value"; + } + + } + + public interface MethodAnnotationControllerInterface { + + @MyAnnotation + String methodOne(String paramOne); + + } + + public static class InterfacedAnnotationMethodController implements MethodAnnotationControllerInterface { + + public String methodOne(String paramOne) { + return "value"; + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptorTests.java new file mode 100644 index 00000000000..6ee0d066b03 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/DelegatingAuthorizationMethodInterceptorTests.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Arrays; +import java.util.function.Supplier; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.After; +import org.junit.Test; + +import org.springframework.aop.Pointcut; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link DelegatingAuthorizationMethodInterceptor}. + * + * @author Evgeniy Cheban + */ +public class DelegatingAuthorizationMethodInterceptorTests { + + @After + public void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + public void invokeWhenAuthenticatedThenVerifyAdvicesUsage() throws Throwable { + Authentication authentication = TestAuthentication.authenticatedUser(); + SecurityContextHolder.setContext(new SecurityContextImpl(authentication)); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString"); + AuthorizationMethodInterceptor interceptor = mock(AuthorizationMethodInterceptor.class); + given(interceptor.getPointcut()).willReturn(Pointcut.TRUE); + given(interceptor.invoke(any(), any(AuthorizationMethodInvocation.class))).willReturn("abc"); + DelegatingAuthorizationMethodInterceptor chain = new DelegatingAuthorizationMethodInterceptor( + Arrays.asList(interceptor)); + Object result = chain.invoke(mockMethodInvocation); + assertThat(result).isEqualTo("abc"); + verify(interceptor).invoke(any(), any(AuthorizationMethodInvocation.class)); + } + + @Test + public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Throwable { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString"); + AuthorizationMethodInterceptor first = new AuthorizationMethodInterceptor() { + @Override + public Pointcut getPointcut() { + return Pointcut.TRUE; + } + + @Override + public Object invoke(Supplier<Authentication> authentication, MethodInvocation mi) { + return authentication.get(); + } + }; + AuthorizationMethodInterceptor second = mock(AuthorizationMethodInterceptor.class); + given(second.getPointcut()).willReturn(Pointcut.TRUE); + DelegatingAuthorizationMethodInterceptor interceptor = new DelegatingAuthorizationMethodInterceptor( + Arrays.asList(first, second)); + assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class) + .isThrownBy(() -> interceptor.invoke(mockMethodInvocation)) + .withMessage("An Authentication object was not found in the SecurityContext"); + verify(second, times(0)).invoke(any(), any()); + } + + public static class TestClass { + + public String doSomethingString() { + return null; + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java new file mode 100644 index 00000000000..0b9c63e7087 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java @@ -0,0 +1,162 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Collections; +import java.util.function.Supplier; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.junit.Test; + +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.core.Authentication; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link Jsr250AuthorizationManager}. + * + * @author Evgeniy Cheban + */ +public class Jsr250AuthorizationManagerTests { + + @Test + public void rolePrefixWhenNotSetThenDefaultsToRole() { + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + assertThat(manager).extracting("rolePrefix").isEqualTo("ROLE_"); + } + + @Test + public void setRolePrefixWhenNullThenException() { + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + assertThatIllegalArgumentException().isThrownBy(() -> manager.setRolePrefix(null)) + .withMessage("rolePrefix cannot be null"); + } + + @Test + public void setRolePrefixWhenNotNullThenSets() { + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + manager.setRolePrefix("CUSTOM_"); + assertThat(manager).extracting("rolePrefix").isEqualTo("CUSTOM_"); + } + + @Test + public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomething"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNull(); + } + + @Test + public void checkPermitAllRolesAllowedAdminWhenRoleUserThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "permitAllRolesAllowedAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkDenyAllRolesAllowedAdminWhenRoleAdminThenDeniedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "denyAllRolesAllowedAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "rolesAllowedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "rolesAllowedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception { + Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", + "ROLE_ANONYMOUS"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "rolesAllowedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); + AuthorizationDecision decision = manager.check(authentication, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + public static class TestClass { + + public void doSomething() { + + } + + @DenyAll + @RolesAllowed("ADMIN") + public void denyAllRolesAllowedAdmin() { + + } + + @PermitAll + @RolesAllowed("ADMIN") + public void permitAllRolesAllowedAdmin() { + + } + + @RolesAllowed({ "USER", "ADMIN" }) + public void rolesAllowedUserOrAdmin() { + + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManagerTests.java new file mode 100644 index 00000000000..47263040564 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManagerTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authorization.AuthorizationDecision; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link PostAuthorizeAuthorizationManager}. + * + * @author Evgeniy Cheban + */ +public class PostAuthorizeAuthorizationManagerTests { + + @Test + public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { + MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + manager.setExpressionHandler(expressionHandler); + assertThat(manager).extracting("expressionHandler").isEqualTo(expressionHandler); + } + + @Test + public void setExpressionHandlerWhenNullThenException() { + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null)) + .withMessage("expressionHandler cannot be null"); + } + + @Test + public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomething", new Class[] {}, new Object[] {}); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null); + assertThat(decision).isNull(); + } + + @Test + public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString", new Class[] { String.class }, new Object[] { "grant" }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString", new Class[] { String.class }, new Object[] { "deny" }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, null); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + @Test + public void checkDoSomethingListWhenReturnObjectContainsGrantThenGrantedDecision() throws Exception { + List<String> list = Arrays.asList("grant", "deny"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingList", new Class[] { List.class }, new Object[] { list }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkDoSomethingListWhenReturnObjectNotContainsGrantThenDeniedDecision() throws Exception { + List<String> list = Collections.singletonList("deny"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingList", new Class[] { List.class }, new Object[] { list }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation, list); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + public static class TestClass { + + public void doSomething() { + + } + + @PostAuthorize("#s == 'grant'") + public String doSomethingString(String s) { + return s; + } + + @PostAuthorize("returnObject.contains('grant')") + public List<String> doSomethingList(List<String> list) { + return list; + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptorTests.java new file mode 100644 index 00000000000..a792b5d1a53 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptorTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Collections; + +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.Test; + +import org.springframework.aop.MethodMatcher; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.authentication.TestAuthentication; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link PostFilterAuthorizationMethodInterceptor}. + * + * @author Evgeniy Cheban + */ +public class PostFilterAuthorizationMethodInterceptorTests { + + @Test + public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { + MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor(); + advice.setExpressionHandler(expressionHandler); + assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); + } + + @Test + public void setExpressionHandlerWhenNullThenException() { + PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor(); + assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) + .withMessage("expressionHandler cannot be null"); + } + + @Test + public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception { + PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor(); + MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); + assertThat(methodMatcher.matches(NoPostFilterClass.class.getMethod("doSomething"), NoPostFilterClass.class)) + .isFalse(); + } + + @Test + public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception { + PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor(); + MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); + assertThat( + methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class)) + .isTrue(); + } + + @Test + public void afterWhenArrayNotNullThenFilteredArray() throws Throwable { + String[] array = { "john", "bob" }; + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }) { + @Override + public Object proceed() { + return array; + } + }; + PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + Object result = advice.invoke(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john"); + } + + @PostFilter("filterObject == 'john'") + public static class TestClass { + + @PostFilter("filterObject == 'john'") + public String[] doSomethingArray(String[] array) { + return array; + } + + public String[] doSomethingArrayClassLevel(String[] array) { + return array; + } + + } + + public static class NoPostFilterClass { + + public void doSomething() { + + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManagerTests.java new file mode 100644 index 00000000000..b38c48c6bf2 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManagerTests.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Collections; + +import org.junit.Test; + +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authorization.AuthorizationDecision; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link PreAuthorizeAuthorizationManager}. + * + * @author Evgeniy Cheban + */ +public class PreAuthorizeAuthorizationManagerTests { + + @Test + public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { + MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); + manager.setExpressionHandler(expressionHandler); + assertThat(manager).extracting("expressionHandler").isEqualTo(expressionHandler); + } + + @Test + public void setExpressionHandlerWhenNullThenException() { + PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); + assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null)) + .withMessage("expressionHandler cannot be null"); + } + + @Test + public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomething", new Class[] {}, new Object[] {}); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNull(); + } + + @Test + public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString", new Class[] { String.class }, new Object[] { "grant" }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingString", new Class[] { String.class }, new Object[] { "deny" }); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + public static class TestClass { + + public void doSomething() { + + } + + @PreAuthorize("#s == 'grant'") + public String doSomethingString(String s) { + return s; + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptorTests.java new file mode 100644 index 00000000000..b9d54d8974e --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptorTests.java @@ -0,0 +1,203 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import org.springframework.aop.MethodMatcher; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.access.prepost.PreFilter; +import org.springframework.security.authentication.TestAuthentication; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link PreFilterAuthorizationMethodInterceptor}. + * + * @author Evgeniy Cheban + */ +public class PreFilterAuthorizationMethodInterceptorTests { + + @Test + public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() { + MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + advice.setExpressionHandler(expressionHandler); + assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler); + } + + @Test + public void setExpressionHandlerWhenNullThenException() { + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null)) + .withMessage("expressionHandler cannot be null"); + } + + @Test + public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception { + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); + assertThat(methodMatcher.matches(NoPreFilterClass.class.getMethod("doSomething"), NoPreFilterClass.class)) + .isFalse(); + } + + @Test + public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception { + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher(); + assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class), + TestClass.class)).isTrue(); + } + + @Test + public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + assertThatIllegalArgumentException() + .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage( + "Filter target was null, or no argument with name 'filterTargetNotMatch' found in method."); + } + + @Test + public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + assertThatIllegalArgumentException() + .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)) + .withMessage("Filter target was null, or no argument with name 'list' found in method."); + } + + @Test + public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable { + List<String> list = new ArrayList<>(); + list.add("john"); + list.add("bob"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + advice.invoke(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(list).hasSize(1); + assertThat(list.get(0)).isEqualTo("john"); + } + + @Test + public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + assertThatIllegalArgumentException() + .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)) + .withMessage("Filter target was null. Make sure you passing the correct value in the method argument."); + } + + @Test + public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable { + List<String> list = new ArrayList<>(); + list.add("john"); + list.add("bob"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + advice.invoke(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(list).hasSize(1); + assertThat(list.get(0)).isEqualTo("john"); + } + + @Test + public void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class }, + new Object[] { new String[] {} }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + assertThatIllegalStateException() + .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)).withMessage( + "Pre-filtering on array types is not supported. Using a Collection will solve this problem."); + } + + @Test + public void findFilterTargetWhenNameNotProvidedAndNotSingleArgThenException() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class }, + new Object[] { "", new ArrayList<>() }); + PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor(); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + assertThatIllegalStateException() + .isThrownBy(() -> advice.invoke(TestAuthentication::authenticatedUser, methodInvocation)) + .withMessage("Unable to determine the method argument for filtering. Specify the filter target."); + } + + @PreFilter("filterObject == 'john'") + public static class TestClass { + + @PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch") + public List<String> doSomethingListFilterTargetNotMatch(List<String> list) { + return list; + } + + @PreFilter(value = "filterObject == 'john'", filterTarget = "list") + public List<String> doSomethingListFilterTargetMatch(List<String> list) { + return list; + } + + @PreFilter("filterObject == 'john'") + public List<String> doSomethingListFilterTargetNotProvided(List<String> list) { + return list; + } + + @PreFilter("filterObject == 'john'") + public String[] doSomethingArrayFilterTargetNotProvided(String[] array) { + return array; + } + + public List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) { + return list; + } + + } + + public static class NoPreFilterClass { + + public void doSomething() { + + } + + } + +} diff --git a/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java new file mode 100644 index 00000000000..076fdf473a7 --- /dev/null +++ b/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.authorization.method; + +import java.util.Collections; +import java.util.function.Supplier; + +import org.junit.Test; + +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.intercept.method.MockMethodInvocation; +import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.core.Authentication; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SecuredAuthorizationManager}. + * + * @author Evgeniy Cheban + */ +public class SecuredAuthorizationManagerTests { + + @Test + public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "doSomething"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNull(); + } + + @Test + public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "securedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedUser, mockMethodInvocation, Collections.emptyList()); + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception { + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "securedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation( + TestAuthentication::authenticatedAdmin, mockMethodInvocation, Collections.emptyList()); + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isTrue(); + } + + @Test + public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception { + Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", + "ROLE_ANONYMOUS"); + MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "securedUserOrAdmin"); + AuthorizationMethodInvocation methodInvocation = new AuthorizationMethodInvocation(authentication, + mockMethodInvocation, Collections.emptyList()); + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + AuthorizationDecision decision = manager.check(authentication, methodInvocation); + assertThat(decision).isNotNull(); + assertThat(decision.isGranted()).isFalse(); + } + + public static class TestClass { + + public void doSomething() { + + } + + @Secured({ "ROLE_USER", "ROLE_ADMIN" }) + public void securedUserOrAdmin() { + + } + + } + +} diff --git a/crypto/spring-security-crypto.gradle b/crypto/spring-security-crypto.gradle index d4f73517baf..d69e1570c4d 100644 --- a/crypto/spring-security-crypto.gradle +++ b/crypto/spring-security-crypto.gradle @@ -1,6 +1,7 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { + management platform(project(":spring-security-dependencies")) optional 'org.springframework:spring-jcl' optional 'org.bouncycastle:bcpkix-jdk15on' } diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java index d8f2fc12b63..d0d92910d72 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java @@ -52,14 +52,49 @@ public final class AesBytesEncryptor implements BytesEncryptor { private static final String AES_GCM_ALGORITHM = "AES/GCM/NoPadding"; + /** + * Constructs an encryptor that uses AES encryption. Example: <code> + * AesBytesEncryptor encryptor = new AesBytesEncryptor(yourPassword, 5c0744940b5c369b); + * </code> Constructed encryptor uses a 16-byte IV and CBC mode encryption. To specify + * a custom length IV, use + * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator) + * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator)}. To specify both, a + * custom length IV and a different encryption mode, use + * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm) + * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)}. + * @param password the password value + * @param salt the hex-encoded salt value + */ public AesBytesEncryptor(String password, CharSequence salt) { this(password, salt, null); } + /** + * Constructs an encryptor that uses AES encryption. Example: <code> + * AesBytesEncryptor encryptor = + * new AesBytesEncryptor(yourPassword, 5c0744940b5c369b, KeyGenerators.secureRandom(16)); + * </code> Constructed encryptor uses CBC mode encryption. To specify a different + * encryption mode, use + * {@link AesBytesEncryptor#AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm) + * AesBytesEncryptor(String, CharSequence, BytesKeyGenerator, CipherAlgorithm)}. + * @param password the password value + * @param salt the hex-encoded salt value + * @param ivGenerator the generator used to generate the initialization vector + */ public AesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) { this(password, salt, ivGenerator, CipherAlgorithm.CBC); } + /** + * Constructs an encryptor that uses AES encryption. Example: <code> + * AesBytesEncryptor encryptor = + * new AesBytesEncryptor(yourPassword, 5c0744940b5c369b, KeyGenerators.secureRandom(16), CipherAlgorithm.GCM); + * </code> + * @param password the password value + * @param salt the hex-encoded salt value + * @param ivGenerator the generator used to generate the initialization vector + * @param alg the {@link CipherAlgorithm} to be used + */ public AesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator, CipherAlgorithm alg) { this(CipherUtils.newSecretKey("PBKDF2WithHmacSHA1", new PBEKeySpec(password.toCharArray(), Hex.decode(salt), 1024, 256)), ivGenerator, alg); diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java index ff7505b289b..f30ca5454a6 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java @@ -63,7 +63,7 @@ public static BytesEncryptor stronger(CharSequence password, CharSequence salt) * @param salt a hex-encoded, random, site-global salt value to use to generate the * key * - * @see #stronger(CharSequence, CharSequence) + * @see Encryptors#stronger(CharSequence, CharSequence) */ public static BytesEncryptor standard(CharSequence password, CharSequence salt) { return new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16)); diff --git a/data/spring-security-data.gradle b/data/spring-security-data.gradle index 8830889f2e2..b86af30805a 100644 --- a/data/spring-security-data.gradle +++ b/data/spring-security-data.gradle @@ -1,9 +1,10 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'javax.xml.bind:jaxb-api' - compile 'org.springframework.data:spring-data-commons' - compile 'org.springframework:spring-core' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'javax.xml.bind:jaxb-api' + api 'org.springframework.data:spring-data-commons' + api 'org.springframework:spring-core' } diff --git a/dependencies/spring-security-dependencies.gradle b/dependencies/spring-security-dependencies.gradle new file mode 100644 index 00000000000..08b230dd7cf --- /dev/null +++ b/dependencies/spring-security-dependencies.gradle @@ -0,0 +1,79 @@ +plugins { + id 'java-platform' +} + +javaPlatform { + allowDependencies() +} + +dependencies { + api platform("org.springframework:spring-framework-bom:5.3.5") + api platform("io.projectreactor:reactor-bom:2020.0.5") + api platform("io.rsocket:rsocket-bom:1.1.0") + api platform("org.springframework.data:spring-data-bom:2020.0.7") + api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion") + api platform("com.fasterxml.jackson:jackson-bom:2.12.2") + constraints { + api "ch.qos.logback:logback-classic:1.2.3" + api "com.google.inject:guice:3.0" + api "com.nimbusds:nimbus-jose-jwt:9.8.1" + api "com.nimbusds:oauth2-oidc-sdk:9.3.1" + api "com.squareup.okhttp3:mockwebserver:3.14.9" + api "com.squareup.okhttp3:okhttp:3.14.9" + api "com.unboundid:unboundid-ldapsdk:4.0.14" + api "commons-codec:commons-codec:1.14" + api "commons-collections:commons-collections:3.2.2" + api "commons-logging:commons-logging:1.2" + api "io.projectreactor.tools:blockhound:1.0.6.RELEASE" + api "javax.annotation:jsr250-api:1.0" + api "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.2" + api "javax.servlet.jsp:javax.servlet.jsp-api:2.3.3" + api "javax.servlet:javax.servlet-api:4.0.1" + api "javax.xml.bind:jaxb-api:2.3.1" + api "junit:junit:4.13.2" + api "ldapsdk:ldapsdk:4.1" + api "net.sf.ehcache:ehcache:2.10.6" + api "net.sourceforge.htmlunit:htmlunit:2.48.0" + api "net.sourceforge.nekohtml:nekohtml:1.9.22" + api "org.apache.directory.server:apacheds-core-entry:1.5.5" + api "org.apache.directory.server:apacheds-core:1.5.5" + api "org.apache.directory.server:apacheds-protocol-ldap:1.5.5" + api "org.apache.directory.server:apacheds-protocol-shared:1.5.5" + api "org.apache.directory.server:apacheds-server-jndi:1.5.5" + api "org.apache.directory.shared:shared-ldap:0.9.15" + api "org.apache.httpcomponents:httpclient:4.5.13" + api "org.aspectj:aspectjrt:$aspectjVersion" + api "org.aspectj:aspectjweaver:$aspectjVersion" + api "org.assertj:assertj-core:3.19.0" + api "org.bouncycastle:bcpkix-jdk15on:1.68" + api "org.bouncycastle:bcprov-jdk15on:1.68" + api "org.eclipse.jetty:jetty-server:9.4.39.v20210325" + api "org.eclipse.jetty:jetty-servlet:9.4.39.v20210325" + api "org.eclipse.persistence:javax.persistence:2.2.1" + api "org.hibernate:hibernate-entitymanager:5.4.30.Final" + api "org.hsqldb:hsqldb:2.6.0" + api "org.jasig.cas.client:cas-client-core:3.6.2" + api "org.mockito:mockito-core:3.9.0" + api "org.openid4java:openid4java-nodeps:0.9.6" + api "org.opensaml:opensaml-core:$openSamlVersion" + api "org.opensaml:opensaml-saml-api:$openSamlVersion" + api "org.opensaml:opensaml-saml-impl:$openSamlVersion" + api "org.powermock:powermock-api-mockito2:2.0.9" + api "org.powermock:powermock-api-support:2.0.9" + api "org.powermock:powermock-core:2.0.9" + api "org.powermock:powermock-module-junit4-common:2.0.9" + api "org.powermock:powermock-module-junit4:2.0.9" + api "org.powermock:powermock-reflect:2.0.9" + api "org.python:jython:2.5.3" + api "org.seleniumhq.selenium:htmlunit-driver:2.48.0" + api "org.seleniumhq.selenium:selenium-java:3.141.59" + api "org.seleniumhq.selenium:selenium-support:3.141.59" + api "org.skyscreamer:jsonassert:1.5.0" + api "org.slf4j:jcl-over-slf4j:1.7.30" + api "org.slf4j:log4j-over-slf4j:1.7.30" + api "org.slf4j:slf4j-api:1.7.30" + api "org.springframework.ldap:spring-ldap-core:2.3.3.RELEASE" + api "org.synchronoss.cloud:nio-multipart-parser:1.1.0" + } +} + diff --git a/docs/manual/spring-security-docs-manual.gradle b/docs/manual/spring-security-docs-manual.gradle index 28d315628d2..a0a2ececd9d 100644 --- a/docs/manual/spring-security-docs-manual.gradle +++ b/docs/manual/spring-security-docs-manual.gradle @@ -1,5 +1,5 @@ apply plugin: 'io.spring.convention.docs' -apply plugin: 'io.spring.convention.springdependencymangement' +apply plugin: 'io.spring.convention.management-configuration' apply plugin: 'io.spring.convention.dependency-set' apply plugin: 'io.spring.convention.repository' apply plugin: 'java' @@ -21,7 +21,7 @@ asciidoctorj { revnumber : project.version, 'gh-url': ghUrl, 'gh-samples-url': "$ghUrl/samples" - attributeProvider resolvedVersions(project.configurations.testCompile) + attributeProvider resolvedVersions(project.configurations.testRuntimeClasspath) } docsZip { @@ -31,9 +31,10 @@ docsZip { } dependencies { - testCompile "com.unboundid:unboundid-ldapsdk" - testCompile "org.apache.directory.server:apacheds-core" - testCompile "org.springframework:spring-core" + management platform(project(":spring-security-dependencies")) + testImplementation "com.unboundid:unboundid-ldapsdk" + testImplementation "org.apache.directory.server:apacheds-core" + testImplementation "org.springframework:spring-core" } def resolvedVersions(Configuration configuration) { diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc index b5f5652b517..c3a92fea4f2 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc @@ -32,7 +32,7 @@ For example, it might be desirable to persist the `CsrfToken` in a cookie to <<w By default the `CookieServerCsrfTokenRepository` will write to a cookie named `XSRF-TOKEN` and read it from a header named `X-XSRF-TOKEN` or the HTTP parameter `_csrf`. These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS] -You can configure `CookieCsrfTokenRepository` in Java Configuration using: +You can configure `CookieServerCsrfTokenRepository` in Java Configuration using: .Store CSRF Token in a Cookie ==== diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc index 23198edc99a..009e5515129 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc @@ -171,6 +171,7 @@ UserDetailsManager users(DataSource dataSource) { JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource); users.createUser(user); users.createUser(admin); + return users; } ---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc index a26bae8c3e6..57f456deb8a 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc @@ -489,7 +489,7 @@ fun authenticationProvider(authenticator: LdapAuthenticator): LdapAuthentication Spring Security's `LdapAuthoritiesPopulator` is used to determine what authorites are returned for the user. -.Minimal Password Compare Configuration +.LdapAuthoritiesPopulator Configuration ==== .Java [source,java,role="primary",attrs="-attributes"] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc index 573412f6bf9..7c1d4304b61 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc @@ -6,6 +6,185 @@ It provides support for JSR-250 annotation security as well as the framework's o From 3.0 you can also make use of new <<el-access,expression-based annotations>>. You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts. +=== EnableMethodSecurity + +In 5.5, we can enable annotation-based security using the `@EnableMethodSecurity` annotation on any `@Configuration` instance. + +[NOTE] +For earlier versions, please read about similar support with <<jc-enable-global-method-security, @EnableGlobalMethodSecurity>>. + +For example, the following would enable Spring Security's `@PreAuthorize` annotation: + +[source,java] +---- +@EnableMethodSecurity +public class MethodSecurityConfig { + // ... +} +---- + +Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly. +Spring Security's native annotatino support defines a set of attributes for the method. +These will be passed to the `DefaultAuthorizationMethodInterceptorChain` for it to make the actual decision: + +[source,java] +---- +public interface BankService { + + @PreAuthorize("hasRole('USER')") + Account readAccount(Long id); + + @PreAuthorize("hasRole('USER')") + Account[] findAccounts(); + + @PreAuthorize("hasRole('TELLER')") + Account post(Account account, double amount); +} +---- + +You can enable support for Spring Security's `@Secured` annotation using: + +[source,java] +---- +@EnableMethodSecurity(secureEnabled = true) +public class MethodSecurityConfig { + // ... +} +---- + +or JSR-250 using: + +[source,java] +---- +@EnableMethodSecurity(jsr250Enabled = true) +public class MethodSecurityConfig { + // ... +} +---- + +==== Customizing Authorization + +Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support. + +If you need to customize the way that expressions are handled, you can expose a custom `MethodSecurityExpressionHandler`, like so: + +[source,java] +---- +@Bean +MethodSecurityExpressionHandler methodSecurityExpressionHandler() { + DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); + handler.setTrustResolver(myCustomTrustResolver); + return handler; +} +---- + +Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`. + +You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so: + +[source,java] +---- +@Bean +GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults("MYPREFIX_"); +} +---- + +==== Custom Authorization Managers + +Method authorization is a combination of before- and after-method authorization. + +[NOTE] +Before-method authorization is performed before the method is invoked. +If that authorization denies access, the method is not invoked and an `AccessDeniedException` is thrown +After-method authorization is performed after the method is invoked, but before the method returns to the caller. +If that authorization denies access, the value is not returned and an `AccessDeniedException` is thrown + +To recreate what Spring Security does by default, you would publish the following bean: + +[source,java] +---- +@Bean +public List<AuthorizationMethodInterceptor> methodSecurity() { + return new DelegatingAuthorizationMethodInterceptor( + new PreFilterAuthorizationMethodInterceptor(), // before-method + AuthorizationMethodInterceptors.preAuthorize(), // before-method + new PostFilterAuthorizationMethodInterceptor(), // after-method + AuthorizationMethodInterceptors.postAuthorize() // after-method + ); +} +---- + +[NOTE] +Keep in mind that publishing a list of `AuthorizationMethodInterceptor`s will completely replace any Spring Security defaults. + +Interceptors are invoked in the order that they are declared. + +You may want to only support `@PreAuthorize` in your application, in which case you can do the following: + +[source,java] +---- +@Bean +public AuthorizationMethodInterceptor methodSecurity() { + return AuthorizationMethodInterceptors.preAuthorize(); +} +---- + +Or, you may have a custom before-method `AuthorizationManager` that you want to add to the list. + +In this case, you will need to tell Spring Security both the `AuthorizationManager` and to which methods and classes your authorization manager applies. + +Spring Security integrates with Spring AOP to achieve this. +Thus, you can configure Spring Security to support `@PreAuthorize`, `@PostAuthorize`, and your own `AuthorizationManager` like so: + +[source,java] +---- +@Bean +public AuthorizationMethodInterceptor methodSecurity() { + JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut(); + pattern.setPattern("org.mycompany.myapp.service.*"); + AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated(); + return new DelegatingAuthorizationMethodInterceptor( + AuthorizationMethodInterceptors.preAuthorize(), + new AuthorizationManagerBeforeMethodInterceptor(pattern, rule), + AuthorizationMethodInterceptors.postAuthorize() + ); +} +---- + +The same can be done for after-method authorization and `AfterMethodAuthorizationManager`. +After-method authorization is generally concerned with analysing the return value to verify access. + +For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so: + +[source,java] +---- +public interface BankService { + + @PreAuthorize("hasRole('USER')") + @PostAuthorize("returnObject.owner == authentication.name") + Account readAccount(Long id); +} +---- + +You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated. + +For example, instead of embedding a great deal of logic into the `@PostAuthorize` SpEL expression, you may want to wire your own `@Bean`. +In that case, you can configure it like so: + +[source,java] +---- +@Bean +public AuthorizationMethodInterceptor methodSecurity + (AfterMethodAuthorizationManager<MethodInvocation> rules) { + AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class); + return new DelegatingAuthorizationMethodInterceptor( + AuthorizationMethodInterceptors.preAuthorize(), + new AuthorizationManagerAfterMethodInterceptor(pattern, rules)); +} +---- + +[[jc-enable-global-method-security]] === EnableGlobalMethodSecurity We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc index cc07bd7ad38..a0431de31ed 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc @@ -223,13 +223,15 @@ static { public Element marshall(XMLObject object, Document document) throws MarshallingException { configureAuthnRequest((AuthnRequest) object); - return super.marshall(object, element); + return super.marshall(object, document); } private void configureAuthnRequest(AuthnRequest authnRequest) { authnRequest.setForceAuthN(true); } } + + factory.getMarshallerFactory().registerMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME, marshaller); }); } ---- diff --git a/etc/checkstyle/checkstyle-suppressions.xml b/etc/checkstyle/checkstyle-suppressions.xml index 8ad15ce4afc..6f58307877f 100644 --- a/etc/checkstyle/checkstyle-suppressions.xml +++ b/etc/checkstyle/checkstyle-suppressions.xml @@ -49,4 +49,5 @@ <suppress files="WebSocketMessageBrokerConfigTests\.java" checks="SpringMethodVisibility"/> <suppress files="WebSecurityConfigurationTests\.java" checks="SpringMethodVisibility"/> <suppress files="WithSecurityContextTestExecutionListenerTests\.java" checks="SpringMethodVisibility"/> + <suppress files="AbstractOAuth2AuthorizationGrantRequestEntityConverter\.java" checks="SpringMethodVisibility"/> </suppressions> diff --git a/gradle.properties b/gradle.properties index e958eda88c9..31054e6eee0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ aspectjVersion=1.9.6 -gaeVersion=1.9.82 -springJavaformatVersion=0.0.25 -springBootVersion=2.4.0 +springJavaformatVersion=0.0.27 +springBootVersion=2.4.2 +openSamlVersion=3.4.6 version=5.5.0-SNAPSHOT -kotlinVersion=1.4.10 +kotlinVersion=1.4.32 org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true org.gradle.caching=true diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle deleted file mode 100644 index d86276dd33d..00000000000 --- a/gradle/dependency-management.gradle +++ /dev/null @@ -1,133 +0,0 @@ -if (!project.hasProperty("reactorVersion")) { - ext.reactorVersion = "2020.0.+" -} - -if (!project.hasProperty("springVersion")) { - ext.springVersion = "5.3.+" -} - -if (!project.hasProperty("springDataVersion")) { - ext.springDataVersion = "Neumann-SR+" -} -if (!project.hasProperty("kotlinVersion")) { - ext.kotlinVersion = "1.+" -} -if (!project.hasProperty("rsocketVersion")) { - ext.rsocketVersion = "1.1.0" -} -if (!project.hasProperty("locksDisabled")) { - dependencyLocking { - lockAllConfigurations() - } -} -ext.openSamlVersion = "3.+" - -dependencies { - management platform("org.springframework:spring-framework-bom:$springVersion") - management platform("io.projectreactor:reactor-bom:$reactorVersion") - management platform("org.springframework.data:spring-data-releasetrain:$springDataVersion") - management platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion") - constraints { - management "ch.qos.logback:logback-classic:1.+" - management "com.fasterxml.jackson.core:jackson-databind:2.+" - management 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.+' - management "com.google.appengine:appengine-api-1.0-sdk:$gaeVersion" - management "com.google.appengine:appengine-api-labs:$gaeVersion" - management "com.google.appengine:appengine-api-stubs:$gaeVersion" - management "com.google.appengine:appengine-testing:$gaeVersion" - management "com.google.appengine:appengine:$gaeVersion" - management "com.google.inject:guice:3.0" - management "com.nimbusds:nimbus-jose-jwt:latest.release" - management "com.nimbusds:oauth2-oidc-sdk:latest.release" - management "com.squareup.okhttp3:mockwebserver:3.+" - management "com.squareup.okhttp3:okhttp:3.+" - management "com.sun.xml.bind:jaxb-core:2.+" - management "com.sun.xml.bind:jaxb-impl:2.+" - management "com.unboundid:unboundid-ldapsdk:4.+" - management "commons-codec:commons-codec:1.14" - management "commons-collections:commons-collections:3.+" - management "commons-httpclient:commons-httpclient:3.+" - management "commons-logging:commons-logging:1.2" - management "io.projectreactor.tools:blockhound:1.+" - management "io.rsocket:rsocket-core:${rsocketVersion}" - management "io.rsocket:rsocket-transport-netty:${rsocketVersion}" - management "javax.annotation:jsr250-api:1.+" - management "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.+" - management "javax.servlet.jsp:javax.servlet.jsp-api:2.+" - management "javax.servlet:javax.servlet-api:4.+" - management "javax.validation:validation-api:2.+" - management "javax.xml.bind:jaxb-api:2.+" - management "junit:junit:4.12" - management "ldapsdk:ldapsdk:4.+" - management "net.sf.ehcache:ehcache:2.+" - management "net.sourceforge.htmlunit:htmlunit:2.37.0" - management "net.sourceforge.nekohtml:nekohtml:1.+" - management "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.+" - management "opensymphony:sitemesh:2.+" - management "org.apache.directory.server:apacheds-core-entry:1.5.5" - management "org.apache.directory.server:apacheds-core:1.5.5" - management "org.apache.directory.server:apacheds-protocol-ldap:1.5.5" - management "org.apache.directory.server:apacheds-protocol-shared:1.5.5" - management "org.apache.directory.server:apacheds-server-jndi:1.5.5" - management "org.apache.directory.shared:shared-ldap:0.9.15" - management "org.apache.httpcomponents:httpclient:4.+" - management "org.apache.taglibs:taglibs-standard-jstlel:1.+" - management "org.aspectj:aspectjrt:$aspectjVersion" - management "org.aspectj:aspectjtools:$aspectjVersion" - management "org.aspectj:aspectjweaver:$aspectjVersion" - management "org.assertj:assertj-core:3.+" - management "org.bouncycastle:bcpkix-jdk15on:1.+" - management "org.bouncycastle:bcprov-jdk15on:1.+" - management "org.eclipse.jetty:jetty-server:9.4.19.v20190610" - management "org.eclipse.jetty:jetty-servlet:9.4.19.v20190610" - management "org.eclipse.persistence:javax.persistence:2.+" - management "org.hibernate:hibernate-entitymanager:5.+" - management "org.hibernate:hibernate-validator:6.+" - management "org.hsqldb:hsqldb:2.+" - management "org.jasig.cas.client:cas-client-core:3.+" - management "org.jasig.cas:cas-server-webapp:4.0.7@war" - management "org.mockito:mockito-core:3.3.+" - management "org.openid4java:openid4java-nodeps:0.+" - management "org.opensaml:opensaml-core:$openSamlVersion" - management "org.opensaml:opensaml-saml-api:$openSamlVersion" - management "org.opensaml:opensaml-saml-impl:$openSamlVersion" - management "org.powermock:powermock-api-mockito2:2.+" - management "org.powermock:powermock-api-support:2.+" - management "org.powermock:powermock-core:2.+" - management "org.powermock:powermock-module-junit4-common:2.+" - management "org.powermock:powermock-module-junit4:2.+" - management "org.powermock:powermock-reflect:2.+" - management "org.python:jython:2.5.+" - management "org.seleniumhq.selenium:htmlunit-driver:2.37.0" - management "org.seleniumhq.selenium:selenium-java:3.+" - management "org.seleniumhq.selenium:selenium-support:3.+" - management "org.skyscreamer:jsonassert:1.+" - management "org.slf4j:jcl-over-slf4j:1.+" - management "org.slf4j:log4j-over-slf4j:1.+" - management "org.slf4j:slf4j-api:1.+" - management "org.springframework.ldap:spring-ldap-core:latest.release" - management "org.synchronoss.cloud:nio-multipart-parser:1.+" - management "org.thymeleaf:thymeleaf-spring5:3.+" - } -} - -configurations { - all { - resolutionStrategy { - componentSelection { - all { ComponentSelection selection -> - def candidate = selection.getCandidate() - def version = candidate.getVersion().toLowerCase() - - if (version.contains("alpha") || version.contains("beta")) { - selection.reject("Rejecting $selection with version $version as alpha/beta") - } - - if (candidate.getModule().equals("jsr250-api") && version.equals("1.0-20050927.133100")) { - selection.reject("Rejecting $selection with version $version as invalid version") - } - } - } - } - } -} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12d38de6a48..442d9132ea3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/itest/context/spring-security-itest-context.gradle b/itest/context/spring-security-itest-context.gradle index 855cea3953e..4d48604b8dd 100644 --- a/itest/context/spring-security-itest-context.gradle +++ b/itest/context/spring-security-itest-context.gradle @@ -1,19 +1,20 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.python:jython' - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-tx' + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.python:jython' + implementation 'org.springframework:spring-aop' + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-tx' - testCompile project(':spring-security-web') - testCompile 'javax.servlet:javax.servlet-api' - testCompile 'org.springframework:spring-web' + testImplementation project(':spring-security-web') + testImplementation 'javax.servlet:javax.servlet-api' + testImplementation 'org.springframework:spring-web' - testRuntime project(':spring-security-config') - testRuntime 'org.aspectj:aspectjweaver' + testRuntimeOnly project(':spring-security-config') + testRuntimeOnly 'org.aspectj:aspectjweaver' } System.setProperty('python.cachedir.skip', 'true') diff --git a/itest/ldap/embedded-ldap-apacheds-default/spring-security-itest-ldap-embedded-apacheds-default.gradle b/itest/ldap/embedded-ldap-apacheds-default/spring-security-itest-ldap-embedded-apacheds-default.gradle index 980de93e6b6..1d69e8d1b7a 100644 --- a/itest/ldap/embedded-ldap-apacheds-default/spring-security-itest-ldap-embedded-apacheds-default.gradle +++ b/itest/ldap/embedded-ldap-apacheds-default/spring-security-itest-ldap-embedded-apacheds-default.gradle @@ -1,13 +1,16 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' - compile project(':spring-security-config') - compile project(':spring-security-ldap') + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-core' + implementation 'org.springframework:spring-tx' + implementation project(':spring-security-config') + implementation project(':spring-security-ldap') - runtime apachedsDependencies + runtimeOnly apachedsDependencies + + testImplementation project(path : ':spring-security-ldap', configuration : 'tests') } diff --git a/itest/ldap/embedded-ldap-mode-apacheds/spring-security-itest-ldap-embedded-mode-apacheds.gradle b/itest/ldap/embedded-ldap-mode-apacheds/spring-security-itest-ldap-embedded-mode-apacheds.gradle index 980de93e6b6..1d69e8d1b7a 100644 --- a/itest/ldap/embedded-ldap-mode-apacheds/spring-security-itest-ldap-embedded-mode-apacheds.gradle +++ b/itest/ldap/embedded-ldap-mode-apacheds/spring-security-itest-ldap-embedded-mode-apacheds.gradle @@ -1,13 +1,16 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' - compile project(':spring-security-config') - compile project(':spring-security-ldap') + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-core' + implementation 'org.springframework:spring-tx' + implementation project(':spring-security-config') + implementation project(':spring-security-ldap') - runtime apachedsDependencies + runtimeOnly apachedsDependencies + + testImplementation project(path : ':spring-security-ldap', configuration : 'tests') } diff --git a/itest/ldap/embedded-ldap-mode-unboundid/spring-security-itest-ldap-embedded-mode-unboundid.gradle b/itest/ldap/embedded-ldap-mode-unboundid/spring-security-itest-ldap-embedded-mode-unboundid.gradle index 45f180468b2..dfa4a147f3b 100644 --- a/itest/ldap/embedded-ldap-mode-unboundid/spring-security-itest-ldap-embedded-mode-unboundid.gradle +++ b/itest/ldap/embedded-ldap-mode-unboundid/spring-security-itest-ldap-embedded-mode-unboundid.gradle @@ -1,13 +1,14 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' - compile project(':spring-security-config') - compile project(':spring-security-ldap') + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-core' + implementation 'org.springframework:spring-tx' + implementation project(':spring-security-config') + implementation project(':spring-security-ldap') - testCompile "com.unboundid:unboundid-ldapsdk" + testImplementation "com.unboundid:unboundid-ldapsdk" } diff --git a/itest/ldap/embedded-ldap-none/spring-security-itest-ldap-embedded-none.gradle b/itest/ldap/embedded-ldap-none/spring-security-itest-ldap-embedded-none.gradle index 79d8418fdeb..973950b47e5 100644 --- a/itest/ldap/embedded-ldap-none/spring-security-itest-ldap-embedded-none.gradle +++ b/itest/ldap/embedded-ldap-none/spring-security-itest-ldap-embedded-none.gradle @@ -1,11 +1,12 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' - compile project(':spring-security-config') - compile project(':spring-security-ldap') + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-core' + implementation 'org.springframework:spring-tx' + implementation project(':spring-security-config') + implementation project(':spring-security-ldap') } diff --git a/itest/ldap/embedded-ldap-unboundid-default/spring-security-itest-ldap-embedded-unboundid-default.gradle b/itest/ldap/embedded-ldap-unboundid-default/spring-security-itest-ldap-embedded-unboundid-default.gradle index 45f180468b2..dfa4a147f3b 100644 --- a/itest/ldap/embedded-ldap-unboundid-default/spring-security-itest-ldap-embedded-unboundid-default.gradle +++ b/itest/ldap/embedded-ldap-unboundid-default/spring-security-itest-ldap-embedded-unboundid-default.gradle @@ -1,13 +1,14 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' - compile project(':spring-security-config') - compile project(':spring-security-ldap') + management platform(project(":spring-security-dependencies")) + implementation project(':spring-security-core') + implementation 'org.springframework:spring-beans' + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-core' + implementation 'org.springframework:spring-tx' + implementation project(':spring-security-config') + implementation project(':spring-security-ldap') - testCompile "com.unboundid:unboundid-ldapsdk" + testImplementation "com.unboundid:unboundid-ldapsdk" } diff --git a/itest/web/spring-security-itest-web.gradle b/itest/web/spring-security-itest-web.gradle index bbe818a3db7..6b0a9940f9e 100644 --- a/itest/web/spring-security-itest-web.gradle +++ b/itest/web/spring-security-itest-web.gradle @@ -1,20 +1,21 @@ apply plugin: 'io.spring.convention.spring-test' dependencies { - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + implementation 'org.springframework:spring-context' + implementation 'org.springframework:spring-web' provided 'javax.servlet:javax.servlet-api' - testCompile project(':spring-security-core') - testCompile project(':spring-security-test') - testCompile project(':spring-security-web') - testCompile 'org.springframework:spring-beans' - testCompile 'org.springframework:spring-test' - testCompile 'org.springframework:spring-webmvc' + testImplementation project(':spring-security-core') + testImplementation project(':spring-security-test') + testImplementation project(':spring-security-web') + testImplementation 'org.springframework:spring-beans' + testImplementation 'org.springframework:spring-test' + testImplementation 'org.springframework:spring-webmvc' - testRuntime project(':spring-security-config') - testRuntime project(':spring-security-ldap') + testRuntimeOnly project(':spring-security-config') + testRuntimeOnly project(':spring-security-ldap') } integrationTest { diff --git a/ldap/spring-security-ldap.gradle b/ldap/spring-security-ldap.gradle index 64f8c0003d2..c9aafc3f228 100644 --- a/ldap/spring-security-ldap.gradle +++ b/ldap/spring-security-ldap.gradle @@ -1,18 +1,19 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-tx' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-tx' optional apachedsDependencies optional 'ldapsdk:ldapsdk' optional "com.unboundid:unboundid-ldapsdk" optional 'org.apache.directory.shared:shared-ldap' - compile ('org.springframework.ldap:spring-ldap-core') { + api ('org.springframework.ldap:spring-ldap-core') { exclude(group: 'commons-logging', module: 'commons-logging') exclude(group: 'org.springframework', module: 'spring-beans') exclude(group: 'org.springframework', module: 'spring-core') @@ -20,9 +21,9 @@ dependencies { exclude(group: 'org.springframework.data', module: 'spring-data-commons') } - testCompile project(':spring-security-test') - testCompile 'org.slf4j:jcl-over-slf4j' - testCompile 'org.slf4j:slf4j-api' + testImplementation project(':spring-security-test') + testImplementation 'org.slf4j:jcl-over-slf4j' + testImplementation 'org.slf4j:slf4j-api' } integrationTest { diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDsContainerConfig.java b/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDsContainerConfig.java index b1a2aca60d0..41478863d6f 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDsContainerConfig.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/ApacheDsContainerConfig.java @@ -16,8 +16,7 @@ package org.springframework.security.ldap; -import javax.annotation.PreDestroy; - +import org.springframework.beans.factory.DisposableBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.ldap.core.ContextSource; @@ -27,7 +26,7 @@ * @author Eddú Meléndez */ @Configuration -public class ApacheDsContainerConfig { +public class ApacheDsContainerConfig implements DisposableBean { private ApacheDSContainer container; @@ -44,8 +43,8 @@ ContextSource contextSource(ApacheDSContainer ldapContainer) throws Exception { "ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=springframework,dc=org"); } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java index 1aeead97a3b..c19bacba04d 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/server/UnboundIdContainerLdifTests.java @@ -16,11 +16,10 @@ package org.springframework.security.ldap.server; -import javax.annotation.PreDestroy; - import org.junit.After; import org.junit.Test; +import org.springframework.beans.factory.DisposableBean; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -92,7 +91,7 @@ public void unboundIdContainerWhenWildcardLdifNotFoundThenProceeds() { } @Configuration - static class CustomLdifConfig { + static class CustomLdifConfig implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif"); @@ -109,15 +108,15 @@ ContextSource contextSource(UnboundIdContainer container) { "ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org"); } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } } @Configuration - static class WildcardLdifConfig { + static class WildcardLdifConfig implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath*:test-server.ldif"); @@ -134,15 +133,15 @@ ContextSource contextSource(UnboundIdContainer container) { "ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org"); } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } } @Configuration - static class MalformedLdifConfig { + static class MalformedLdifConfig implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server-malformed.txt"); @@ -153,15 +152,15 @@ UnboundIdContainer ldapContainer() { return this.container; } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } } @Configuration - static class MissingLdifConfig { + static class MissingLdifConfig implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:does-not-exist.ldif"); @@ -172,15 +171,15 @@ UnboundIdContainer ldapContainer() { return this.container; } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } } @Configuration - static class WildcardNoLdifConfig { + static class WildcardNoLdifConfig implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath*:*.test.ldif"); @@ -191,8 +190,8 @@ UnboundIdContainer ldapContainer() { return this.container; } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerModifyPasswordTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerModifyPasswordTests.java index ab2e6e3992d..bdb7fce9acf 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerModifyPasswordTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsManagerModifyPasswordTests.java @@ -16,12 +16,11 @@ package org.springframework.security.ldap.userdetails; -import javax.annotation.PreDestroy; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -81,7 +80,7 @@ public void changePasswordWhenOldPasswordIsCorrectThenPasses() { } @Configuration - static class UnboundIdContainerConfiguration { + static class UnboundIdContainerConfiguration implements DisposableBean { private UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:test-server.ldif"); @@ -98,8 +97,8 @@ ContextSource contextSource(UnboundIdContainer container) { "ldap://127.0.0.1:" + container.getPort() + "/dc=springframework,dc=org"); } - @PreDestroy - void shutdown() { + @Override + public void destroy() throws Exception { this.container.stop(); } diff --git a/ldap/src/test/java/org/apache/directory/server/core/avltree/ArrayMarshaller.java b/ldap/src/test/java/org/apache/directory/server/core/avltree/ArrayMarshaller.java new file mode 100644 index 00000000000..f322c879988 --- /dev/null +++ b/ldap/src/test/java/org/apache/directory/server/core/avltree/ArrayMarshaller.java @@ -0,0 +1,173 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.apache.directory.server.core.avltree; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Comparator; + +import org.apache.directory.shared.ldap.util.StringTools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class to serialize the Array data. + * + * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> + * @version $Rev$, $Date$ + */ +@SuppressWarnings("unchecked") +public class ArrayMarshaller<E> implements Marshaller<ArrayTree<E>> { + + /** static logger */ + private static final Logger LOG = LoggerFactory.getLogger(ArrayMarshaller.class); + + /** used for serialized form of an empty AvlTree */ + private static final byte[] EMPTY_TREE = new byte[1]; + + /** marshaller to be used for marshalling the keys */ + private Marshaller<E> keyMarshaller; + + /** key Comparator for the AvlTree */ + private Comparator<E> comparator; + + /** + * Creates a new instance of AvlTreeMarshaller with a custom key Marshaller. + * @param comparator Comparator to be used for key comparision + * @param keyMarshaller marshaller for keys + */ + public ArrayMarshaller(Comparator<E> comparator, Marshaller<E> keyMarshaller) { + this.comparator = comparator; + this.keyMarshaller = keyMarshaller; + } + + /** + * Creates a new instance of AvlTreeMarshaller with the default key Marshaller which + * uses Java Serialization. + * @param comparator Comparator to be used for key comparision + */ + public ArrayMarshaller(Comparator<E> comparator) { + this.comparator = comparator; + this.keyMarshaller = DefaultMarshaller.INSTANCE; + } + + /** + * Marshals the given tree to bytes + * @param tree the tree to be marshalled + */ + public byte[] serialize(ArrayTree<E> tree) { + if ((tree == null) || (tree.size() == 0)) { + return EMPTY_TREE; + } + + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(byteStream); + byte[] data = null; + + try { + out.writeByte(0); // represents the start of an Array byte stream + out.writeInt(tree.size()); + + for (int position = 0; position < tree.size(); position++) { + E value = tree.get(position); + byte[] bytes = this.keyMarshaller.serialize(value); + + // Write the key length + out.writeInt(bytes.length); + + // Write the key if its length is not null + if (bytes.length != 0) { + out.write(bytes); + } + } + + out.flush(); + data = byteStream.toByteArray(); + + // Try to deserialize, just to see + try { + deserialize(data); + } + catch (NullPointerException npe) { + System.out.println("Bad serialization, tree : [" + StringTools.dumpBytes(data) + "]"); + throw npe; + } + + out.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + + return data; + } + + /** + * Creates an Array from given bytes of data. + * @param data byte array to be converted into an array + */ + public ArrayTree<E> deserialize(byte[] data) throws IOException { + try { + if ((data == null) || (data.length == 0)) { + throw new IOException("Null or empty data array is invalid."); + } + + if ((data.length == 1) && (data[0] == 0)) { + E[] array = (E[]) new Object[] {}; + ArrayTree<E> tree = new ArrayTree<E>(this.comparator, array); + return tree; + } + + ByteArrayInputStream bin = new ByteArrayInputStream(data); + DataInputStream din = new DataInputStream(bin); + + byte startByte = din.readByte(); + + if (startByte != 0) { + throw new IOException("wrong array serialized data format"); + } + + int size = din.readInt(); + E[] nodes = (E[]) new Object[size]; + + for (int i = 0; i < size; i++) { + // Read the object's size + int dataSize = din.readInt(); + + if (dataSize != 0) { + byte[] bytes = new byte[dataSize]; + + din.read(bytes); + E key = this.keyMarshaller.deserialize(bytes); + nodes[i] = key; + } + } + + ArrayTree<E> arrayTree = new ArrayTree<E>(this.comparator, nodes); + + return arrayTree; + } + catch (NullPointerException npe) { + System.out.println("Bad tree : [" + StringTools.dumpBytes(data) + "]"); + throw npe; + } + } + +} diff --git a/messaging/spring-security-messaging.gradle b/messaging/spring-security-messaging.gradle index 05d8d34f0ad..2028b397f85 100644 --- a/messaging/spring-security-messaging.gradle +++ b/messaging/spring-security-messaging.gradle @@ -1,22 +1,23 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-expression' - compile 'org.springframework:spring-messaging' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-expression' + api 'org.springframework:spring-messaging' optional project(':spring-security-web') optional 'org.springframework:spring-websocket' optional 'io.projectreactor:reactor-core' optional 'javax.servlet:javax.servlet-api' - testCompile project(path: ':spring-security-core', configuration: 'tests') - testCompile 'commons-codec:commons-codec' - testCompile powerMock2Dependencies - testCompile slf4jDependencies + testImplementation project(path: ':spring-security-core', configuration: 'tests') + testImplementation 'commons-codec:commons-codec' + testImplementation powerMock2Dependencies + testImplementation slf4jDependencies - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' } diff --git a/oauth2/oauth2-client/spring-security-oauth2-client.gradle b/oauth2/oauth2-client/spring-security-oauth2-client.gradle index a966c27b8ca..f720f244efd 100644 --- a/oauth2/oauth2-client/spring-security-oauth2-client.gradle +++ b/oauth2/oauth2-client/spring-security-oauth2-client.gradle @@ -1,11 +1,12 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-oauth2-core') - compile project(':spring-security-web') - compile springCoreDependency - compile 'com.nimbusds:oauth2-oidc-sdk' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-oauth2-core') + api project(':spring-security-web') + api springCoreDependency + api 'com.nimbusds:oauth2-oidc-sdk' optional project(':spring-security-oauth2-jose') optional 'io.projectreactor:reactor-core' @@ -15,18 +16,18 @@ dependencies { optional 'org.springframework:spring-jdbc' optional 'org.springframework:spring-r2dbc' - testCompile project(path: ':spring-security-oauth2-core', configuration: 'tests') - testCompile project(path: ':spring-security-oauth2-jose', configuration: 'tests') - testCompile powerMock2Dependencies - testCompile 'com.squareup.okhttp3:mockwebserver' - testCompile 'io.projectreactor.netty:reactor-netty' - testCompile 'io.projectreactor:reactor-test' - testCompile 'io.projectreactor.tools:blockhound' - testCompile 'org.skyscreamer:jsonassert' - testCompile 'io.r2dbc:r2dbc-h2:0.8.4.RELEASE' - testCompile 'io.r2dbc:r2dbc-spi-test:0.8.3.RELEASE' + testImplementation project(path: ':spring-security-oauth2-core', configuration: 'tests') + testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests') + testImplementation powerMock2Dependencies + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation 'io.projectreactor.netty:reactor-netty' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'io.projectreactor.tools:blockhound' + testImplementation 'org.skyscreamer:jsonassert' + testImplementation 'io.r2dbc:r2dbc-h2:0.8.4.RELEASE' + testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.4.RELEASE' - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' provided 'javax.servlet:javax.servlet-api' } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequest.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequest.java index a0d5a698d41..8016896b321 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequest.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.security.oauth2.client.endpoint; +import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.util.Assert; @@ -27,6 +28,7 @@ * @author Joe Grandja * @since 5.0 * @see AuthorizationGrantType + * @see ClientRegistration * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3">Section * 1.3 Authorization Grant</a> */ @@ -34,13 +36,34 @@ public abstract class AbstractOAuth2AuthorizationGrantRequest { private final AuthorizationGrantType authorizationGrantType; + private final ClientRegistration clientRegistration; + /** * Sub-class constructor. * @param authorizationGrantType the authorization grant type + * @deprecated Use + * {@link #AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType, ClientRegistration)} + * instead */ + @Deprecated protected AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType authorizationGrantType) { Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null"); this.authorizationGrantType = authorizationGrantType; + this.clientRegistration = null; + } + + /** + * Sub-class constructor. + * @param authorizationGrantType the authorization grant type + * @param clientRegistration the client registration + * @since 5.5 + */ + protected AbstractOAuth2AuthorizationGrantRequest(AuthorizationGrantType authorizationGrantType, + ClientRegistration clientRegistration) { + Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null"); + Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + this.authorizationGrantType = authorizationGrantType; + this.clientRegistration = clientRegistration; } /** @@ -51,4 +74,13 @@ public AuthorizationGrantType getGrantType() { return this.authorizationGrantType; } + /** + * Returns the {@link ClientRegistration client registration}. + * @return the {@link ClientRegistration} + * @since 5.5 + */ + public ClientRegistration getClientRegistration() { + return this.clientRegistration; + } + } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequestEntityConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequestEntityConverter.java new file mode 100644 index 00000000000..7da5d530445 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractOAuth2AuthorizationGrantRequestEntityConverter.java @@ -0,0 +1,173 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.net.URI; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Base implementation of a {@link Converter} that converts the provided + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link RequestEntity} + * representation of an OAuth 2.0 Access Token Request for the Authorization Grant. + * + * @param <T> the type of {@link AbstractOAuth2AuthorizationGrantRequest} + * @author Joe Grandja + * @since 5.5 + * @see Converter + * @see AbstractOAuth2AuthorizationGrantRequest + * @see RequestEntity + */ +abstract class AbstractOAuth2AuthorizationGrantRequestEntityConverter<T extends AbstractOAuth2AuthorizationGrantRequest> + implements Converter<T, RequestEntity<?>> { + + // @formatter:off + private Converter<T, HttpHeaders> headersConverter = + (authorizationGrantRequest) -> OAuth2AuthorizationGrantRequestEntityUtils + .getTokenRequestHeaders(authorizationGrantRequest.getClientRegistration()); + // @formatter:on + + private Converter<T, MultiValueMap<String, String>> parametersConverter = this::createParameters; + + @Override + public RequestEntity<?> convert(T authorizationGrantRequest) { + HttpHeaders headers = getHeadersConverter().convert(authorizationGrantRequest); + MultiValueMap<String, String> parameters = getParametersConverter().convert(authorizationGrantRequest); + URI uri = UriComponentsBuilder + .fromUriString(authorizationGrantRequest.getClientRegistration().getProviderDetails().getTokenUri()) + .build().toUri(); + return new RequestEntity<>(parameters, headers, HttpMethod.POST, uri); + } + + /** + * Returns a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access + * Token Request body. + * @param authorizationGrantRequest the authorization grant request + * @return a {@link MultiValueMap} of the parameters used in the OAuth 2.0 Access + * Token Request body + */ + abstract MultiValueMap<String, String> createParameters(T authorizationGrantRequest); + + /** + * Returns the {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders} + * used in the OAuth 2.0 Access Token Request headers. + * @return the {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to {@link HttpHeaders} + */ + final Converter<T, HttpHeaders> getHeadersConverter() { + return this.headersConverter; + } + + /** + * Sets the {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders} + * used in the OAuth 2.0 Access Token Request headers. + * @param headersConverter the {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to {@link HttpHeaders} + */ + public final void setHeadersConverter(Converter<T, HttpHeaders> headersConverter) { + Assert.notNull(headersConverter, "headersConverter cannot be null"); + this.headersConverter = headersConverter; + } + + /** + * Add (compose) the provided {@code headersConverter} to the current + * {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders} + * used in the OAuth 2.0 Access Token Request headers. + * @param headersConverter the {@link Converter} to add (compose) to the current + * {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to a {@link HttpHeaders} + */ + public final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter) { + Assert.notNull(headersConverter, "headersConverter cannot be null"); + Converter<T, HttpHeaders> currentHeadersConverter = this.headersConverter; + this.headersConverter = (authorizationGrantRequest) -> { + // Append headers using a Composite Converter + HttpHeaders headers = currentHeadersConverter.convert(authorizationGrantRequest); + if (headers == null) { + headers = new HttpHeaders(); + } + HttpHeaders headersToAdd = headersConverter.convert(authorizationGrantRequest); + if (headersToAdd != null) { + headers.addAll(headersToAdd); + } + return headers; + }; + } + + /** + * Returns the {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap} + * of the parameters used in the OAuth 2.0 Access Token Request body. + * @return the {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to a {@link MultiValueMap} of the + * parameters + */ + final Converter<T, MultiValueMap<String, String>> getParametersConverter() { + return this.parametersConverter; + } + + /** + * Sets the {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap} + * of the parameters used in the OAuth 2.0 Access Token Request body. + * @param parametersConverter the {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to a {@link MultiValueMap} of the + * parameters + */ + public final void setParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) { + Assert.notNull(parametersConverter, "parametersConverter cannot be null"); + this.parametersConverter = parametersConverter; + } + + /** + * Add (compose) the provided {@code parametersConverter} to the current + * {@link Converter} used for converting the + * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap} + * of the parameters used in the OAuth 2.0 Access Token Request body. + * @param parametersConverter the {@link Converter} to add (compose) to the current + * {@link Converter} used for converting the + * {@link OAuth2AuthorizationCodeGrantRequest} to a {@link MultiValueMap} of the + * parameters + */ + public final void addParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) { + Assert.notNull(parametersConverter, "parametersConverter cannot be null"); + Converter<T, MultiValueMap<String, String>> currentParametersConverter = this.parametersConverter; + this.parametersConverter = (authorizationGrantRequest) -> { + // Append parameters using a Composite Converter + MultiValueMap<String, String> parameters = currentParametersConverter.convert(authorizationGrantRequest); + if (parameters == null) { + parameters = new LinkedMultiValueMap<>(); + } + MultiValueMap<String, String> parametersToAdd = parametersConverter.convert(authorizationGrantRequest); + if (parametersToAdd != null) { + parameters.addAll(parametersToAdd); + } + return parameters; + }; + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeader.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeader.java new file mode 100644 index 00000000000..b148e670a3e --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeader.java @@ -0,0 +1,390 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import org.springframework.security.oauth2.core.converter.ClaimConversionService; +import org.springframework.security.oauth2.jose.JwaAlgorithm; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.util.Assert; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * The JOSE header is a JSON object representing the header parameters of a JSON Web + * Token, whether the JWT is a JWS or JWE, that describe the cryptographic operations + * applied to the JWT and optionally, additional properties of the JWT. + * + * @author Anoop Garlapati + * @author Joe Grandja + * @since 5.5 + * @see Jwt + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-5">JWT JOSE + * Header</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515#section-4">JWS JOSE + * Header</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516#section-4">JWE JOSE + * Header</a> + */ +final class JoseHeader { + + private final Map<String, Object> headers; + + private JoseHeader(Map<String, Object> headers) { + this.headers = Collections.unmodifiableMap(new HashMap<>(headers)); + } + + /** + * Returns the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or + * encrypt the JWE. + * @return the {@link JwaAlgorithm} + */ + @SuppressWarnings("unchecked") + <T extends JwaAlgorithm> T getAlgorithm() { + return (T) getHeader(JoseHeaderNames.ALG); + } + + /** + * Returns the JWK Set URL that refers to the resource of a set of JSON-encoded public + * keys, one of which corresponds to the key used to digitally sign the JWS or encrypt + * the JWE. + * @return the JWK Set URL + */ + URL getJwkSetUrl() { + return getHeader(JoseHeaderNames.JKU); + } + + /** + * Returns the JSON Web Key which is the public key that corresponds to the key used + * to digitally sign the JWS or encrypt the JWE. + * @return the JSON Web Key + */ + Map<String, Object> getJwk() { + return getHeader(JoseHeaderNames.JWK); + } + + /** + * Returns the key ID that is a hint indicating which key was used to secure the JWS + * or JWE. + * @return the key ID + */ + String getKeyId() { + return getHeader(JoseHeaderNames.KID); + } + + /** + * Returns the X.509 URL that refers to the resource for the X.509 public key + * certificate or certificate chain corresponding to the key used to digitally sign + * the JWS or encrypt the JWE. + * @return the X.509 URL + */ + URL getX509Url() { + return getHeader(JoseHeaderNames.X5U); + } + + /** + * Returns the X.509 certificate chain that contains the X.509 public key certificate + * or certificate chain corresponding to the key used to digitally sign the JWS or + * encrypt the JWE. The certificate or certificate chain is represented as a + * {@code List} of certificate value {@code String}s. Each {@code String} in the + * {@code List} is a Base64-encoded DER PKIX certificate value. + * @return the X.509 certificate chain + */ + List<String> getX509CertificateChain() { + return getHeader(JoseHeaderNames.X5C); + } + + /** + * Returns the X.509 certificate SHA-1 thumbprint that is a base64url-encoded SHA-1 + * thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate + * corresponding to the key used to digitally sign the JWS or encrypt the JWE. + * @return the X.509 certificate SHA-1 thumbprint + */ + String getX509SHA1Thumbprint() { + return getHeader(JoseHeaderNames.X5T); + } + + /** + * Returns the X.509 certificate SHA-256 thumbprint that is a base64url-encoded + * SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate + * corresponding to the key used to digitally sign the JWS or encrypt the JWE. + * @return the X.509 certificate SHA-256 thumbprint + */ + String getX509SHA256Thumbprint() { + return getHeader(JoseHeaderNames.X5T_S256); + } + + /** + * Returns the type header that declares the media type of the JWS/JWE. + * @return the type header + */ + String getType() { + return getHeader(JoseHeaderNames.TYP); + } + + /** + * Returns the content type header that declares the media type of the secured content + * (the payload). + * @return the content type header + */ + String getContentType() { + return getHeader(JoseHeaderNames.CTY); + } + + /** + * Returns the critical headers that indicates which extensions to the JWS/JWE/JWA + * specifications are being used that MUST be understood and processed. + * @return the critical headers + */ + Set<String> getCritical() { + return getHeader(JoseHeaderNames.CRIT); + } + + /** + * Returns the headers. + * @return the headers + */ + Map<String, Object> getHeaders() { + return this.headers; + } + + /** + * Returns the header value. + * @param name the header name + * @param <T> the type of the header value + * @return the header value + */ + @SuppressWarnings("unchecked") + <T> T getHeader(String name) { + Assert.hasText(name, "name cannot be empty"); + return (T) getHeaders().get(name); + } + + /** + * Returns a new {@link Builder}, initialized with the provided {@link JwaAlgorithm}. + * @param jwaAlgorithm the {@link JwaAlgorithm} + * @return the {@link Builder} + */ + static Builder withAlgorithm(JwaAlgorithm jwaAlgorithm) { + return new Builder(jwaAlgorithm); + } + + /** + * Returns a new {@link Builder}, initialized with the provided {@code headers}. + * @param headers the headers + * @return the {@link Builder} + */ + static Builder from(JoseHeader headers) { + return new Builder(headers); + } + + /** + * A builder for {@link JoseHeader}. + */ + static final class Builder { + + final Map<String, Object> headers = new HashMap<>(); + + private Builder(JwaAlgorithm jwaAlgorithm) { + algorithm(jwaAlgorithm); + } + + private Builder(JoseHeader headers) { + Assert.notNull(headers, "headers cannot be null"); + this.headers.putAll(headers.getHeaders()); + } + + /** + * Sets the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or + * encrypt the JWE. + * @param jwaAlgorithm the {@link JwaAlgorithm} + * @return the {@link Builder} + */ + Builder algorithm(JwaAlgorithm jwaAlgorithm) { + Assert.notNull(jwaAlgorithm, "jwaAlgorithm cannot be null"); + return header(JoseHeaderNames.ALG, jwaAlgorithm); + } + + /** + * Sets the JWK Set URL that refers to the resource of a set of JSON-encoded + * public keys, one of which corresponds to the key used to digitally sign the JWS + * or encrypt the JWE. + * @param jwkSetUrl the JWK Set URL + * @return the {@link Builder} + */ + Builder jwkSetUrl(String jwkSetUrl) { + return header(JoseHeaderNames.JKU, convertAsURL(JoseHeaderNames.JKU, jwkSetUrl)); + } + + /** + * Sets the JSON Web Key which is the public key that corresponds to the key used + * to digitally sign the JWS or encrypt the JWE. + * @param jwk the JSON Web Key + * @return the {@link Builder} + */ + Builder jwk(Map<String, Object> jwk) { + return header(JoseHeaderNames.JWK, jwk); + } + + /** + * Sets the key ID that is a hint indicating which key was used to secure the JWS + * or JWE. + * @param keyId the key ID + * @return the {@link Builder} + */ + Builder keyId(String keyId) { + return header(JoseHeaderNames.KID, keyId); + } + + /** + * Sets the X.509 URL that refers to the resource for the X.509 public key + * certificate or certificate chain corresponding to the key used to digitally + * sign the JWS or encrypt the JWE. + * @param x509Url the X.509 URL + * @return the {@link Builder} + */ + Builder x509Url(String x509Url) { + return header(JoseHeaderNames.X5U, convertAsURL(JoseHeaderNames.X5U, x509Url)); + } + + /** + * Sets the X.509 certificate chain that contains the X.509 public key certificate + * or certificate chain corresponding to the key used to digitally sign the JWS or + * encrypt the JWE. The certificate or certificate chain is represented as a + * {@code List} of certificate value {@code String}s. Each {@code String} in the + * {@code List} is a Base64-encoded DER PKIX certificate value. + * @param x509CertificateChain the X.509 certificate chain + * @return the {@link Builder} + */ + Builder x509CertificateChain(List<String> x509CertificateChain) { + return header(JoseHeaderNames.X5C, x509CertificateChain); + } + + /** + * Sets the X.509 certificate SHA-1 thumbprint that is a base64url-encoded SHA-1 + * thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate + * corresponding to the key used to digitally sign the JWS or encrypt the JWE. + * @param x509SHA1Thumbprint the X.509 certificate SHA-1 thumbprint + * @return the {@link Builder} + */ + Builder x509SHA1Thumbprint(String x509SHA1Thumbprint) { + return header(JoseHeaderNames.X5T, x509SHA1Thumbprint); + } + + /** + * Sets the X.509 certificate SHA-256 thumbprint that is a base64url-encoded + * SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate + * corresponding to the key used to digitally sign the JWS or encrypt the JWE. + * @param x509SHA256Thumbprint the X.509 certificate SHA-256 thumbprint + * @return the {@link Builder} + */ + Builder x509SHA256Thumbprint(String x509SHA256Thumbprint) { + return header(JoseHeaderNames.X5T_S256, x509SHA256Thumbprint); + } + + /** + * Sets the type header that declares the media type of the JWS/JWE. + * @param type the type header + * @return the {@link Builder} + */ + Builder type(String type) { + return header(JoseHeaderNames.TYP, type); + } + + /** + * Sets the content type header that declares the media type of the secured + * content (the payload). + * @param contentType the content type header + * @return the {@link Builder} + */ + Builder contentType(String contentType) { + return header(JoseHeaderNames.CTY, contentType); + } + + /** + * Sets the critical headers that indicates which extensions to the JWS/JWE/JWA + * specifications are being used that MUST be understood and processed. + * @param headerNames the critical header names + * @return the {@link Builder} + */ + Builder critical(Set<String> headerNames) { + return header(JoseHeaderNames.CRIT, headerNames); + } + + /** + * Sets the header. + * @param name the header name + * @param value the header value + * @return the {@link Builder} + */ + Builder header(String name, Object value) { + Assert.hasText(name, "name cannot be empty"); + Assert.notNull(value, "value cannot be null"); + this.headers.put(name, value); + return this; + } + + /** + * A {@code Consumer} to be provided access to the headers allowing the ability to + * add, replace, or remove. + * @param headersConsumer a {@code Consumer} of the headers + * @return the {@link Builder} + */ + Builder headers(Consumer<Map<String, Object>> headersConsumer) { + headersConsumer.accept(this.headers); + return this; + } + + /** + * Builds a new {@link JoseHeader}. + * @return a {@link JoseHeader} + */ + JoseHeader build() { + Assert.notEmpty(this.headers, "headers cannot be empty"); + return new JoseHeader(this.headers); + } + + private static URL convertAsURL(String header, String value) { + URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class); + Assert.isTrue(convertedValue != null, + () -> "Unable to convert header '" + header + "' of type '" + value.getClass() + "' to URL."); + return convertedValue; + } + + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderNames.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderNames.java new file mode 100644 index 00000000000..41abd7eeba0 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderNames.java @@ -0,0 +1,127 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * The Registered Header Parameter Names defined by the JSON Web Token (JWT), JSON Web + * Signature (JWS) and JSON Web Encryption (JWE) specifications that may be contained in + * the JOSE Header of a JWT. + * + * @author Anoop Garlapati + * @author Joe Grandja + * @since 5.5 + * @see JoseHeader + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-5">JWT JOSE + * Header</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515#section-4">JWS JOSE + * Header</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516#section-4">JWE JOSE + * Header</a> + */ +final class JoseHeaderNames { + + /** + * {@code alg} - the algorithm header identifies the cryptographic algorithm used to + * secure a JWS or JWE + */ + static final String ALG = "alg"; + + /** + * {@code jku} - the JWK Set URL header is a URI that refers to a resource for a set + * of JSON-encoded public keys, one of which corresponds to the key used to digitally + * sign a JWS or encrypt a JWE + */ + static final String JKU = "jku"; + + /** + * {@code jwk} - the JSON Web Key header is the public key that corresponds to the key + * used to digitally sign a JWS or encrypt a JWE + */ + static final String JWK = "jwk"; + + /** + * {@code kid} - the key ID header is a hint indicating which key was used to secure a + * JWS or JWE + */ + static final String KID = "kid"; + + /** + * {@code x5u} - the X.509 URL header is a URI that refers to a resource for the X.509 + * public key certificate or certificate chain corresponding to the key used to + * digitally sign a JWS or encrypt a JWE + */ + static final String X5U = "x5u"; + + /** + * {@code x5c} - the X.509 certificate chain header contains the X.509 public key + * certificate or certificate chain corresponding to the key used to digitally sign a + * JWS or encrypt a JWE + */ + static final String X5C = "x5c"; + + /** + * {@code x5t} - the X.509 certificate SHA-1 thumbprint header is a base64url-encoded + * SHA-1 thumbprint (a.k.a. digest) of the DER encoding of the X.509 certificate + * corresponding to the key used to digitally sign a JWS or encrypt a JWE + */ + static final String X5T = "x5t"; + + /** + * {@code x5t#S256} - the X.509 certificate SHA-256 thumbprint header is a + * base64url-encoded SHA-256 thumbprint (a.k.a. digest) of the DER encoding of the + * X.509 certificate corresponding to the key used to digitally sign a JWS or encrypt + * a JWE + */ + static final String X5T_S256 = "x5t#S256"; + + /** + * {@code typ} - the type header is used by JWS/JWE applications to declare the media + * type of a JWS/JWE + */ + static final String TYP = "typ"; + + /** + * {@code cty} - the content type header is used by JWS/JWE applications to declare + * the media type of the secured content (the payload) + */ + static final String CTY = "cty"; + + /** + * {@code crit} - the critical header indicates that extensions to the JWS/JWE/JWA + * specifications are being used that MUST be understood and processed + */ + static final String CRIT = "crit"; + + private JoseHeaderNames() { + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSet.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSet.java new file mode 100644 index 00000000000..d383c04b661 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSet.java @@ -0,0 +1,222 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.net.URL; +import java.time.Instant; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.springframework.security.oauth2.core.converter.ClaimConversionService; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimAccessor; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.util.Assert; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * The {@link Jwt JWT} Claims Set is a JSON object representing the claims conveyed by a + * JSON Web Token. + * + * @author Anoop Garlapati + * @author Joe Grandja + * @since 5.5 + * @see Jwt + * @see JwtClaimAccessor + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-4">JWT Claims + * Set</a> + */ +final class JwtClaimsSet implements JwtClaimAccessor { + + private final Map<String, Object> claims; + + private JwtClaimsSet(Map<String, Object> claims) { + this.claims = Collections.unmodifiableMap(new HashMap<>(claims)); + } + + @Override + public Map<String, Object> getClaims() { + return this.claims; + } + + /** + * Returns a new {@link Builder}. + * @return the {@link Builder} + */ + static Builder builder() { + return new Builder(); + } + + /** + * Returns a new {@link Builder}, initialized with the provided {@code claims}. + * @param claims a JWT claims set + * @return the {@link Builder} + */ + static Builder from(JwtClaimsSet claims) { + return new Builder(claims); + } + + /** + * A builder for {@link JwtClaimsSet}. + */ + static final class Builder { + + final Map<String, Object> claims = new HashMap<>(); + + private Builder() { + } + + private Builder(JwtClaimsSet claims) { + Assert.notNull(claims, "claims cannot be null"); + this.claims.putAll(claims.getClaims()); + } + + /** + * Sets the issuer {@code (iss)} claim, which identifies the principal that issued + * the JWT. + * @param issuer the issuer identifier + * @return the {@link Builder} + */ + Builder issuer(String issuer) { + return claim(JwtClaimNames.ISS, issuer); + } + + /** + * Sets the subject {@code (sub)} claim, which identifies the principal that is + * the subject of the JWT. + * @param subject the subject identifier + * @return the {@link Builder} + */ + Builder subject(String subject) { + return claim(JwtClaimNames.SUB, subject); + } + + /** + * Sets the audience {@code (aud)} claim, which identifies the recipient(s) that + * the JWT is intended for. + * @param audience the audience that this JWT is intended for + * @return the {@link Builder} + */ + Builder audience(List<String> audience) { + return claim(JwtClaimNames.AUD, audience); + } + + /** + * Sets the expiration time {@code (exp)} claim, which identifies the time on or + * after which the JWT MUST NOT be accepted for processing. + * @param expiresAt the time on or after which the JWT MUST NOT be accepted for + * processing + * @return the {@link Builder} + */ + Builder expiresAt(Instant expiresAt) { + return claim(JwtClaimNames.EXP, expiresAt); + } + + /** + * Sets the not before {@code (nbf)} claim, which identifies the time before which + * the JWT MUST NOT be accepted for processing. + * @param notBefore the time before which the JWT MUST NOT be accepted for + * processing + * @return the {@link Builder} + */ + Builder notBefore(Instant notBefore) { + return claim(JwtClaimNames.NBF, notBefore); + } + + /** + * Sets the issued at {@code (iat)} claim, which identifies the time at which the + * JWT was issued. + * @param issuedAt the time at which the JWT was issued + * @return the {@link Builder} + */ + Builder issuedAt(Instant issuedAt) { + return claim(JwtClaimNames.IAT, issuedAt); + } + + /** + * Sets the JWT ID {@code (jti)} claim, which provides a unique identifier for the + * JWT. + * @param jti the unique identifier for the JWT + * @return the {@link Builder} + */ + Builder id(String jti) { + return claim(JwtClaimNames.JTI, jti); + } + + /** + * Sets the claim. + * @param name the claim name + * @param value the claim value + * @return the {@link Builder} + */ + Builder claim(String name, Object value) { + Assert.hasText(name, "name cannot be empty"); + Assert.notNull(value, "value cannot be null"); + this.claims.put(name, value); + return this; + } + + /** + * A {@code Consumer} to be provided access to the claims allowing the ability to + * add, replace, or remove. + * @param claimsConsumer a {@code Consumer} of the claims + */ + Builder claims(Consumer<Map<String, Object>> claimsConsumer) { + claimsConsumer.accept(this.claims); + return this; + } + + /** + * Builds a new {@link JwtClaimsSet}. + * @return a {@link JwtClaimsSet} + */ + JwtClaimsSet build() { + Assert.notEmpty(this.claims, "claims cannot be empty"); + + // The value of the 'iss' claim is a String or URL (StringOrURI). + // Attempt to convert to URL. + Object issuer = this.claims.get(JwtClaimNames.ISS); + if (issuer != null) { + URL convertedValue = ClaimConversionService.getSharedInstance().convert(issuer, URL.class); + if (convertedValue != null) { + this.claims.put(JwtClaimNames.ISS, convertedValue); + } + } + + return new JwtClaimsSet(this.claims); + } + + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtEncodingException.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtEncodingException.java new file mode 100644 index 00000000000..53c82b13bd5 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/JwtEncodingException.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import org.springframework.security.oauth2.jwt.JwtException; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * This exception is thrown when an error occurs while attempting to encode a JSON Web + * Token (JWT). + * + * @author Joe Grandja + * @since 5.5 + */ +class JwtEncodingException extends JwtException { + + /** + * Constructs a {@code JwtEncodingException} using the provided parameters. + * @param message the detail message + */ + JwtEncodingException(String message) { + super(message); + } + + /** + * Constructs a {@code JwtEncodingException} using the provided parameters. + * @param message the detail message + * @param cause the root cause + */ + JwtEncodingException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoder.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoder.java new file mode 100644 index 00000000000..c6d681a6232 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoder.java @@ -0,0 +1,359 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.net.URI; +import java.net.URL; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.factories.DefaultJWSSignerFactory; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKMatcher; +import com.nimbusds.jose.jwk.JWKSelector; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.KeyUse; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jose.produce.JWSSignerFactory; +import com.nimbusds.jose.util.Base64; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * A JWT encoder that encodes a JSON Web Token (JWT) using the JSON Web Signature (JWS) + * Compact Serialization format. The private/secret key used for signing the JWS is + * supplied by the {@code com.nimbusds.jose.jwk.source.JWKSource} provided via the + * constructor. + * + * <p> + * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK. + * + * @author Joe Grandja + * @since 5.5 + * @see com.nimbusds.jose.jwk.source.JWKSource + * @see com.nimbusds.jose.jwk.JWK + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token + * (JWT)</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature + * (JWS)</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515#section-3.1">JWS + * Compact Serialization</a> + * @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus + * JOSE + JWT SDK</a> + */ +final class NimbusJwsEncoder { + + private static final String ENCODING_ERROR_MESSAGE_TEMPLATE = "An error occurred while attempting to encode the Jwt: %s"; + + private static final JWSSignerFactory JWS_SIGNER_FACTORY = new DefaultJWSSignerFactory(); + + private final Map<JWK, JWSSigner> jwsSigners = new ConcurrentHashMap<>(); + + private final JWKSource<SecurityContext> jwkSource; + + /** + * Constructs a {@code NimbusJwsEncoder} using the provided parameters. + * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource} + */ + NimbusJwsEncoder(JWKSource<SecurityContext> jwkSource) { + Assert.notNull(jwkSource, "jwkSource cannot be null"); + this.jwkSource = jwkSource; + } + + Jwt encode(JoseHeader headers, JwtClaimsSet claims) throws JwtEncodingException { + Assert.notNull(headers, "headers cannot be null"); + Assert.notNull(claims, "claims cannot be null"); + + JWK jwk = selectJwk(headers); + headers = addKeyIdentifierHeadersIfNecessary(headers, jwk); + + String jws = serialize(headers, claims, jwk); + + return new Jwt(jws, claims.getIssuedAt(), claims.getExpiresAt(), headers.getHeaders(), claims.getClaims()); + } + + private JWK selectJwk(JoseHeader headers) { + List<JWK> jwks; + try { + JWKSelector jwkSelector = new JWKSelector(createJwkMatcher(headers)); + jwks = this.jwkSource.get(jwkSelector, null); + } + catch (Exception ex) { + throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, + "Failed to select a JWK signing key -> " + ex.getMessage()), ex); + } + + if (jwks.size() > 1) { + throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, + "Found multiple JWK signing keys for algorithm '" + headers.getAlgorithm().getName() + "'")); + } + + if (jwks.isEmpty()) { + throw new JwtEncodingException( + String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to select a JWK signing key")); + } + + return jwks.get(0); + } + + private String serialize(JoseHeader headers, JwtClaimsSet claims, JWK jwk) { + JWSHeader jwsHeader = convert(headers); + JWTClaimsSet jwtClaimsSet = convert(claims); + + JWSSigner jwsSigner = this.jwsSigners.computeIfAbsent(jwk, NimbusJwsEncoder::createSigner); + + SignedJWT signedJwt = new SignedJWT(jwsHeader, jwtClaimsSet); + try { + signedJwt.sign(jwsSigner); + } + catch (JOSEException ex) { + throw new JwtEncodingException( + String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to sign the JWT -> " + ex.getMessage()), ex); + } + return signedJwt.serialize(); + } + + private static JWKMatcher createJwkMatcher(JoseHeader headers) { + JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(headers.getAlgorithm().getName()); + + if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm) || JWSAlgorithm.Family.EC.contains(jwsAlgorithm)) { + // @formatter:off + return new JWKMatcher.Builder() + .keyType(KeyType.forAlgorithm(jwsAlgorithm)) + .keyID(headers.getKeyId()) + .keyUses(KeyUse.SIGNATURE, null) + .algorithms(jwsAlgorithm, null) + .x509CertSHA256Thumbprint(Base64URL.from(headers.getX509SHA256Thumbprint())) + .build(); + // @formatter:on + } + else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) { + // @formatter:off + return new JWKMatcher.Builder() + .keyType(KeyType.forAlgorithm(jwsAlgorithm)) + .keyID(headers.getKeyId()) + .privateOnly(true) + .algorithms(jwsAlgorithm, null) + .build(); + // @formatter:on + } + + return null; + } + + private static JoseHeader addKeyIdentifierHeadersIfNecessary(JoseHeader headers, JWK jwk) { + // Check if headers have already been added + if (StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(headers.getX509SHA256Thumbprint())) { + return headers; + } + // Check if headers can be added from JWK + if (!StringUtils.hasText(jwk.getKeyID()) && jwk.getX509CertSHA256Thumbprint() == null) { + return headers; + } + + JoseHeader.Builder headersBuilder = JoseHeader.from(headers); + if (!StringUtils.hasText(headers.getKeyId()) && StringUtils.hasText(jwk.getKeyID())) { + headersBuilder.keyId(jwk.getKeyID()); + } + if (!StringUtils.hasText(headers.getX509SHA256Thumbprint()) && jwk.getX509CertSHA256Thumbprint() != null) { + headersBuilder.x509SHA256Thumbprint(jwk.getX509CertSHA256Thumbprint().toString()); + } + + return headersBuilder.build(); + } + + private static JWSSigner createSigner(JWK jwk) { + try { + return JWS_SIGNER_FACTORY.createJWSSigner(jwk); + } + catch (JOSEException ex) { + throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, + "Failed to create a JWS Signer -> " + ex.getMessage()), ex); + } + } + + private static JWSHeader convert(JoseHeader headers) { + JWSHeader.Builder builder = new JWSHeader.Builder(JWSAlgorithm.parse(headers.getAlgorithm().getName())); + + if (headers.getJwkSetUrl() != null) { + builder.jwkURL(convertAsURI(JoseHeaderNames.JKU, headers.getJwkSetUrl())); + } + + Map<String, Object> jwk = headers.getJwk(); + if (!CollectionUtils.isEmpty(jwk)) { + try { + builder.jwk(JWK.parse(jwk)); + } + catch (Exception ex) { + throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, + "Unable to convert '" + JoseHeaderNames.JWK + "' JOSE header"), ex); + } + } + + String keyId = headers.getKeyId(); + if (StringUtils.hasText(keyId)) { + builder.keyID(keyId); + } + + if (headers.getX509Url() != null) { + builder.x509CertURL(convertAsURI(JoseHeaderNames.X5U, headers.getX509Url())); + } + + List<String> x509CertificateChain = headers.getX509CertificateChain(); + if (!CollectionUtils.isEmpty(x509CertificateChain)) { + List<Base64> x5cList = new ArrayList<>(); + x509CertificateChain.forEach((x5c) -> x5cList.add(new Base64(x5c))); + if (!x5cList.isEmpty()) { + builder.x509CertChain(x5cList); + } + } + + String x509SHA1Thumbprint = headers.getX509SHA1Thumbprint(); + if (StringUtils.hasText(x509SHA1Thumbprint)) { + builder.x509CertThumbprint(new Base64URL(x509SHA1Thumbprint)); + } + + String x509SHA256Thumbprint = headers.getX509SHA256Thumbprint(); + if (StringUtils.hasText(x509SHA256Thumbprint)) { + builder.x509CertSHA256Thumbprint(new Base64URL(x509SHA256Thumbprint)); + } + + String type = headers.getType(); + if (StringUtils.hasText(type)) { + builder.type(new JOSEObjectType(type)); + } + + String contentType = headers.getContentType(); + if (StringUtils.hasText(contentType)) { + builder.contentType(contentType); + } + + Set<String> critical = headers.getCritical(); + if (!CollectionUtils.isEmpty(critical)) { + builder.criticalParams(critical); + } + + Map<String, Object> customHeaders = new HashMap<>(); + headers.getHeaders().forEach((name, value) -> { + if (!JWSHeader.getRegisteredParameterNames().contains(name)) { + customHeaders.put(name, value); + } + }); + if (!customHeaders.isEmpty()) { + builder.customParams(customHeaders); + } + + return builder.build(); + } + + private static JWTClaimsSet convert(JwtClaimsSet claims) { + JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(); + + // NOTE: The value of the 'iss' claim is a String or URL (StringOrURI). + Object issuer = claims.getClaim(JwtClaimNames.ISS); + if (issuer != null) { + builder.issuer(issuer.toString()); + } + + String subject = claims.getSubject(); + if (StringUtils.hasText(subject)) { + builder.subject(subject); + } + + List<String> audience = claims.getAudience(); + if (!CollectionUtils.isEmpty(audience)) { + builder.audience(audience); + } + + Instant expiresAt = claims.getExpiresAt(); + if (expiresAt != null) { + builder.expirationTime(Date.from(expiresAt)); + } + + Instant notBefore = claims.getNotBefore(); + if (notBefore != null) { + builder.notBeforeTime(Date.from(notBefore)); + } + + Instant issuedAt = claims.getIssuedAt(); + if (issuedAt != null) { + builder.issueTime(Date.from(issuedAt)); + } + + String jwtId = claims.getId(); + if (StringUtils.hasText(jwtId)) { + builder.jwtID(jwtId); + } + + Map<String, Object> customClaims = new HashMap<>(); + claims.getClaims().forEach((name, value) -> { + if (!JWTClaimsSet.getRegisteredNames().contains(name)) { + customClaims.put(name, value); + } + }); + if (!customClaims.isEmpty()) { + customClaims.forEach(builder::claim); + } + + return builder.build(); + } + + private static URI convertAsURI(String header, URL url) { + try { + return url.toURI(); + } + catch (Exception ex) { + throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, + "Unable to convert '" + header + "' JOSE header to a URI"), ex); + } + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverter.java new file mode 100644 index 00000000000..b7c80f17b51 --- /dev/null +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverter.java @@ -0,0 +1,183 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.time.Duration; +import java.time.Instant; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.source.ImmutableJWKSet; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.security.oauth2.core.OAuth2AuthorizationException; +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.jose.jws.JwsAlgorithm; +import org.springframework.security.oauth2.jose.jws.MacAlgorithm; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** + * A {@link Converter} that customizes the OAuth 2.0 Access Token Request parameters by + * adding a signed JSON Web Token (JWS) to be used for client authentication at the + * Authorization Server's Token Endpoint. The private/secret key used for signing the JWS + * is supplied by the {@code com.nimbusds.jose.jwk.JWK} resolver provided via the + * constructor. + * + * <p> + * <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK. + * + * @param <T> the type of {@link AbstractOAuth2AuthorizationGrantRequest} + * @author Joe Grandja + * @since 5.5 + * @see Converter + * @see com.nimbusds.jose.jwk.JWK + * @see OAuth2AuthorizationCodeGrantRequestEntityConverter#addParametersConverter(Converter) + * @see OAuth2ClientCredentialsGrantRequestEntityConverter#addParametersConverter(Converter) + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7523#section-2.2">2.2 + * Using JWTs for Client Authentication</a> + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7521#section-4.2">4.2 + * Using Assertions for Client Authentication</a> + * @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus + * JOSE + JWT SDK</a> + */ +public final class NimbusJwtClientAuthenticationParametersConverter<T extends AbstractOAuth2AuthorizationGrantRequest> + implements Converter<T, MultiValueMap<String, String>> { + + private static final String INVALID_KEY_ERROR_CODE = "invalid_key"; + + private static final String INVALID_ALGORITHM_ERROR_CODE = "invalid_algorithm"; + + private static final String CLIENT_ASSERTION_TYPE_VALUE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + + private final Function<ClientRegistration, JWK> jwkResolver; + + private final Map<String, NimbusJwsEncoder> jwsEncoders = new ConcurrentHashMap<>(); + + /** + * Constructs a {@code NimbusJwtClientAuthenticationParametersConverter} using the + * provided parameters. + * @param jwkResolver the resolver that provides the {@code com.nimbusds.jose.jwk.JWK} + * associated to the {@link ClientRegistration client} + */ + public NimbusJwtClientAuthenticationParametersConverter(Function<ClientRegistration, JWK> jwkResolver) { + Assert.notNull(jwkResolver, "jwkResolver cannot be null"); + this.jwkResolver = jwkResolver; + } + + @Override + public MultiValueMap<String, String> convert(T authorizationGrantRequest) { + Assert.notNull(authorizationGrantRequest, "authorizationGrantRequest cannot be null"); + + ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration(); + if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.equals(clientRegistration.getClientAuthenticationMethod()) + && !ClientAuthenticationMethod.CLIENT_SECRET_JWT + .equals(clientRegistration.getClientAuthenticationMethod())) { + return null; + } + + JWK jwk = this.jwkResolver.apply(clientRegistration); + if (jwk == null) { + OAuth2Error oauth2Error = new OAuth2Error(INVALID_KEY_ERROR_CODE, + "Failed to resolve JWK signing key for client registration '" + + clientRegistration.getRegistrationId() + "'.", + null); + throw new OAuth2AuthorizationException(oauth2Error); + } + + JwsAlgorithm jwsAlgorithm = resolveAlgorithm(jwk); + if (jwsAlgorithm == null) { + OAuth2Error oauth2Error = new OAuth2Error(INVALID_ALGORITHM_ERROR_CODE, + "Unable to resolve JWS (signing) algorithm from JWK associated to client registration '" + + clientRegistration.getRegistrationId() + "'.", + null); + throw new OAuth2AuthorizationException(oauth2Error); + } + + JoseHeader.Builder headersBuilder = JoseHeader.withAlgorithm(jwsAlgorithm); + + Instant issuedAt = Instant.now(); + Instant expiresAt = issuedAt.plus(Duration.ofSeconds(60)); + + // @formatter:off + JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder() + .issuer(clientRegistration.getClientId()) + .subject(clientRegistration.getClientId()) + .audience(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri())) + .id(UUID.randomUUID().toString()) + .issuedAt(issuedAt) + .expiresAt(expiresAt); + // @formatter:on + + JoseHeader joseHeader = headersBuilder.build(); + JwtClaimsSet jwtClaimsSet = claimsBuilder.build(); + + NimbusJwsEncoder jwsEncoder = this.jwsEncoders.computeIfAbsent(clientRegistration.getRegistrationId(), + (clientRegistrationId) -> { + JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk)); + return new NimbusJwsEncoder(jwkSource); + }); + + Jwt jws = jwsEncoder.encode(joseHeader, jwtClaimsSet); + + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + parameters.set(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, CLIENT_ASSERTION_TYPE_VALUE); + parameters.set(OAuth2ParameterNames.CLIENT_ASSERTION, jws.getTokenValue()); + + return parameters; + } + + private static JwsAlgorithm resolveAlgorithm(JWK jwk) { + JwsAlgorithm jwsAlgorithm = null; + + if (jwk.getAlgorithm() != null) { + jwsAlgorithm = SignatureAlgorithm.from(jwk.getAlgorithm().getName()); + if (jwsAlgorithm == null) { + jwsAlgorithm = MacAlgorithm.from(jwk.getAlgorithm().getName()); + } + } + + if (jwsAlgorithm == null) { + if (KeyType.RSA.equals(jwk.getKeyType())) { + jwsAlgorithm = SignatureAlgorithm.RS256; + } + else if (KeyType.EC.equals(jwk.getKeyType())) { + jwsAlgorithm = SignatureAlgorithm.ES256; + } + else if (KeyType.OCT.equals(jwk.getKeyType())) { + jwsAlgorithm = MacAlgorithm.HS256; + } + } + + return jwsAlgorithm; + } + +} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequest.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequest.java index feae3d1f378..698ebeec212 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequest.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,8 +37,6 @@ */ public class OAuth2AuthorizationCodeGrantRequest extends AbstractOAuth2AuthorizationGrantRequest { - private final ClientRegistration clientRegistration; - private final OAuth2AuthorizationExchange authorizationExchange; /** @@ -49,21 +47,11 @@ public class OAuth2AuthorizationCodeGrantRequest extends AbstractOAuth2Authoriza */ public OAuth2AuthorizationCodeGrantRequest(ClientRegistration clientRegistration, OAuth2AuthorizationExchange authorizationExchange) { - super(AuthorizationGrantType.AUTHORIZATION_CODE); - Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + super(AuthorizationGrantType.AUTHORIZATION_CODE, clientRegistration); Assert.notNull(authorizationExchange, "authorizationExchange cannot be null"); - this.clientRegistration = clientRegistration; this.authorizationExchange = authorizationExchange; } - /** - * Returns the {@link ClientRegistration client registration}. - * @return the {@link ClientRegistration} - */ - public ClientRegistration getClientRegistration() { - return this.clientRegistration; - } - /** * Returns the {@link OAuth2AuthorizationExchange authorization exchange}. * @return the {@link OAuth2AuthorizationExchange} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverter.java index 5286efade03..364a4ac591e 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,6 @@ package org.springframework.security.oauth2.client.endpoint; -import java.net.URI; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @@ -29,69 +24,48 @@ import org.springframework.security.oauth2.core.endpoint.PkceParameterNames; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponentsBuilder; /** - * A {@link Converter} that converts the provided - * {@link OAuth2AuthorizationCodeGrantRequest} to a {@link RequestEntity} representation - * of an OAuth 2.0 Access Token Request for the Authorization Code Grant. + * An implementation of an {@link AbstractOAuth2AuthorizationGrantRequestEntityConverter} + * that converts the provided {@link OAuth2AuthorizationCodeGrantRequest} to a + * {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the + * Authorization Code Grant. * * @author Joe Grandja * @since 5.1 - * @see Converter + * @see AbstractOAuth2AuthorizationGrantRequestEntityConverter * @see OAuth2AuthorizationCodeGrantRequest * @see RequestEntity */ public class OAuth2AuthorizationCodeGrantRequestEntityConverter - implements Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>> { + extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<OAuth2AuthorizationCodeGrantRequest> { - /** - * Returns the {@link RequestEntity} used for the Access Token Request. - * @param authorizationCodeGrantRequest the authorization code grant request - * @return the {@link RequestEntity} used for the Access Token Request - */ @Override - public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) { - ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration(); - HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration); - MultiValueMap<String, String> formParameters = this.buildFormParameters(authorizationCodeGrantRequest); - URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build() - .toUri(); - return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri); - } - - /** - * Returns a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body. - * @param authorizationCodeGrantRequest the authorization code grant request - * @return a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body - */ - private MultiValueMap<String, String> buildFormParameters( + protected MultiValueMap<String, String> createParameters( OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) { ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration(); OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange(); - MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); - formParameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue()); - formParameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode()); + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + parameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue()); + parameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode()); String redirectUri = authorizationExchange.getAuthorizationRequest().getRedirectUri(); String codeVerifier = authorizationExchange.getAuthorizationRequest() .getAttribute(PkceParameterNames.CODE_VERIFIER); if (redirectUri != null) { - formParameters.add(OAuth2ParameterNames.REDIRECT_URI, redirectUri); + parameters.add(OAuth2ParameterNames.REDIRECT_URI, redirectUri); } if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientRegistration.getClientAuthenticationMethod()) && !ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) { - formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); + parameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); } if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod()) || ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { - formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); + parameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); } if (codeVerifier != null) { - formParameters.add(PkceParameterNames.CODE_VERIFIER, codeVerifier); + parameters.add(PkceParameterNames.CODE_VERIFIER, codeVerifier); } - return formParameters; + return parameters; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequest.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequest.java index f91868a0fe5..b1ab0f1f3b3 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequest.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,27 +34,15 @@ */ public class OAuth2ClientCredentialsGrantRequest extends AbstractOAuth2AuthorizationGrantRequest { - private final ClientRegistration clientRegistration; - /** * Constructs an {@code OAuth2ClientCredentialsGrantRequest} using the provided * parameters. * @param clientRegistration the client registration */ public OAuth2ClientCredentialsGrantRequest(ClientRegistration clientRegistration) { - super(AuthorizationGrantType.CLIENT_CREDENTIALS); - Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientRegistration); Assert.isTrue(AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()), "clientRegistration.authorizationGrantType must be AuthorizationGrantType.CLIENT_CREDENTIALS"); - this.clientRegistration = clientRegistration; - } - - /** - * Returns the {@link ClientRegistration client registration}. - * @return the {@link ClientRegistration} - */ - public ClientRegistration getClientRegistration() { - return this.clientRegistration; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverter.java index 73eef647219..14b51138dfd 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,6 @@ package org.springframework.security.oauth2.client.endpoint; -import java.net.URI; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @@ -29,59 +24,38 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponentsBuilder; /** - * A {@link Converter} that converts the provided - * {@link OAuth2ClientCredentialsGrantRequest} to a {@link RequestEntity} representation - * of an OAuth 2.0 Access Token Request for the Client Credentials Grant. + * An implementation of an {@link AbstractOAuth2AuthorizationGrantRequestEntityConverter} + * that converts the provided {@link OAuth2ClientCredentialsGrantRequest} to a + * {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the + * Client Credentials Grant. * * @author Joe Grandja * @since 5.1 - * @see Converter + * @see AbstractOAuth2AuthorizationGrantRequestEntityConverter * @see OAuth2ClientCredentialsGrantRequest * @see RequestEntity */ public class OAuth2ClientCredentialsGrantRequestEntityConverter - implements Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> { + extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<OAuth2ClientCredentialsGrantRequest> { - /** - * Returns the {@link RequestEntity} used for the Access Token Request. - * @param clientCredentialsGrantRequest the client credentials grant request - * @return the {@link RequestEntity} used for the Access Token Request - */ @Override - public RequestEntity<?> convert(OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) { - ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration(); - HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration); - MultiValueMap<String, String> formParameters = this.buildFormParameters(clientCredentialsGrantRequest); - URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build() - .toUri(); - return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri); - } - - /** - * Returns a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body. - * @param clientCredentialsGrantRequest the client credentials grant request - * @return a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body - */ - private MultiValueMap<String, String> buildFormParameters( + protected MultiValueMap<String, String> createParameters( OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest) { ClientRegistration clientRegistration = clientCredentialsGrantRequest.getClientRegistration(); - MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); - formParameters.add(OAuth2ParameterNames.GRANT_TYPE, clientCredentialsGrantRequest.getGrantType().getValue()); + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + parameters.add(OAuth2ParameterNames.GRANT_TYPE, clientCredentialsGrantRequest.getGrantType().getValue()); if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) { - formParameters.add(OAuth2ParameterNames.SCOPE, + parameters.add(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), " ")); } if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod()) || ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { - formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); - formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); + parameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); + parameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); } - return formParameters; + return parameters; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequest.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequest.java index cc82b3f47fb..7cddb4c2e12 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequest.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,6 @@ */ public class OAuth2PasswordGrantRequest extends AbstractOAuth2AuthorizationGrantRequest { - private final ClientRegistration clientRegistration; - private final String username; private final String password; @@ -46,25 +44,15 @@ public class OAuth2PasswordGrantRequest extends AbstractOAuth2AuthorizationGrant * @param password the resource owner's password */ public OAuth2PasswordGrantRequest(ClientRegistration clientRegistration, String username, String password) { - super(AuthorizationGrantType.PASSWORD); - Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + super(AuthorizationGrantType.PASSWORD, clientRegistration); Assert.isTrue(AuthorizationGrantType.PASSWORD.equals(clientRegistration.getAuthorizationGrantType()), "clientRegistration.authorizationGrantType must be AuthorizationGrantType.PASSWORD"); Assert.hasText(username, "username cannot be empty"); Assert.hasText(password, "password cannot be empty"); - this.clientRegistration = clientRegistration; this.username = username; this.password = password; } - /** - * Returns the {@link ClientRegistration client registration}. - * @return the {@link ClientRegistration} - */ - public ClientRegistration getClientRegistration() { - return this.clientRegistration; - } - /** * Returns the resource owner's username. * @return the resource owner's username diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverter.java index b0f2e8691ca..34dc96479be 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,6 @@ package org.springframework.security.oauth2.client.endpoint; -import java.net.URI; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @@ -29,60 +24,39 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponentsBuilder; /** - * A {@link Converter} that converts the provided {@link OAuth2PasswordGrantRequest} to a + * An implementation of an {@link AbstractOAuth2AuthorizationGrantRequestEntityConverter} + * that converts the provided {@link OAuth2PasswordGrantRequest} to a * {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the * Resource Owner Password Credentials Grant. * * @author Joe Grandja * @since 5.2 - * @see Converter + * @see AbstractOAuth2AuthorizationGrantRequestEntityConverter * @see OAuth2PasswordGrantRequest * @see RequestEntity */ public class OAuth2PasswordGrantRequestEntityConverter - implements Converter<OAuth2PasswordGrantRequest, RequestEntity<?>> { + extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<OAuth2PasswordGrantRequest> { - /** - * Returns the {@link RequestEntity} used for the Access Token Request. - * @param passwordGrantRequest the password grant request - * @return the {@link RequestEntity} used for the Access Token Request - */ @Override - public RequestEntity<?> convert(OAuth2PasswordGrantRequest passwordGrantRequest) { - ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration(); - HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration); - MultiValueMap<String, String> formParameters = buildFormParameters(passwordGrantRequest); - URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build() - .toUri(); - return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri); - } - - /** - * Returns a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body. - * @param passwordGrantRequest the password grant request - * @return a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body - */ - private MultiValueMap<String, String> buildFormParameters(OAuth2PasswordGrantRequest passwordGrantRequest) { + protected MultiValueMap<String, String> createParameters(OAuth2PasswordGrantRequest passwordGrantRequest) { ClientRegistration clientRegistration = passwordGrantRequest.getClientRegistration(); - MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); - formParameters.add(OAuth2ParameterNames.GRANT_TYPE, passwordGrantRequest.getGrantType().getValue()); - formParameters.add(OAuth2ParameterNames.USERNAME, passwordGrantRequest.getUsername()); - formParameters.add(OAuth2ParameterNames.PASSWORD, passwordGrantRequest.getPassword()); + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + parameters.add(OAuth2ParameterNames.GRANT_TYPE, passwordGrantRequest.getGrantType().getValue()); + parameters.add(OAuth2ParameterNames.USERNAME, passwordGrantRequest.getUsername()); + parameters.add(OAuth2ParameterNames.PASSWORD, passwordGrantRequest.getPassword()); if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) { - formParameters.add(OAuth2ParameterNames.SCOPE, + parameters.add(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), " ")); } if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod()) || ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { - formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); - formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); + parameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); + parameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); } - return formParameters; + return parameters; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequest.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequest.java index 5bb5d127586..d57fad0e97c 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequest.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,8 +39,6 @@ */ public class OAuth2RefreshTokenGrantRequest extends AbstractOAuth2AuthorizationGrantRequest { - private final ClientRegistration clientRegistration; - private final OAuth2AccessToken accessToken; private final OAuth2RefreshToken refreshToken; @@ -67,25 +65,15 @@ public OAuth2RefreshTokenGrantRequest(ClientRegistration clientRegistration, OAu */ public OAuth2RefreshTokenGrantRequest(ClientRegistration clientRegistration, OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken, Set<String> scopes) { - super(AuthorizationGrantType.REFRESH_TOKEN); - Assert.notNull(clientRegistration, "clientRegistration cannot be null"); + super(AuthorizationGrantType.REFRESH_TOKEN, clientRegistration); Assert.notNull(accessToken, "accessToken cannot be null"); Assert.notNull(refreshToken, "refreshToken cannot be null"); - this.clientRegistration = clientRegistration; this.accessToken = accessToken; this.refreshToken = refreshToken; this.scopes = Collections .unmodifiableSet((scopes != null) ? new LinkedHashSet<>(scopes) : Collections.emptySet()); } - /** - * Returns the authorized client's {@link ClientRegistration registration}. - * @return the {@link ClientRegistration} - */ - public ClientRegistration getClientRegistration() { - return this.clientRegistration; - } - /** * Returns the {@link OAuth2AccessToken access token} credential granted. * @return the {@link OAuth2AccessToken} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverter.java index 683fadd4e64..e98652ae544 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,6 @@ package org.springframework.security.oauth2.client.endpoint; -import java.net.URI; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; @@ -29,60 +24,38 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponentsBuilder; /** - * A {@link Converter} that converts the provided {@link OAuth2RefreshTokenGrantRequest} - * to a {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the + * An implementation of an {@link AbstractOAuth2AuthorizationGrantRequestEntityConverter} + * that converts the provided {@link OAuth2RefreshTokenGrantRequest} to a + * {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the * Refresh Token Grant. * * @author Joe Grandja * @since 5.2 - * @see Converter + * @see AbstractOAuth2AuthorizationGrantRequestEntityConverter * @see OAuth2RefreshTokenGrantRequest * @see RequestEntity */ public class OAuth2RefreshTokenGrantRequestEntityConverter - implements Converter<OAuth2RefreshTokenGrantRequest, RequestEntity<?>> { + extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<OAuth2RefreshTokenGrantRequest> { - /** - * Returns the {@link RequestEntity} used for the Access Token Request. - * @param refreshTokenGrantRequest the refresh token grant request - * @return the {@link RequestEntity} used for the Access Token Request - */ @Override - public RequestEntity<?> convert(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) { - ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration(); - HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration); - MultiValueMap<String, String> formParameters = buildFormParameters(refreshTokenGrantRequest); - URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build() - .toUri(); - return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri); - } - - /** - * Returns a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body. - * @param refreshTokenGrantRequest the refresh token grant request - * @return a {@link MultiValueMap} of the form parameters used for the Access Token - * Request body - */ - private MultiValueMap<String, String> buildFormParameters(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) { + protected MultiValueMap<String, String> createParameters(OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest) { ClientRegistration clientRegistration = refreshTokenGrantRequest.getClientRegistration(); - MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); - formParameters.add(OAuth2ParameterNames.GRANT_TYPE, refreshTokenGrantRequest.getGrantType().getValue()); - formParameters.add(OAuth2ParameterNames.REFRESH_TOKEN, - refreshTokenGrantRequest.getRefreshToken().getTokenValue()); + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + parameters.add(OAuth2ParameterNames.GRANT_TYPE, refreshTokenGrantRequest.getGrantType().getValue()); + parameters.add(OAuth2ParameterNames.REFRESH_TOKEN, refreshTokenGrantRequest.getRefreshToken().getTokenValue()); if (!CollectionUtils.isEmpty(refreshTokenGrantRequest.getScopes())) { - formParameters.add(OAuth2ParameterNames.SCOPE, + parameters.add(OAuth2ParameterNames.SCOPE, StringUtils.collectionToDelimitedString(refreshTokenGrantRequest.getScopes(), " ")); } if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod()) || ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { - formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); - formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); + parameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); + parameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); } - return formParameters; + return parameters; } } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java index 8d9cf1d92c6..4a19852893c 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,7 +108,7 @@ public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2Aut authenticationMethod); // @formatter:off Mono<Map<String, Object>> userAttributes = requestHeadersSpec.retrieve() - .onStatus((s) -> s != HttpStatus.OK, (response) -> + .onStatus(HttpStatus::isError, (response) -> parse(response) .map((userInfoErrorResponse) -> { String description = userInfoErrorResponse.getErrorObject().getDescription(); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/PasswordOAuth2AuthorizedClientProviderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/PasswordOAuth2AuthorizedClientProviderTests.java index 10d375e4b6c..ad750933101 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/PasswordOAuth2AuthorizedClientProviderTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/PasswordOAuth2AuthorizedClientProviderTests.java @@ -212,7 +212,7 @@ public void authorizeWhenPasswordAndAuthorizedWithRefreshTokenAndTokenExpiredThe public void authorizeWhenPasswordAndAuthorizedAndTokenNotExpiredButClockSkewForcesExpiryThenReauthorize() { Instant now = Instant.now(); Instant issuedAt = now.minus(Duration.ofMinutes(60)); - Instant expiresAt = now.minus(Duration.ofMinutes(1)); + Instant expiresAt = now.plus(Duration.ofMinutes(1)); OAuth2AccessToken expiresInOneMinAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token-1234", issuedAt, expiresAt); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration, diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientServiceTests.java index 41c5e9a0c56..a38af89aaf6 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/R2dbcReactiveOAuth2AuthorizedClientServiceTests.java @@ -16,6 +16,11 @@ package org.springframework.security.oauth2.client; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; + import io.r2dbc.h2.H2ConnectionFactory; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.Result; @@ -38,8 +43,6 @@ import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; -import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; -import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -364,16 +367,19 @@ private static OAuth2AuthorizedClient createAuthorizedClient(Authentication prin private static OAuth2AuthorizedClient createAuthorizedClient(Authentication principal, ClientRegistration clientRegistration, boolean requiredAttributesOnly) { + Instant issuedAt = Instant.ofEpochSecond(1234567890, 123456000); OAuth2AccessToken accessToken; if (!requiredAttributesOnly) { - accessToken = TestOAuth2AccessTokens.scopes("read", "write"); + accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "scopes", issuedAt, + issuedAt.plus(Duration.ofDays(1)), new HashSet<>(Arrays.asList("read", "write"))); } else { - accessToken = TestOAuth2AccessTokens.noScopes(); + accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "no-scopes", issuedAt, + issuedAt.plus(Duration.ofDays(1))); } OAuth2RefreshToken refreshToken = null; if (!requiredAttributesOnly) { - refreshToken = TestOAuth2RefreshTokens.refreshToken(); + refreshToken = new OAuth2RefreshToken("refresh-token", issuedAt); } return new OAuth2AuthorizedClient(clientRegistration, principal.getName(), accessToken, refreshToken); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java index 1864dd6f1bf..99dc69b029e 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,13 @@ package org.springframework.security.oauth2.client.endpoint; +import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.function.Function; +import javax.crypto.spec.SecretKeySpec; + +import com.nimbusds.jose.jwk.JWK; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -29,7 +34,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; @@ -37,6 +42,8 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.TestKeys; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -49,32 +56,25 @@ */ public class DefaultAuthorizationCodeTokenResponseClientTests { - private DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient(); + private DefaultAuthorizationCodeTokenResponseClient tokenResponseClient; - private ClientRegistration clientRegistration; + private ClientRegistration.Builder clientRegistration; private MockWebServer server; @Before public void setup() throws Exception { + this.tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient(); this.server = new MockWebServer(); this.server.start(); String tokenUri = this.server.url("/oauth2/token").toString(); // @formatter:off - this.clientRegistration = ClientRegistration - .withRegistrationId("registration-1") + this.clientRegistration = TestClientRegistrations.clientRegistration() .clientId("client-1") .clientSecret("secret") - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("https://client.com/callback/client-1") - .scope("read", "write") - .authorizationUri("https://provider.com/oauth2/authorize") .tokenUri(tokenUri) - .userInfoUri("https://provider.com/user") - .userNameAttributeName("id") - .clientName("client-1") - .build(); + .scope("read", "write"); // @formatter:on } @@ -114,7 +114,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); Instant expiresAtBefore = Instant.now().plusSeconds(3600); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient - .getTokenResponse(this.authorizationCodeGrantRequest()); + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build())); Instant expiresAtAfter = Instant.now().plusSeconds(3600); RecordedRequest recordedRequest = this.server.takeRequest(); assertThat(recordedRequest.getMethod()).isEqualTo(HttpMethod.POST.toString()); @@ -136,7 +136,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t } @Test - public void getTokenResponseWhenClientAuthenticationBasicThenAuthorizationHeaderIsSent() throws Exception { + public void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception { // @formatter:off String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" @@ -145,13 +145,13 @@ public void getTokenResponseWhenClientAuthenticationBasicThenAuthorizationHeader + "}\n"; // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); - this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest()); + this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build())); RecordedRequest recordedRequest = this.server.takeRequest(); assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith("Basic "); } @Test - public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSent() throws Exception { + public void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception { // @formatter:off String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" @@ -160,9 +160,9 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen + "}\n"; // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); - ClientRegistration clientRegistration = this.from(this.clientRegistration) + ClientRegistration clientRegistration = this.clientRegistration .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).build(); - this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest(clientRegistration)); + this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration)); RecordedRequest recordedRequest = this.server.takeRequest(); assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); String formParameters = recordedRequest.getBody().readUtf8(); @@ -170,6 +170,79 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen assertThat(formParameters).contains("client_secret=secret"); } + @Test + public void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + SecretKeySpec secretKey = new SecretKeySpec( + clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + JWK jwk = TestJwks.jwk(secretKey).build(); + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration)); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + @Test + public void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + JWK jwk = TestJwks.DEFAULT_RSA_JWK; + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration)); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + private void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) { + NimbusJwtClientAuthenticationParametersConverter<OAuth2AuthorizationCodeGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>( + jwkResolver); + OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter(); + requestEntityConverter.addParametersConverter(jwtClientAuthenticationConverter); + this.tokenResponseClient.setRequestEntityConverter(requestEntityConverter); + } + @Test public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() { // @formatter:off @@ -181,7 +254,8 @@ public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAu // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); assertThatExceptionOfType(OAuth2AuthorizationException.class) - .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest())) + .isThrownBy(() -> this.tokenResponseClient + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build()))) .withMessageContaining( "[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response") .withMessageContaining("tokenType cannot be null"); @@ -196,7 +270,8 @@ public void getTokenResponseWhenSuccessResponseAndMissingTokenTypeParameterThenT // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); assertThatExceptionOfType(OAuth2AuthorizationException.class) - .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest())) + .isThrownBy(() -> this.tokenResponseClient + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build()))) .withMessageContaining( "[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response") .withMessageContaining("tokenType cannot be null"); @@ -215,7 +290,7 @@ public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasRe // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient - .getTokenResponse(this.authorizationCodeGrantRequest()); + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build())); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read"); } @@ -231,16 +306,16 @@ public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessToke // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient - .getTokenResponse(this.authorizationCodeGrantRequest()); + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build())); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); } @Test public void getTokenResponseWhenTokenUriInvalidThenThrowOAuth2AuthorizationException() { String invalidTokenUri = "https://invalid-provider.com/oauth2/token"; - ClientRegistration clientRegistration = this.from(this.clientRegistration).tokenUri(invalidTokenUri).build(); + ClientRegistration clientRegistration = this.clientRegistration.tokenUri(invalidTokenUri).build(); assertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy( - () -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest(clientRegistration))) + () -> this.tokenResponseClient.getTokenResponse(authorizationCodeGrantRequest(clientRegistration))) .withMessageContaining( "[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response"); } @@ -260,7 +335,8 @@ public void getTokenResponseWhenMalformedResponseThenThrowOAuth2AuthorizationExc // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); assertThatExceptionOfType(OAuth2AuthorizationException.class) - .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest())) + .isThrownBy(() -> this.tokenResponseClient + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build()))) .withMessageContaining( "[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response"); } @@ -270,7 +346,8 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti String accessTokenErrorResponse = "{\n" + " \"error\": \"unauthorized_client\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenErrorResponse).setResponseCode(400)); assertThatExceptionOfType(OAuth2AuthorizationException.class) - .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest())) + .isThrownBy(() -> this.tokenResponseClient + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build()))) .withMessageContaining("[unauthorized_client]"); } @@ -278,15 +355,12 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti public void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() { this.server.enqueue(new MockResponse().setResponseCode(500)); assertThatExceptionOfType(OAuth2AuthorizationException.class) - .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(this.authorizationCodeGrantRequest())) + .isThrownBy(() -> this.tokenResponseClient + .getTokenResponse(authorizationCodeGrantRequest(this.clientRegistration.build()))) .withMessageContaining("[invalid_token_response] An error occurred while attempting to retrieve " + "the OAuth 2.0 Access Token Response"); } - private OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest() { - return this.authorizationCodeGrantRequest(this.clientRegistration); - } - private OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest(ClientRegistration clientRegistration) { OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() .clientId(clientRegistration.getClientId()).state("state-1234") @@ -303,22 +377,4 @@ private MockResponse jsonResponse(String json) { return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json); } - private ClientRegistration.Builder from(ClientRegistration registration) { - // @formatter:off - return ClientRegistration.withRegistrationId(registration.getRegistrationId()) - .clientId(registration.getClientId()) - .clientSecret(registration.getClientSecret()) - .clientAuthenticationMethod(registration.getClientAuthenticationMethod()) - .authorizationGrantType(registration.getAuthorizationGrantType()) - .redirectUri(registration.getRedirectUri()) - .scope(registration.getScopes()) - .authorizationUri(registration.getProviderDetails().getAuthorizationUri()) - .tokenUri(registration.getProviderDetails().getTokenUri()) - .userInfoUri(registration.getProviderDetails().getUserInfoEndpoint().getUri()) - .userNameAttributeName( - registration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()) - .clientName(registration.getClientName()); - // @formatter:on - } - } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java index fee6942fa56..c6e6492eb30 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,13 @@ package org.springframework.security.oauth2.client.endpoint; +import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.function.Function; +import javax.crypto.spec.SecretKeySpec; + +import com.nimbusds.jose.jwk.JWK; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -29,11 +34,13 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.TestKeys; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -46,26 +53,24 @@ */ public class DefaultClientCredentialsTokenResponseClientTests { - private DefaultClientCredentialsTokenResponseClient tokenResponseClient = new DefaultClientCredentialsTokenResponseClient(); + private DefaultClientCredentialsTokenResponseClient tokenResponseClient; - private ClientRegistration clientRegistration; + private ClientRegistration.Builder clientRegistration; private MockWebServer server; @Before public void setup() throws Exception { + this.tokenResponseClient = new DefaultClientCredentialsTokenResponseClient(); this.server = new MockWebServer(); this.server.start(); String tokenUri = this.server.url("/oauth2/token").toString(); // @formatter:off - this.clientRegistration = ClientRegistration.withRegistrationId("registration-1") + this.clientRegistration = TestClientRegistrations.clientCredentials() .clientId("client-1") .clientSecret("secret") - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .scope("read", "write") .tokenUri(tokenUri) - .build(); + .scope("read", "write"); // @formatter:on } @@ -110,7 +115,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); Instant expiresAtBefore = Instant.now().plusSeconds(3600); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient .getTokenResponse(clientCredentialsGrantRequest); Instant expiresAtAfter = Instant.now().plusSeconds(3600); @@ -133,7 +138,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t } @Test - public void getTokenResponseWhenClientAuthenticationBasicThenAuthorizationHeaderIsSent() throws Exception { + public void getTokenResponseWhenAuthenticationClientSecretBasicThenAuthorizationHeaderIsSent() throws Exception { // @formatter:off String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" @@ -143,14 +148,14 @@ public void getTokenResponseWhenClientAuthenticationBasicThenAuthorizationHeader // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); RecordedRequest recordedRequest = this.server.takeRequest(); assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).startsWith("Basic "); } @Test - public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSent() throws Exception { + public void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception { // @formatter:off String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" @@ -159,7 +164,7 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen + "}\n"; // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); - ClientRegistration clientRegistration = this.from(this.clientRegistration) + ClientRegistration clientRegistration = this.clientRegistration .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).build(); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( clientRegistration); @@ -171,6 +176,83 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen assertThat(formParameters).contains("client_secret=secret"); } + @Test + public void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + SecretKeySpec secretKey = new SecretKeySpec( + clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + JWK jwk = TestJwks.jwk(secretKey).build(); + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + @Test + public void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + JWK jwk = TestJwks.DEFAULT_RSA_JWK; + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + private void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) { + NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>( + jwkResolver); + OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter = new OAuth2ClientCredentialsGrantRequestEntityConverter(); + requestEntityConverter.addParametersConverter(jwtClientAuthenticationConverter); + this.tokenResponseClient.setRequestEntityConverter(requestEntityConverter); + } + @Test public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() { // @formatter:off @@ -182,7 +264,7 @@ public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAu // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest)) .withMessageContaining( @@ -195,7 +277,7 @@ public void getTokenResponseWhenSuccessResponseAndMissingTokenTypeParameterThenT String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest)) .withMessageContaining( @@ -215,7 +297,7 @@ public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasRe // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient .getTokenResponse(clientCredentialsGrantRequest); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read"); @@ -232,7 +314,7 @@ public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessToke // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient .getTokenResponse(clientCredentialsGrantRequest); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); @@ -241,7 +323,7 @@ public void getTokenResponseWhenSuccessResponseDoesNotIncludeScopeThenAccessToke @Test public void getTokenResponseWhenTokenUriInvalidThenThrowOAuth2AuthorizationException() { String invalidTokenUri = "https://invalid-provider.com/oauth2/token"; - ClientRegistration clientRegistration = this.from(this.clientRegistration).tokenUri(invalidTokenUri).build(); + ClientRegistration clientRegistration = this.clientRegistration.tokenUri(invalidTokenUri).build(); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( clientRegistration); assertThatExceptionOfType(OAuth2AuthorizationException.class) @@ -264,7 +346,7 @@ public void getTokenResponseWhenMalformedResponseThenThrowOAuth2AuthorizationExc // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest)) .withMessageContaining( @@ -280,7 +362,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti // @formatter:on this.server.enqueue(jsonResponse(accessTokenErrorResponse).setResponseCode(400)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest)) .withMessageContaining("[unauthorized_client]"); @@ -290,7 +372,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti public void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() { this.server.enqueue(new MockResponse().setResponseCode(500)); OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( - this.clientRegistration); + this.clientRegistration.build()); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(clientCredentialsGrantRequest)) .withMessageContaining( @@ -301,16 +383,4 @@ private MockResponse jsonResponse(String json) { return new MockResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(json); } - private ClientRegistration.Builder from(ClientRegistration registration) { - // @formatter:off - return ClientRegistration.withRegistrationId(registration.getRegistrationId()) - .clientId(registration.getClientId()) - .clientSecret(registration.getClientSecret()) - .clientAuthenticationMethod(registration.getClientAuthenticationMethod()) - .authorizationGrantType(registration.getAuthorizationGrantType()) - .scope(registration.getScopes()) - .tokenUri(registration.getProviderDetails().getTokenUri()); - // @formatter:on - } - } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultPasswordTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultPasswordTokenResponseClientTests.java index 66d098880bf..195ab0b8588 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultPasswordTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultPasswordTokenResponseClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,13 @@ package org.springframework.security.oauth2.client.endpoint; +import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.function.Function; +import javax.crypto.spec.SecretKeySpec; + +import com.nimbusds.jose.jwk.JWK; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -30,11 +35,12 @@ import org.springframework.http.MediaType; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; -import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthorizationException; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.TestKeys; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -47,9 +53,9 @@ */ public class DefaultPasswordTokenResponseClientTests { - private DefaultPasswordTokenResponseClient tokenResponseClient = new DefaultPasswordTokenResponseClient(); + private DefaultPasswordTokenResponseClient tokenResponseClient; - private ClientRegistration.Builder clientRegistrationBuilder; + private ClientRegistration.Builder clientRegistration; private String username = "user1"; @@ -59,11 +65,15 @@ public class DefaultPasswordTokenResponseClientTests { @Before public void setup() throws Exception { + this.tokenResponseClient = new DefaultPasswordTokenResponseClient(); this.server = new MockWebServer(); this.server.start(); String tokenUri = this.server.url("/oauth2/token").toString(); - this.clientRegistrationBuilder = TestClientRegistrations.clientRegistration() - .authorizationGrantType(AuthorizationGrantType.PASSWORD).scope("read", "write").tokenUri(tokenUri); + // @formatter:off + this.clientRegistration = TestClientRegistrations.password() + .scope("read", "write") + .tokenUri(tokenUri); + // @formatter:on } @After @@ -97,7 +107,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); Instant expiresAtBefore = Instant.now().plusSeconds(3600); - ClientRegistration clientRegistration = this.clientRegistrationBuilder.build(); + ClientRegistration clientRegistration = this.clientRegistration.build(); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, this.username, this.password); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(passwordGrantRequest); @@ -121,7 +131,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t } @Test - public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSent() throws Exception { + public void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception { // @formatter:off String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" @@ -130,7 +140,7 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen + "}\n"; // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); - ClientRegistration clientRegistration = this.clientRegistrationBuilder + ClientRegistration clientRegistration = this.clientRegistration .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).build(); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, this.username, this.password); @@ -142,6 +152,83 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen assertThat(formParameters).contains("client_secret=client-secret"); } + @Test + public void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + SecretKeySpec secretKey = new SecretKeySpec( + clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + JWK jwk = TestJwks.jwk(secretKey).build(); + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, + this.username, this.password); + this.tokenResponseClient.getTokenResponse(passwordGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + @Test + public void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + JWK jwk = TestJwks.DEFAULT_RSA_JWK; + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, + this.username, this.password); + this.tokenResponseClient.getTokenResponse(passwordGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + private void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) { + NimbusJwtClientAuthenticationParametersConverter<OAuth2PasswordGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>( + jwkResolver); + OAuth2PasswordGrantRequestEntityConverter requestEntityConverter = new OAuth2PasswordGrantRequestEntityConverter(); + requestEntityConverter.addParametersConverter(jwtClientAuthenticationConverter); + this.tokenResponseClient.setRequestEntityConverter(requestEntityConverter); + } + @Test public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() { // @formatter:off @@ -153,7 +240,7 @@ public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAu // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest( - this.clientRegistrationBuilder.build(), this.username, this.password); + this.clientRegistration.build(), this.username, this.password); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(passwordGrantRequest)) .withMessageContaining( @@ -173,7 +260,7 @@ public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasRe // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest( - this.clientRegistrationBuilder.build(), this.username, this.password); + this.clientRegistration.build(), this.username, this.password); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient.getTokenResponse(passwordGrantRequest); RecordedRequest recordedRequest = this.server.takeRequest(); String formParameters = recordedRequest.getBody().readUtf8(); @@ -186,7 +273,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti String accessTokenErrorResponse = "{\n" + " \"error\": \"unauthorized_client\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenErrorResponse).setResponseCode(400)); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest( - this.clientRegistrationBuilder.build(), this.username, this.password); + this.clientRegistration.build(), this.username, this.password); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(passwordGrantRequest)) .withMessageContaining("[unauthorized_client]"); @@ -196,7 +283,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti public void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() { this.server.enqueue(new MockResponse().setResponseCode(500)); OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest( - this.clientRegistrationBuilder.build(), this.username, this.password); + this.clientRegistration.build(), this.username, this.password); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(passwordGrantRequest)) .withMessageContaining("[invalid_token_response] An error occurred while attempting to " diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultRefreshTokenTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultRefreshTokenTokenResponseClientTests.java index c997e94ea9d..beae7489b01 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultRefreshTokenTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultRefreshTokenTokenResponseClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,14 @@ package org.springframework.security.oauth2.client.endpoint; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Collections; +import java.util.function.Function; +import javax.crypto.spec.SecretKeySpec; + +import com.nimbusds.jose.jwk.JWK; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -38,6 +43,8 @@ import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.TestKeys; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -50,9 +57,9 @@ */ public class DefaultRefreshTokenTokenResponseClientTests { - private DefaultRefreshTokenTokenResponseClient tokenResponseClient = new DefaultRefreshTokenTokenResponseClient(); + private DefaultRefreshTokenTokenResponseClient tokenResponseClient; - private ClientRegistration.Builder clientRegistrationBuilder; + private ClientRegistration.Builder clientRegistration; private OAuth2AccessToken accessToken; @@ -62,10 +69,11 @@ public class DefaultRefreshTokenTokenResponseClientTests { @Before public void setup() throws Exception { + this.tokenResponseClient = new DefaultRefreshTokenTokenResponseClient(); this.server = new MockWebServer(); this.server.start(); String tokenUri = this.server.url("/oauth2/token").toString(); - this.clientRegistrationBuilder = TestClientRegistrations.clientRegistration().tokenUri(tokenUri); + this.clientRegistration = TestClientRegistrations.clientRegistration().tokenUri(tokenUri); this.accessToken = TestOAuth2AccessTokens.scopes("read", "write"); this.refreshToken = TestOAuth2RefreshTokens.refreshToken(); } @@ -102,7 +110,7 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); Instant expiresAtBefore = Instant.now().plusSeconds(3600); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - this.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken); + this.clientRegistration.build(), this.accessToken, this.refreshToken); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient .getTokenResponse(refreshTokenGrantRequest); Instant expiresAtAfter = Instant.now().plusSeconds(3600); @@ -124,11 +132,16 @@ public void getTokenResponseWhenSuccessResponseThenReturnAccessTokenResponse() t } @Test - public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSent() throws Exception { - String accessTokenSuccessResponse = "{\n" + " \"access_token\": \"access-token-1234\",\n" - + " \"token_type\": \"bearer\",\n" + " \"expires_in\": \"3600\"\n" + "}\n"; + public void getTokenResponseWhenAuthenticationClientSecretPostThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); - ClientRegistration clientRegistration = this.clientRegistrationBuilder + ClientRegistration clientRegistration = this.clientRegistration .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).build(); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, this.accessToken, this.refreshToken); @@ -140,6 +153,83 @@ public void getTokenResponseWhenClientAuthenticationPostThenFormParametersAreSen assertThat(formParameters).contains("client_secret=client-secret"); } + @Test + public void getTokenResponseWhenAuthenticationClientSecretJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientSecret(TestKeys.DEFAULT_ENCODED_SECRET_KEY) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + SecretKeySpec secretKey = new SecretKeySpec( + clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + JWK jwk = TestJwks.jwk(secretKey).build(); + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, + this.accessToken, this.refreshToken); + this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + @Test + public void getTokenResponseWhenAuthenticationPrivateKeyJwtThenFormParametersAreSent() throws Exception { + // @formatter:off + String accessTokenSuccessResponse = "{\n" + + " \"access_token\": \"access-token-1234\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"expires_in\": \"3600\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); + + // @formatter:off + ClientRegistration clientRegistration = this.clientRegistration + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + + // Configure Jwt client authentication converter + JWK jwk = TestJwks.DEFAULT_RSA_JWK; + Function<ClientRegistration, JWK> jwkResolver = (registration) -> jwk; + configureJwtClientAuthenticationConverter(jwkResolver); + + OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, + this.accessToken, this.refreshToken); + this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest); + RecordedRequest recordedRequest = this.server.takeRequest(); + assertThat(recordedRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); + String formParameters = recordedRequest.getBody().readUtf8(); + assertThat(formParameters) + .contains("client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"); + assertThat(formParameters).contains("client_assertion="); + } + + private void configureJwtClientAuthenticationConverter(Function<ClientRegistration, JWK> jwkResolver) { + NimbusJwtClientAuthenticationParametersConverter<OAuth2RefreshTokenGrantRequest> jwtClientAuthenticationConverter = new NimbusJwtClientAuthenticationParametersConverter<>( + jwkResolver); + OAuth2RefreshTokenGrantRequestEntityConverter requestEntityConverter = new OAuth2RefreshTokenGrantRequestEntityConverter(); + requestEntityConverter.addParametersConverter(jwtClientAuthenticationConverter); + this.tokenResponseClient.setRequestEntityConverter(requestEntityConverter); + } + @Test public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAuth2AuthorizationException() { // @formatter:off @@ -151,7 +241,7 @@ public void getTokenResponseWhenSuccessResponseAndNotBearerTokenTypeThenThrowOAu // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - this.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken); + this.clientRegistration.build(), this.accessToken, this.refreshToken); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest)) .withMessageContaining("[invalid_token_response] An error occurred while attempting to " @@ -171,8 +261,7 @@ public void getTokenResponseWhenSuccessResponseIncludesScopeThenAccessTokenHasRe // @formatter:on this.server.enqueue(jsonResponse(accessTokenSuccessResponse)); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - this.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken, - Collections.singleton("read")); + this.clientRegistration.build(), this.accessToken, this.refreshToken, Collections.singleton("read")); OAuth2AccessTokenResponse accessTokenResponse = this.tokenResponseClient .getTokenResponse(refreshTokenGrantRequest); RecordedRequest recordedRequest = this.server.takeRequest(); @@ -186,7 +275,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti String accessTokenErrorResponse = "{\n" + " \"error\": \"unauthorized_client\"\n" + "}\n"; this.server.enqueue(jsonResponse(accessTokenErrorResponse).setResponseCode(400)); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - this.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken); + this.clientRegistration.build(), this.accessToken, this.refreshToken); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest)) .withMessageContaining("[unauthorized_client]"); @@ -196,7 +285,7 @@ public void getTokenResponseWhenErrorResponseThenThrowOAuth2AuthorizationExcepti public void getTokenResponseWhenServerErrorResponseThenThrowOAuth2AuthorizationException() { this.server.enqueue(new MockResponse().setResponseCode(500)); OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - this.clientRegistrationBuilder.build(), this.accessToken, this.refreshToken); + this.clientRegistration.build(), this.accessToken, this.refreshToken); assertThatExceptionOfType(OAuth2AuthorizationException.class) .isThrownBy(() -> this.tokenResponseClient.getTokenResponse(refreshTokenGrantRequest)) .withMessageContaining("[invalid_token_response] An error occurred while attempting to " diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderTests.java new file mode 100644 index 00000000000..e0a650daf0f --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JoseHeaderTests.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import org.junit.Test; + +import org.springframework.security.oauth2.jose.JwaAlgorithm; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * Tests for {@link JoseHeader}. + * + * @author Joe Grandja + */ +public class JoseHeaderTests { + + @Test + public void withAlgorithmWhenNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JoseHeader.withAlgorithm(null)) + .isInstanceOf(IllegalArgumentException.class).withMessage("jwaAlgorithm cannot be null"); + } + + @Test + public void buildWhenAllHeadersProvidedThenAllHeadersAreSet() { + JoseHeader expectedJoseHeader = TestJoseHeaders.joseHeader().build(); + + // @formatter:off + JoseHeader joseHeader = JoseHeader.withAlgorithm(expectedJoseHeader.getAlgorithm()) + .jwkSetUrl(expectedJoseHeader.getJwkSetUrl().toExternalForm()) + .jwk(expectedJoseHeader.getJwk()) + .keyId(expectedJoseHeader.getKeyId()) + .x509Url(expectedJoseHeader.getX509Url().toExternalForm()) + .x509CertificateChain(expectedJoseHeader.getX509CertificateChain()) + .x509SHA1Thumbprint(expectedJoseHeader.getX509SHA1Thumbprint()) + .x509SHA256Thumbprint(expectedJoseHeader.getX509SHA256Thumbprint()) + .type(expectedJoseHeader.getType()) + .contentType(expectedJoseHeader.getContentType()) + .headers((headers) -> headers.put("custom-header-name", "custom-header-value")) + .build(); + // @formatter:on + + assertThat(joseHeader.<JwaAlgorithm>getAlgorithm()).isEqualTo(expectedJoseHeader.getAlgorithm()); + assertThat(joseHeader.getJwkSetUrl()).isEqualTo(expectedJoseHeader.getJwkSetUrl()); + assertThat(joseHeader.getJwk()).isEqualTo(expectedJoseHeader.getJwk()); + assertThat(joseHeader.getKeyId()).isEqualTo(expectedJoseHeader.getKeyId()); + assertThat(joseHeader.getX509Url()).isEqualTo(expectedJoseHeader.getX509Url()); + assertThat(joseHeader.getX509CertificateChain()).isEqualTo(expectedJoseHeader.getX509CertificateChain()); + assertThat(joseHeader.getX509SHA1Thumbprint()).isEqualTo(expectedJoseHeader.getX509SHA1Thumbprint()); + assertThat(joseHeader.getX509SHA256Thumbprint()).isEqualTo(expectedJoseHeader.getX509SHA256Thumbprint()); + assertThat(joseHeader.getType()).isEqualTo(expectedJoseHeader.getType()); + assertThat(joseHeader.getContentType()).isEqualTo(expectedJoseHeader.getContentType()); + assertThat(joseHeader.<String>getHeader("custom-header-name")).isEqualTo("custom-header-value"); + assertThat(joseHeader.getHeaders()).isEqualTo(expectedJoseHeader.getHeaders()); + } + + @Test + public void fromWhenNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JoseHeader.from(null)) + .isInstanceOf(IllegalArgumentException.class).withMessage("headers cannot be null"); + } + + @Test + public void fromWhenHeadersProvidedThenCopied() { + JoseHeader expectedJoseHeader = TestJoseHeaders.joseHeader().build(); + JoseHeader joseHeader = JoseHeader.from(expectedJoseHeader).build(); + assertThat(joseHeader.getHeaders()).isEqualTo(expectedJoseHeader.getHeaders()); + } + + @Test + public void headerWhenNameNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).header(null, "value")) + .withMessage("name cannot be empty"); + } + + @Test + public void headerWhenValueNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).header("name", null)) + .withMessage("value cannot be null"); + } + + @Test + public void getHeaderWhenNullThenThrowIllegalArgumentException() { + JoseHeader joseHeader = TestJoseHeaders.joseHeader().build(); + + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> joseHeader.getHeader(null)) + .isInstanceOf(IllegalArgumentException.class).withMessage("name cannot be empty"); + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSetTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSetTests.java new file mode 100644 index 00000000000..2b10762c66f --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/JwtClaimsSetTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * Tests for {@link JwtClaimsSet}. + * + * @author Joe Grandja + */ +public class JwtClaimsSetTests { + + @Test + public void buildWhenClaimsEmptyThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwtClaimsSet.builder().build()) + .isInstanceOf(IllegalArgumentException.class).withMessage("claims cannot be empty"); + } + + @Test + public void buildWhenAllClaimsProvidedThenAllClaimsAreSet() { + JwtClaimsSet expectedJwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + // @formatter:off + JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder() + .issuer(expectedJwtClaimsSet.getIssuer().toExternalForm()) + .subject(expectedJwtClaimsSet.getSubject()) + .audience(expectedJwtClaimsSet.getAudience()) + .issuedAt(expectedJwtClaimsSet.getIssuedAt()) + .notBefore(expectedJwtClaimsSet.getNotBefore()) + .expiresAt(expectedJwtClaimsSet.getExpiresAt()) + .id(expectedJwtClaimsSet.getId()) + .claims((claims) -> claims.put("custom-claim-name", "custom-claim-value")) + .build(); + // @formatter:on + + assertThat(jwtClaimsSet.getIssuer()).isEqualTo(expectedJwtClaimsSet.getIssuer()); + assertThat(jwtClaimsSet.getSubject()).isEqualTo(expectedJwtClaimsSet.getSubject()); + assertThat(jwtClaimsSet.getAudience()).isEqualTo(expectedJwtClaimsSet.getAudience()); + assertThat(jwtClaimsSet.getIssuedAt()).isEqualTo(expectedJwtClaimsSet.getIssuedAt()); + assertThat(jwtClaimsSet.getNotBefore()).isEqualTo(expectedJwtClaimsSet.getNotBefore()); + assertThat(jwtClaimsSet.getExpiresAt()).isEqualTo(expectedJwtClaimsSet.getExpiresAt()); + assertThat(jwtClaimsSet.getId()).isEqualTo(expectedJwtClaimsSet.getId()); + assertThat(jwtClaimsSet.<String>getClaim("custom-claim-name")).isEqualTo("custom-claim-value"); + assertThat(jwtClaimsSet.getClaims()).isEqualTo(expectedJwtClaimsSet.getClaims()); + } + + @Test + public void fromWhenNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> JwtClaimsSet.from(null)) + .isInstanceOf(IllegalArgumentException.class).withMessage("claims cannot be null"); + } + + @Test + public void fromWhenClaimsProvidedThenCopied() { + JwtClaimsSet expectedJwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + JwtClaimsSet jwtClaimsSet = JwtClaimsSet.from(expectedJwtClaimsSet).build(); + assertThat(jwtClaimsSet.getClaims()).isEqualTo(expectedJwtClaimsSet.getClaims()); + } + + @Test + public void claimWhenNameNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> JwtClaimsSet.builder().claim(null, "value")).withMessage("name cannot be empty"); + } + + @Test + public void claimWhenValueNullThenThrowIllegalArgumentException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> JwtClaimsSet.builder().claim("name", null)).withMessage("value cannot be null"); + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoderTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoderTests.java new file mode 100644 index 00000000000..2d29a370ea0 --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwsEncoderTests.java @@ -0,0 +1,347 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.nimbusds.jose.KeySourceException; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSelector; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.KeyUse; +import com.nimbusds.jose.jwk.OctetSequenceKey; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jose.util.Base64URL; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.TestKeys; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * Tests for {@link NimbusJwsEncoder}. + * + * @author Joe Grandja + */ +public class NimbusJwsEncoderTests { + + private List<JWK> jwkList; + + private JWKSource<SecurityContext> jwkSource; + + private NimbusJwsEncoder jwsEncoder; + + @Before + public void setUp() { + this.jwkList = new ArrayList<>(); + this.jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(new JWKSet(this.jwkList)); + this.jwsEncoder = new NimbusJwsEncoder(this.jwkSource); + } + + @Test + public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> new NimbusJwsEncoder(null)) + .withMessage("jwkSource cannot be null"); + } + + @Test + public void encodeWhenHeadersNullThenThrowIllegalArgumentException() { + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + assertThatIllegalArgumentException().isThrownBy(() -> this.jwsEncoder.encode(null, jwtClaimsSet)) + .withMessage("headers cannot be null"); + } + + @Test + public void encodeWhenClaimsNullThenThrowIllegalArgumentException() { + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + + assertThatIllegalArgumentException().isThrownBy(() -> this.jwsEncoder.encode(joseHeader, null)) + .withMessage("claims cannot be null"); + } + + @Test + public void encodeWhenJwkSelectFailedThenThrowJwtEncodingException() throws Exception { + this.jwkSource = mock(JWKSource.class); + this.jwsEncoder = new NimbusJwsEncoder(this.jwkSource); + given(this.jwkSource.get(any(), any())).willThrow(new KeySourceException("key source error")); + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + assertThatExceptionOfType(JwtEncodingException.class) + .isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet)) + .withMessageContaining("Failed to select a JWK signing key -> key source error"); + } + + @Test + public void encodeWhenJwkMultipleSelectedThenThrowJwtEncodingException() throws Exception { + RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK; + this.jwkList.add(rsaJwk); + this.jwkList.add(rsaJwk); + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + assertThatExceptionOfType(JwtEncodingException.class) + .isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet)) + .withMessageContaining("Found multiple JWK signing keys for algorithm 'RS256'"); + } + + @Test + public void encodeWhenJwkSelectEmptyThenThrowJwtEncodingException() { + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + assertThatExceptionOfType(JwtEncodingException.class) + .isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet)) + .withMessageContaining("Failed to select a JWK signing key"); + } + + @Test + public void encodeWhenJwkSelectWithProvidedKidThenSelected() { + // @formatter:off + RSAKey rsaJwk1 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .keyID("rsa-jwk-1") + .build(); + this.jwkList.add(rsaJwk1); + RSAKey rsaJwk2 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .keyID("rsa-jwk-2") + .build(); + this.jwkList.add(rsaJwk2); + // @formatter:on + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).keyId(rsaJwk2.getKeyID()).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + Jwt encodedJws = this.jwsEncoder.encode(joseHeader, jwtClaimsSet); + + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk2.getKeyID()); + } + + @Test + public void encodeWhenJwkSelectWithProvidedX5TS256ThenSelected() { + // @formatter:off + RSAKey rsaJwk1 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .x509CertSHA256Thumbprint(new Base64URL("x509CertSHA256Thumbprint-1")) + .keyID(null) + .build(); + this.jwkList.add(rsaJwk1); + RSAKey rsaJwk2 = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .x509CertSHA256Thumbprint(new Base64URL("x509CertSHA256Thumbprint-2")) + .keyID(null) + .build(); + this.jwkList.add(rsaJwk2); + // @formatter:on + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256) + .x509SHA256Thumbprint(rsaJwk1.getX509CertSHA256Thumbprint().toString()).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + Jwt encodedJws = this.jwsEncoder.encode(joseHeader, jwtClaimsSet); + + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T_S256)) + .isEqualTo(rsaJwk1.getX509CertSHA256Thumbprint().toString()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isNull(); + } + + @Test + public void encodeWhenJwkUseEncryptionThenThrowJwtEncodingException() throws Exception { + // @formatter:off + RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .keyUse(KeyUse.ENCRYPTION) + .build(); + // @formatter:on + + this.jwkSource = mock(JWKSource.class); + this.jwsEncoder = new NimbusJwsEncoder(this.jwkSource); + given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk)); + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + assertThatExceptionOfType(JwtEncodingException.class) + .isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet)).withMessageContaining( + "Failed to create a JWS Signer -> The JWK use must be sig (signature) or unspecified"); + } + + @Test + public void encodeWhenSuccessThenDecodes() throws Exception { + // @formatter:off + RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .keyID("rsa-jwk-1") + .x509CertSHA256Thumbprint(new Base64URL("x509CertSHA256Thumbprint-1")) + .build(); + this.jwkList.add(rsaJwk); + // @formatter:on + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + Jwt encodedJws = this.jwsEncoder.encode(joseHeader, jwtClaimsSet); + + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(joseHeader.getAlgorithm()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.JKU)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.JWK)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5U)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5C)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T_S256)) + .isEqualTo(rsaJwk.getX509CertSHA256Thumbprint().toString()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.TYP)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.CTY)).isNull(); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.CRIT)).isNull(); + + assertThat(encodedJws.getIssuer()).isEqualTo(jwtClaimsSet.getIssuer()); + assertThat(encodedJws.getSubject()).isEqualTo(jwtClaimsSet.getSubject()); + assertThat(encodedJws.getAudience()).isEqualTo(jwtClaimsSet.getAudience()); + assertThat(encodedJws.getExpiresAt()).isEqualTo(jwtClaimsSet.getExpiresAt()); + assertThat(encodedJws.getNotBefore()).isEqualTo(jwtClaimsSet.getNotBefore()); + assertThat(encodedJws.getIssuedAt()).isEqualTo(jwtClaimsSet.getIssuedAt()); + assertThat(encodedJws.getId()).isEqualTo(jwtClaimsSet.getId()); + assertThat(encodedJws.<String>getClaim("custom-claim-name")).isEqualTo("custom-claim-value"); + + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build(); + jwtDecoder.decode(encodedJws.getTokenValue()); + } + + @Test + public void encodeWhenKeysRotatedThenNewKeyUsed() throws Exception { + TestJWKSource jwkSource = new TestJWKSource(); + JWKSource<SecurityContext> jwkSourceDelegate = spy(new JWKSource<SecurityContext>() { + @Override + public List<JWK> get(JWKSelector jwkSelector, SecurityContext context) { + return jwkSource.get(jwkSelector, context); + } + }); + NimbusJwsEncoder jwsEncoder = new NimbusJwsEncoder(jwkSourceDelegate); + + JwkListResultCaptor jwkListResultCaptor = new JwkListResultCaptor(); + willAnswer(jwkListResultCaptor).given(jwkSourceDelegate).get(any(), any()); + + JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); + JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build(); + + Jwt encodedJws = jwsEncoder.encode(joseHeader, jwtClaimsSet); + + JWK jwk1 = jwkListResultCaptor.getResult().get(0); + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk1).toRSAPublicKey()).build(); + jwtDecoder.decode(encodedJws.getTokenValue()); + + jwkSource.rotate(); // Simulate key rotation + + encodedJws = jwsEncoder.encode(joseHeader, jwtClaimsSet); + + JWK jwk2 = jwkListResultCaptor.getResult().get(0); + jwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk2).toRSAPublicKey()).build(); + jwtDecoder.decode(encodedJws.getTokenValue()); + + assertThat(jwk1.getKeyID()).isNotEqualTo(jwk2.getKeyID()); + } + + private static final class JwkListResultCaptor implements Answer<List<JWK>> { + + private List<JWK> result; + + private List<JWK> getResult() { + return this.result; + } + + @SuppressWarnings("unchecked") + @Override + public List<JWK> answer(InvocationOnMock invocationOnMock) throws Throwable { + this.result = (List<JWK>) invocationOnMock.callRealMethod(); + return this.result; + } + + } + + private static final class TestJWKSource implements JWKSource<SecurityContext> { + + private int keyId = 1000; + + private JWKSet jwkSet; + + private TestJWKSource() { + init(); + } + + @Override + public List<JWK> get(JWKSelector jwkSelector, SecurityContext context) { + return jwkSelector.select(this.jwkSet); + } + + private void init() { + // @formatter:off + RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY) + .keyID("rsa-jwk-" + this.keyId++) + .build(); + ECKey ecJwk = TestJwks.jwk((ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(), (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate()) + .keyID("ec-jwk-" + this.keyId++) + .build(); + OctetSequenceKey secretJwk = TestJwks.jwk(TestKeys.DEFAULT_SECRET_KEY) + .keyID("secret-jwk-" + this.keyId++) + .build(); + // @formatter:on + this.jwkSet = new JWKSet(Arrays.asList(rsaJwk, ecJwk, secretJwk)); + } + + private void rotate() { + init(); + } + + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java new file mode 100644 index 00000000000..eabeef00c2f --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java @@ -0,0 +1,175 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.util.Collections; +import java.util.function.Function; + +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.OctetSequenceKey; +import com.nimbusds.jose.jwk.RSAKey; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.security.oauth2.core.OAuth2AuthorizationException; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.jose.TestJwks; +import org.springframework.security.oauth2.jose.jws.MacAlgorithm; +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.util.MultiValueMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; + +/** + * Tests for {@link NimbusJwtClientAuthenticationParametersConverter}. + * + * @author Joe Grandja + */ +public class NimbusJwtClientAuthenticationParametersConverterTests { + + private Function<ClientRegistration, JWK> jwkResolver; + + private NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter; + + @Before + public void setup() { + this.jwkResolver = mock(Function.class); + this.converter = new NimbusJwtClientAuthenticationParametersConverter<>(this.jwkResolver); + } + + @Test + public void constructorWhenJwkResolverNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new NimbusJwtClientAuthenticationParametersConverter<>(null)) + .withMessage("jwkResolver cannot be null"); + } + + @Test + public void convertWhenAuthorizationGrantRequestNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.convert(null)) + .withMessage("authorizationGrantRequest cannot be null"); + } + + @Test + public void convertWhenOtherClientAuthenticationMethodThenNotCustomized() { + // @formatter:off + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials() + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .build(); + // @formatter:on + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + assertThat(this.converter.convert(clientCredentialsGrantRequest)).isNull(); + verifyNoInteractions(this.jwkResolver); + } + + @Test + public void convertWhenJwkNotResolvedThenThrowOAuth2AuthorizationException() { + // @formatter:off + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials() + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + assertThatExceptionOfType(OAuth2AuthorizationException.class) + .isThrownBy(() -> this.converter.convert(clientCredentialsGrantRequest)) + .withMessage("[invalid_key] Failed to resolve JWK signing key for client registration '" + + clientRegistration.getRegistrationId() + "'."); + } + + @Test + public void convertWhenPrivateKeyJwtClientAuthenticationMethodThenCustomized() throws Exception { + RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK; + given(this.jwkResolver.apply(any())).willReturn(rsaJwk); + + // @formatter:off + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials() + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build(); + // @formatter:on + + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + MultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest); + + assertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE)) + .isEqualTo("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + String encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION); + assertThat(encodedJws).isNotNull(); + + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build(); + Jwt jws = jwtDecoder.decode(encodedJws); + + assertThat(jws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(SignatureAlgorithm.RS256.getName()); + assertThat(jws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); + assertThat(jws.<String>getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId()); + assertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId()); + assertThat(jws.getAudience()) + .isEqualTo(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri())); + assertThat(jws.getId()).isNotNull(); + assertThat(jws.getIssuedAt()).isNotNull(); + assertThat(jws.getExpiresAt()).isNotNull(); + } + + @Test + public void convertWhenClientSecretJwtClientAuthenticationMethodThenCustomized() { + OctetSequenceKey secretJwk = TestJwks.DEFAULT_SECRET_JWK; + given(this.jwkResolver.apply(any())).willReturn(secretJwk); + + // @formatter:off + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials() + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .build(); + // @formatter:on + + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + MultiValueMap<String, String> parameters = this.converter.convert(clientCredentialsGrantRequest); + + assertThat(parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE)) + .isEqualTo("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + String encodedJws = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION); + assertThat(encodedJws).isNotNull(); + + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretJwk.toSecretKey()).build(); + Jwt jws = jwtDecoder.decode(encodedJws); + + assertThat(jws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(jws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(secretJwk.getKeyID()); + assertThat(jws.<String>getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId()); + assertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId()); + assertThat(jws.getAudience()) + .isEqualTo(Collections.singletonList(clientRegistration.getProviderDetails().getTokenUri())); + assertThat(jws.getId()).isNotNull(); + assertThat(jws.getIssuedAt()).isNotNull(); + assertThat(jws.getExpiresAt()).isNotNull(); + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverterTests.java index fb46cfccdee..8394f1be8b1 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2AuthorizationCodeGrantRequestEntityConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,36 @@ package org.springframework.security.oauth2.client.endpoint; -import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; +import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; +import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.endpoint.PkceParameterNames; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationExchanges; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests; +import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses; import org.springframework.util.MultiValueMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; /** * Tests for {@link OAuth2AuthorizationCodeGrantRequestEntityConverter}. @@ -46,49 +54,78 @@ */ public class OAuth2AuthorizationCodeGrantRequestEntityConverterTests { - private OAuth2AuthorizationCodeGrantRequestEntityConverter converter = new OAuth2AuthorizationCodeGrantRequestEntityConverter(); - - // @formatter:off - private ClientRegistration.Builder clientRegistrationBuilder = ClientRegistration - .withRegistrationId("registration-1") - .clientId("client-1") - .clientSecret("secret") - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .redirectUri("https://client.com/callback/client-1") - .scope("read", "write") - .authorizationUri("https://provider.com/oauth2/authorize") - .tokenUri("https://provider.com/oauth2/token") - .userInfoUri("https://provider.com/user") - .userNameAttributeName("id") - .clientName("client-1"); - // @formatter:on - - // @formatter:off - private OAuth2AuthorizationRequest.Builder authorizationRequestBuilder = OAuth2AuthorizationRequest - .authorizationCode() - .clientId("client-1") - .state("state-1234") - .authorizationUri("https://provider.com/oauth2/authorize") - .redirectUri("https://client.com/callback/client-1") - .scopes(new HashSet(Arrays.asList("read", "write"))); - // @formatter:on - - // @formatter:off - private OAuth2AuthorizationResponse.Builder authorizationResponseBuilder = OAuth2AuthorizationResponse - .success("code-1234") - .state("state-1234") - .redirectUri("https://client.com/callback/client-1"); - // @formatter:on + private OAuth2AuthorizationCodeGrantRequestEntityConverter converter; + + @Before + public void setup() { + this.converter = new OAuth2AuthorizationCodeGrantRequestEntityConverter(); + } + + @Test + public void setHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void addHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void setParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void addParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void convertWhenHeadersConverterSetThenCalled() { + Converter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> headersConverter1 = mock(Converter.class); + this.converter.setHeadersConverter(headersConverter1); + Converter<OAuth2AuthorizationCodeGrantRequest, HttpHeaders> headersConverter2 = mock(Converter.class); + this.converter.addHeadersConverter(headersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AuthorizationExchange authorizationExchange = TestOAuth2AuthorizationExchanges.success(); + OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = new OAuth2AuthorizationCodeGrantRequest( + clientRegistration, authorizationExchange); + this.converter.convert(authorizationCodeGrantRequest); + InOrder inOrder = inOrder(headersConverter1, headersConverter2); + inOrder.verify(headersConverter1).convert(any(OAuth2AuthorizationCodeGrantRequest.class)); + inOrder.verify(headersConverter2).convert(any(OAuth2AuthorizationCodeGrantRequest.class)); + } + + @Test + public void convertWhenParametersConverterSetThenCalled() { + Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter1 = mock( + Converter.class); + this.converter.setParametersConverter(parametersConverter1); + Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter2 = mock( + Converter.class); + this.converter.addParametersConverter(parametersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AuthorizationExchange authorizationExchange = TestOAuth2AuthorizationExchanges.success(); + OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = new OAuth2AuthorizationCodeGrantRequest( + clientRegistration, authorizationExchange); + this.converter.convert(authorizationCodeGrantRequest); + InOrder inOrder = inOrder(parametersConverter1, parametersConverter2); + inOrder.verify(parametersConverter1).convert(any(OAuth2AuthorizationCodeGrantRequest.class)); + inOrder.verify(parametersConverter2).convert(any(OAuth2AuthorizationCodeGrantRequest.class)); + } @SuppressWarnings("unchecked") @Test public void convertWhenGrantRequestValidThenConverts() { - ClientRegistration clientRegistration = this.clientRegistrationBuilder.build(); - OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build(); - OAuth2AuthorizationResponse authorizationResponse = this.authorizationResponseBuilder.build(); - OAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest, - authorizationResponse); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AuthorizationExchange authorizationExchange = TestOAuth2AuthorizationExchanges.success(); + OAuth2AuthorizationRequest authorizationRequest = authorizationExchange.getAuthorizationRequest(); + OAuth2AuthorizationResponse authorizationResponse = authorizationExchange.getAuthorizationResponse(); OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = new OAuth2AuthorizationCodeGrantRequest( clientRegistration, authorizationExchange); RequestEntity<?> requestEntity = this.converter.convert(authorizationCodeGrantRequest); @@ -103,25 +140,25 @@ public void convertWhenGrantRequestValidThenConverts() { MultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody(); assertThat(formParameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) .isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - assertThat(formParameters.getFirst(OAuth2ParameterNames.CODE)).isEqualTo("code-1234"); + assertThat(formParameters.getFirst(OAuth2ParameterNames.CODE)).isEqualTo(authorizationResponse.getCode()); assertThat(formParameters.getFirst(OAuth2ParameterNames.CLIENT_ID)).isNull(); assertThat(formParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI)) - .isEqualTo(clientRegistration.getRedirectUri()); + .isEqualTo(authorizationRequest.getRedirectUri()); } @SuppressWarnings("unchecked") @Test public void convertWhenPkceGrantRequestValidThenConverts() { - ClientRegistration clientRegistration = this.clientRegistrationBuilder.clientAuthenticationMethod(null) - .clientSecret(null).build(); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() + .clientAuthenticationMethod(null).clientSecret(null).build(); Map<String, Object> attributes = new HashMap<>(); attributes.put(PkceParameterNames.CODE_VERIFIER, "code-verifier-1234"); Map<String, Object> additionalParameters = new HashMap<>(); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge-1234"); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); - OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.attributes(attributes) - .additionalParameters(additionalParameters).build(); - OAuth2AuthorizationResponse authorizationResponse = this.authorizationResponseBuilder.build(); + OAuth2AuthorizationRequest authorizationRequest = TestOAuth2AuthorizationRequests.request() + .attributes(attributes).additionalParameters(additionalParameters).build(); + OAuth2AuthorizationResponse authorizationResponse = TestOAuth2AuthorizationResponses.success().build(); OAuth2AuthorizationExchange authorizationExchange = new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse); OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest = new OAuth2AuthorizationCodeGrantRequest( @@ -138,11 +175,13 @@ public void convertWhenPkceGrantRequestValidThenConverts() { MultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody(); assertThat(formParameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) .isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - assertThat(formParameters.getFirst(OAuth2ParameterNames.CODE)).isEqualTo("code-1234"); + assertThat(formParameters.getFirst(OAuth2ParameterNames.CODE)).isEqualTo(authorizationResponse.getCode()); assertThat(formParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI)) - .isEqualTo(clientRegistration.getRedirectUri()); - assertThat(formParameters.getFirst(OAuth2ParameterNames.CLIENT_ID)).isEqualTo("client-1"); - assertThat(formParameters.getFirst(PkceParameterNames.CODE_VERIFIER)).isEqualTo("code-verifier-1234"); + .isEqualTo(authorizationRequest.getRedirectUri()); + assertThat(formParameters.getFirst(OAuth2ParameterNames.CLIENT_ID)) + .isEqualTo(authorizationRequest.getClientId()); + assertThat(formParameters.getFirst(PkceParameterNames.CODE_VERIFIER)) + .isEqualTo(authorizationRequest.getAttribute(PkceParameterNames.CODE_VERIFIER)); } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverterTests.java index 79825c2b99e..b509cfcd352 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,18 +18,24 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; +import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.util.MultiValueMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; /** * Tests for {@link OAuth2ClientCredentialsGrantRequestEntityConverter}. @@ -38,30 +44,76 @@ */ public class OAuth2ClientCredentialsGrantRequestEntityConverterTests { - private OAuth2ClientCredentialsGrantRequestEntityConverter converter = new OAuth2ClientCredentialsGrantRequestEntityConverter(); - - private OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest; + private OAuth2ClientCredentialsGrantRequestEntityConverter converter; @Before public void setup() { - // @formatter:off - ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("registration-1") - .clientId("client-1") - .clientSecret("secret") - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .scope("read", "write") - .tokenUri("https://provider.com/oauth2/token") - .build(); - // @formatter:on - this.clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration); + this.converter = new OAuth2ClientCredentialsGrantRequestEntityConverter(); + } + + @Test + public void setHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void addHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void setParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void addParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void convertWhenHeadersConverterSetThenCalled() { + Converter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> headersConverter1 = mock(Converter.class); + this.converter.setHeadersConverter(headersConverter1); + Converter<OAuth2ClientCredentialsGrantRequest, HttpHeaders> headersConverter2 = mock(Converter.class); + this.converter.addHeadersConverter(headersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build(); + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + this.converter.convert(clientCredentialsGrantRequest); + InOrder inOrder = inOrder(headersConverter1, headersConverter2); + inOrder.verify(headersConverter1).convert(any(OAuth2ClientCredentialsGrantRequest.class)); + inOrder.verify(headersConverter2).convert(any(OAuth2ClientCredentialsGrantRequest.class)); + } + + @Test + public void convertWhenParametersConverterSetThenCalled() { + Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter1 = mock( + Converter.class); + this.converter.setParametersConverter(parametersConverter1); + Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter2 = mock( + Converter.class); + this.converter.addParametersConverter(parametersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build(); + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + this.converter.convert(clientCredentialsGrantRequest); + InOrder inOrder = inOrder(parametersConverter1, parametersConverter2); + inOrder.verify(parametersConverter1).convert(any(OAuth2ClientCredentialsGrantRequest.class)); + inOrder.verify(parametersConverter2).convert(any(OAuth2ClientCredentialsGrantRequest.class)); } @SuppressWarnings("unchecked") @Test public void convertWhenGrantRequestValidThenConverts() { - RequestEntity<?> requestEntity = this.converter.convert(this.clientCredentialsGrantRequest); - ClientRegistration clientRegistration = this.clientCredentialsGrantRequest.getClientRegistration(); + ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials().build(); + OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest( + clientRegistration); + RequestEntity<?> requestEntity = this.converter.convert(clientCredentialsGrantRequest); assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST); assertThat(requestEntity.getUrl().toASCIIString()) .isEqualTo(clientRegistration.getProviderDetails().getTokenUri()); @@ -73,7 +125,7 @@ public void convertWhenGrantRequestValidThenConverts() { MultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody(); assertThat(formParameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) .isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()); - assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("read write"); + assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).contains(clientRegistration.getScopes()); } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverterTests.java index 7e85dcc4978..7ab3519d7ae 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2PasswordGrantRequestEntityConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; +import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -30,6 +32,10 @@ import org.springframework.util.MultiValueMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; /** * Tests for {@link OAuth2PasswordGrantRequestEntityConverter}. @@ -38,26 +44,76 @@ */ public class OAuth2PasswordGrantRequestEntityConverterTests { - private OAuth2PasswordGrantRequestEntityConverter converter = new OAuth2PasswordGrantRequestEntityConverter(); - - private OAuth2PasswordGrantRequest passwordGrantRequest; + private OAuth2PasswordGrantRequestEntityConverter converter; @Before public void setup() { - // @formatter:off - ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() - .authorizationGrantType(AuthorizationGrantType.PASSWORD) - .scope("read", "write") - .build(); - // @formatter:on - this.passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, "user1", "password"); + this.converter = new OAuth2PasswordGrantRequestEntityConverter(); + } + + @Test + public void setHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void addHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void setParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void addParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void convertWhenHeadersConverterSetThenCalled() { + Converter<OAuth2PasswordGrantRequest, HttpHeaders> headersConverter1 = mock(Converter.class); + this.converter.setHeadersConverter(headersConverter1); + Converter<OAuth2PasswordGrantRequest, HttpHeaders> headersConverter2 = mock(Converter.class); + this.converter.addHeadersConverter(headersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.password().build(); + OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, "user1", + "password"); + this.converter.convert(passwordGrantRequest); + InOrder inOrder = inOrder(headersConverter1, headersConverter2); + inOrder.verify(headersConverter1).convert(any(OAuth2PasswordGrantRequest.class)); + inOrder.verify(headersConverter2).convert(any(OAuth2PasswordGrantRequest.class)); + } + + @Test + public void convertWhenParametersConverterSetThenCalled() { + Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> parametersConverter1 = mock( + Converter.class); + this.converter.setParametersConverter(parametersConverter1); + Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> parametersConverter2 = mock( + Converter.class); + this.converter.addParametersConverter(parametersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.password().build(); + OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, "user1", + "password"); + this.converter.convert(passwordGrantRequest); + InOrder inOrder = inOrder(parametersConverter1, parametersConverter2); + inOrder.verify(parametersConverter1).convert(any(OAuth2PasswordGrantRequest.class)); + inOrder.verify(parametersConverter2).convert(any(OAuth2PasswordGrantRequest.class)); } @SuppressWarnings("unchecked") @Test public void convertWhenGrantRequestValidThenConverts() { - RequestEntity<?> requestEntity = this.converter.convert(this.passwordGrantRequest); - ClientRegistration clientRegistration = this.passwordGrantRequest.getClientRegistration(); + ClientRegistration clientRegistration = TestClientRegistrations.password().build(); + OAuth2PasswordGrantRequest passwordGrantRequest = new OAuth2PasswordGrantRequest(clientRegistration, "user1", + "password"); + RequestEntity<?> requestEntity = this.converter.convert(passwordGrantRequest); assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST); assertThat(requestEntity.getUrl().toASCIIString()) .isEqualTo(clientRegistration.getProviderDetails().getTokenUri()); @@ -71,7 +127,7 @@ public void convertWhenGrantRequestValidThenConverts() { .isEqualTo(AuthorizationGrantType.PASSWORD.getValue()); assertThat(formParameters.getFirst(OAuth2ParameterNames.USERNAME)).isEqualTo("user1"); assertThat(formParameters.getFirst(OAuth2ParameterNames.PASSWORD)).isEqualTo("password"); - assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("read write"); + assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).contains(clientRegistration.getScopes()); } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverterTests.java index 60c53bdeda5..8d8d2665f02 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/OAuth2RefreshTokenGrantRequestEntityConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,9 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; +import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; @@ -28,6 +30,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.core.TestOAuth2AccessTokens; import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; @@ -35,6 +38,10 @@ import org.springframework.util.MultiValueMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; /** * Tests for {@link OAuth2RefreshTokenGrantRequestEntityConverter}. @@ -43,23 +50,82 @@ */ public class OAuth2RefreshTokenGrantRequestEntityConverterTests { - private OAuth2RefreshTokenGrantRequestEntityConverter converter = new OAuth2RefreshTokenGrantRequestEntityConverter(); - - private OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest; + private OAuth2RefreshTokenGrantRequestEntityConverter converter; @Before public void setup() { - this.refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest( - TestClientRegistrations.clientRegistration().build(), TestOAuth2AccessTokens.scopes("read", "write"), - TestOAuth2RefreshTokens.refreshToken(), Collections.singleton("read")); + this.converter = new OAuth2RefreshTokenGrantRequestEntityConverter(); + } + + @Test + public void setHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void addHeadersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addHeadersConverter(null)) + .withMessage("headersConverter cannot be null"); + } + + @Test + public void setParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void addParametersConverterWhenNullThenThrowIllegalArgumentException() { + assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addParametersConverter(null)) + .withMessage("parametersConverter cannot be null"); + } + + @Test + public void convertWhenHeadersConverterSetThenCalled() { + Converter<OAuth2RefreshTokenGrantRequest, HttpHeaders> headersConverter1 = mock(Converter.class); + this.converter.setHeadersConverter(headersConverter1); + Converter<OAuth2RefreshTokenGrantRequest, HttpHeaders> headersConverter2 = mock(Converter.class); + this.converter.addHeadersConverter(headersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("read", "write"); + OAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken(); + OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, + accessToken, refreshToken); + this.converter.convert(refreshTokenGrantRequest); + InOrder inOrder = inOrder(headersConverter1, headersConverter2); + inOrder.verify(headersConverter1).convert(any(OAuth2RefreshTokenGrantRequest.class)); + inOrder.verify(headersConverter2).convert(any(OAuth2RefreshTokenGrantRequest.class)); + } + + @Test + public void convertWhenParametersConverterSetThenCalled() { + Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> parametersConverter1 = mock( + Converter.class); + this.converter.setParametersConverter(parametersConverter1); + Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> parametersConverter2 = mock( + Converter.class); + this.converter.addParametersConverter(parametersConverter2); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("read", "write"); + OAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken(); + OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, + accessToken, refreshToken); + this.converter.convert(refreshTokenGrantRequest); + InOrder inOrder = inOrder(parametersConverter1, parametersConverter2); + inOrder.verify(parametersConverter1).convert(any(OAuth2RefreshTokenGrantRequest.class)); + inOrder.verify(parametersConverter2).convert(any(OAuth2RefreshTokenGrantRequest.class)); } @SuppressWarnings("unchecked") @Test public void convertWhenGrantRequestValidThenConverts() { - RequestEntity<?> requestEntity = this.converter.convert(this.refreshTokenGrantRequest); - ClientRegistration clientRegistration = this.refreshTokenGrantRequest.getClientRegistration(); - OAuth2RefreshToken refreshToken = this.refreshTokenGrantRequest.getRefreshToken(); + ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build(); + OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("read", "write"); + OAuth2RefreshToken refreshToken = TestOAuth2RefreshTokens.refreshToken(); + OAuth2RefreshTokenGrantRequest refreshTokenGrantRequest = new OAuth2RefreshTokenGrantRequest(clientRegistration, + accessToken, refreshToken, Collections.singleton("read")); + RequestEntity<?> requestEntity = this.converter.convert(refreshTokenGrantRequest); assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST); assertThat(requestEntity.getUrl().toASCIIString()) .isEqualTo(clientRegistration.getProviderDetails().getTokenUri()); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJoseHeaders.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJoseHeaders.java new file mode 100644 index 00000000000..ffda877694f --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJoseHeaders.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * @author Joe Grandja + */ +final class TestJoseHeaders { + + private TestJoseHeaders() { + } + + static JoseHeader.Builder joseHeader() { + return joseHeader(SignatureAlgorithm.RS256); + } + + static JoseHeader.Builder joseHeader(SignatureAlgorithm signatureAlgorithm) { + // @formatter:off + return JoseHeader.withAlgorithm(signatureAlgorithm) + .jwkSetUrl("https://provider.com/oauth2/jwks") + .jwk(rsaJwk()) + .keyId("keyId") + .x509Url("https://provider.com/oauth2/x509") + .x509CertificateChain(Arrays.asList("x509Cert1", "x509Cert2")) + .x509SHA1Thumbprint("x509SHA1Thumbprint") + .x509SHA256Thumbprint("x509SHA256Thumbprint") + .type("JWT") + .contentType("jwt-content-type") + .header("custom-header-name", "custom-header-value"); + // @formatter:on + } + + private static Map<String, Object> rsaJwk() { + Map<String, Object> rsaJwk = new HashMap<>(); + rsaJwk.put("kty", "RSA"); + rsaJwk.put("n", "modulus"); + rsaJwk.put("e", "exponent"); + return rsaJwk; + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJwtClaimsSets.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJwtClaimsSets.java new file mode 100644 index 00000000000..1b311979457 --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/TestJwtClaimsSets.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.client.endpoint; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; + +/* + * NOTE: + * This originated in gh-9208 (JwtEncoder), + * which is required to realize the feature in gh-8175 (JWT Client Authentication). + * However, we decided not to merge gh-9208 as part of the 5.5.0 release + * and instead packaged it up privately with the gh-8175 feature. + * We MAY merge gh-9208 in a later release but that is yet to be determined. + * + * gh-9208 Introduce JwtEncoder + * https://github.com/spring-projects/spring-security/pull/9208 + * + * gh-8175 Support JWT for Client Authentication + * https://github.com/spring-projects/spring-security/issues/8175 + */ + +/** + * @author Joe Grandja + */ +final class TestJwtClaimsSets { + + private TestJwtClaimsSets() { + } + + static JwtClaimsSet.Builder jwtClaimsSet() { + String issuer = "https://provider.com"; + Instant issuedAt = Instant.now(); + Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); + + // @formatter:off + return JwtClaimsSet.builder() + .issuer(issuer) + .subject("subject") + .audience(Collections.singletonList("client-1")) + .issuedAt(issuedAt) + .notBefore(issuedAt) + .expiresAt(expiresAt) + .id("jti") + .claim("custom-claim-name", "custom-claim-value"); + // @formatter:on + } + +} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java index e394254109a..40afdc93435 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.DecimalUtils; import org.junit.Before; @@ -64,6 +65,9 @@ public void setup() { ClassLoader loader = getClass().getClassLoader(); this.mapper = new ObjectMapper(); this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); + + // see https://github.com/FasterXML/jackson-databind/issues/3052 for details + this.mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); } @Test diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java index e905c1a9e90..df758645c79 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -144,6 +145,24 @@ public void loadUserWhenUserInfoSuccessResponseThenReturnUser() { assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes()); } + // gh-9336 + @Test + public void loadUserWhenUserInfo201CreatedResponseThenReturnUser() { + // @formatter:off + String userInfoResponse = "{\n" + + " \"id\": \"user1\",\n" + + " \"first-name\": \"first\",\n" + + " \"last-name\": \"last\",\n" + + " \"middle-name\": \"middle\",\n" + + " \"address\": \"address\",\n" + + " \"email\": \"user1@example.com\"\n" + + "}\n"; + // @formatter:on + this.server.enqueue(new MockResponse().setResponseCode(201) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(userInfoResponse)); + assertThatNoException().isThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block()); + } + // gh-5500 @Test public void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception { diff --git a/oauth2/oauth2-core/spring-security-oauth2-core.gradle b/oauth2/oauth2-core/spring-security-oauth2-core.gradle index 0ee8e931c8e..8e9ff3076d6 100644 --- a/oauth2/oauth2-core/spring-security-oauth2-core.gradle +++ b/oauth2/oauth2-core/spring-security-oauth2-core.gradle @@ -1,13 +1,14 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile springCoreDependency - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api springCoreDependency + api 'org.springframework:spring-web' optional 'com.fasterxml.jackson.core:jackson-databind' optional 'com.nimbusds:oauth2-oidc-sdk' optional 'org.springframework:spring-webflux' - testCompile powerMock2Dependencies + testImplementation powerMock2Dependencies } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java index c4a93a93e6c..7193f90eb63 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +58,17 @@ public final class ClientAuthenticationMethod implements Serializable { public static final ClientAuthenticationMethod CLIENT_SECRET_POST = new ClientAuthenticationMethod( "client_secret_post"); + /** + * @since 5.5 + */ + public static final ClientAuthenticationMethod CLIENT_SECRET_JWT = new ClientAuthenticationMethod( + "client_secret_jwt"); + + /** + * @since 5.5 + */ + public static final ClientAuthenticationMethod PRIVATE_KEY_JWT = new ClientAuthenticationMethod("private_key_jwt"); + /** * @since 5.2 */ diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2ParameterNames.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2ParameterNames.java index e77cd847f44..464f6b1493c 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2ParameterNames.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/OAuth2ParameterNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,18 @@ public interface OAuth2ParameterNames { */ String CLIENT_SECRET = "client_secret"; + /** + * {@code client_assertion_type} - used in Access Token Request. + * @since 5.5 + */ + String CLIENT_ASSERTION_TYPE = "client_assertion_type"; + + /** + * {@code client_assertion} - used in Access Token Request. + * @since 5.5 + */ + String CLIENT_ASSERTION = "client_assertion"; + /** * {@code redirect_uri} - used in Authorization Request and Access Token Request. */ diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java index 31fb080f503..a8ad76cd00f 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.util.Assert; /** @@ -65,13 +66,14 @@ public class DefaultOAuth2User implements OAuth2User, Serializable { */ public DefaultOAuth2User(Collection<? extends GrantedAuthority> authorities, Map<String, Object> attributes, String nameAttributeKey) { - Assert.notEmpty(authorities, "authorities cannot be empty"); Assert.notEmpty(attributes, "attributes cannot be empty"); Assert.hasText(nameAttributeKey, "nameAttributeKey cannot be empty"); if (!attributes.containsKey(nameAttributeKey)) { throw new IllegalArgumentException("Missing attribute '" + nameAttributeKey + "' in attributes"); } - this.authorities = Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities))); + this.authorities = (authorities != null) + ? Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities))) + : Collections.unmodifiableSet(new LinkedHashSet<>(AuthorityUtils.NO_AUTHORITIES)); this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes)); this.nameAttributeKey = nameAttributeKey; } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClientAuthenticationMethodTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClientAuthenticationMethodTests.java index 7a1f2be4f8c..f008049170d 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClientAuthenticationMethodTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/ClientAuthenticationMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,16 @@ public void getValueWhenAuthenticationMethodClientSecretPostThenReturnClientSecr assertThat(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()).isEqualTo("client_secret_post"); } + @Test + public void getValueWhenAuthenticationMethodClientSecretJwtThenReturnClientSecretJwt() { + assertThat(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()).isEqualTo("client_secret_jwt"); + } + + @Test + public void getValueWhenAuthenticationMethodPrivateKeyJwtThenReturnPrivateKeyJwt() { + assertThat(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()).isEqualTo("private_key_jwt"); + } + @Test public void getValueWhenAuthenticationMethodNoneThenReturnNone() { assertThat(ClientAuthenticationMethod.NONE.getValue()).isEqualTo("none"); diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java index a7f21a50986..a928dc0fbce 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.junit.Test; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; import org.springframework.security.oauth2.core.oidc.OidcIdToken; @@ -66,11 +67,6 @@ public class DefaultOidcUserTests { private static final OidcUserInfo USER_INFO = new OidcUserInfo(USER_INFO_CLAIMS); - @Test - public void constructorWhenAuthoritiesIsNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> new DefaultOidcUser(null, ID_TOKEN)); - } - @Test public void constructorWhenIdTokenIsNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException().isThrownBy(() -> new DefaultOidcUser(AUTHORITIES, null)); @@ -81,6 +77,26 @@ public void constructorWhenNameAttributeKeyInvalidThenThrowIllegalArgumentExcept assertThatIllegalArgumentException().isThrownBy(() -> new DefaultOidcUser(AUTHORITIES, ID_TOKEN, "invalid")); } + @Test + public void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() { + DefaultOidcUser user = new DefaultOidcUser(null, ID_TOKEN); + assertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB); + assertThat(user.getIdToken()).isEqualTo(ID_TOKEN); + assertThat(user.getName()).isEqualTo(SUBJECT); + assertThat(user.getAuthorities()).isEmpty(); + assertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB); + } + + @Test + public void constructorWhenAuthoritiesIsEmptyThenCreated() { + DefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, ID_TOKEN); + assertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB); + assertThat(user.getIdToken()).isEqualTo(ID_TOKEN); + assertThat(user.getName()).isEqualTo(SUBJECT); + assertThat(user.getAuthorities()).isEmpty(); + assertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB); + } + @Test public void constructorWhenAuthoritiesIdTokenProvidedThenCreated() { DefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN); diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java index 9d0e5abce13..5ba71e4fb7d 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,18 +47,6 @@ public class DefaultOAuth2UserTests { private static final Map<String, Object> ATTRIBUTES = Collections.singletonMap(ATTRIBUTE_NAME_KEY, USERNAME); - @Test - public void constructorWhenAuthoritiesIsNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new DefaultOAuth2User(null, ATTRIBUTES, ATTRIBUTE_NAME_KEY)); - } - - @Test - public void constructorWhenAuthoritiesIsEmptyThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new DefaultOAuth2User(Collections.emptySet(), ATTRIBUTES, ATTRIBUTE_NAME_KEY)); - } - @Test public void constructorWhenAttributesIsNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() @@ -82,6 +70,22 @@ public void constructorWhenNameAttributeKeyIsInvalidThenThrowIllegalArgumentExce .isThrownBy(() -> new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, "invalid")); } + @Test + public void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() { + DefaultOAuth2User user = new DefaultOAuth2User(null, ATTRIBUTES, ATTRIBUTE_NAME_KEY); + assertThat(user.getName()).isEqualTo(USERNAME); + assertThat(user.getAuthorities()).isEmpty(); + assertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY); + } + + @Test + public void constructorWhenAuthoritiesIsEmptyThenCreated() { + DefaultOAuth2User user = new DefaultOAuth2User(Collections.emptySet(), ATTRIBUTES, ATTRIBUTE_NAME_KEY); + assertThat(user.getName()).isEqualTo(USERNAME); + assertThat(user.getAuthorities()).isEmpty(); + assertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY); + } + @Test public void constructorWhenAllParametersProvidedAndValidThenCreated() { DefaultOAuth2User user = new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, ATTRIBUTE_NAME_KEY); diff --git a/oauth2/oauth2-jose/spring-security-oauth2-jose.gradle b/oauth2/oauth2-jose/spring-security-oauth2-jose.gradle index ff521ca88e1..43a4d5e187a 100644 --- a/oauth2/oauth2-jose/spring-security-oauth2-jose.gradle +++ b/oauth2/oauth2-jose/spring-security-oauth2-jose.gradle @@ -1,16 +1,17 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-oauth2-core') - compile springCoreDependency - compile 'com.nimbusds:nimbus-jose-jwt' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-oauth2-core') + api springCoreDependency + api 'com.nimbusds:nimbus-jose-jwt' optional 'io.projectreactor:reactor-core' optional 'org.springframework:spring-webflux' - testCompile powerMock2Dependencies - testCompile 'com.squareup.okhttp3:mockwebserver' - testCompile 'io.projectreactor.netty:reactor-netty' - testCompile 'com.fasterxml.jackson.core:jackson-databind' + testImplementation powerMock2Dependencies + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation 'io.projectreactor.netty:reactor-netty' + testImplementation 'com.fasterxml.jackson.core:jackson-databind' } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java new file mode 100644 index 00000000000..412adbfd4d2 --- /dev/null +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.jose; + +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import javax.crypto.SecretKey; + +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.OctetSequenceKey; +import com.nimbusds.jose.jwk.RSAKey; + +/** + * @author Joe Grandja + */ +public final class TestJwks { + + // @formatter:off + public static final RSAKey DEFAULT_RSA_JWK = + jwk( + TestKeys.DEFAULT_PUBLIC_KEY, + TestKeys.DEFAULT_PRIVATE_KEY + ).build(); + // @formatter:on + + // @formatter:off + public static final ECKey DEFAULT_EC_JWK = + jwk( + (ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(), + (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate() + ).build(); + // @formatter:on + + // @formatter:off + public static final OctetSequenceKey DEFAULT_SECRET_JWK = + jwk( + TestKeys.DEFAULT_SECRET_KEY + ).build(); + // @formatter:on + + private TestJwks() { + } + + public static RSAKey.Builder jwk(RSAPublicKey publicKey, RSAPrivateKey privateKey) { + // @formatter:off + return new RSAKey.Builder(publicKey) + .privateKey(privateKey) + .keyID("rsa-jwk-kid"); + // @formatter:on + } + + public static ECKey.Builder jwk(ECPublicKey publicKey, ECPrivateKey privateKey) { + // @formatter:off + Curve curve = Curve.forECParameterSpec(publicKey.getParams()); + return new ECKey.Builder(curve, publicKey) + .privateKey(privateKey) + .keyID("ec-jwk-kid"); + // @formatter:on + } + + public static OctetSequenceKey.Builder jwk(SecretKey secretKey) { + // @formatter:off + return new OctetSequenceKey.Builder(secretKey) + .keyID("secret-jwk-kid"); + // @formatter:on + } + +} diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java index 7a2b7fb70db..3b11b4504e1 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jose/TestKeys.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,17 @@ package org.springframework.security.oauth2.jose; +import java.math.BigInteger; import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @@ -109,6 +116,34 @@ public final class TestKeys { } } + public static final KeyPair DEFAULT_RSA_KEY_PAIR = new KeyPair(DEFAULT_PUBLIC_KEY, DEFAULT_PRIVATE_KEY); + + public static final KeyPair DEFAULT_EC_KEY_PAIR = generateEcKeyPair(); + + static KeyPair generateEcKeyPair() { + EllipticCurve ellipticCurve = new EllipticCurve( + new ECFieldFp(new BigInteger( + "115792089210356248762697446949407573530086143415290314195533631308867097853951")), + new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"), + new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291")); + ECPoint ecPoint = new ECPoint( + new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"), + new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109")); + ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, ecPoint, + new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"), 1); + + KeyPair keyPair; + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); + keyPairGenerator.initialize(ecParameterSpec); + keyPair = keyPairGenerator.generateKeyPair(); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } + return keyPair; + } + private TestKeys() { } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java index 31de6e4c23e..31b3c0ace98 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtilsTests.java @@ -38,8 +38,8 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.mock; -import static org.mockito.BDDMockito.when; public class JwtDecoderProviderConfigurationUtilsTests { @@ -48,7 +48,7 @@ public void getSignatureAlgorithmsWhenJwkSetSpecifiesAlgorithmThenUses() throws JWKSource<SecurityContext> jwkSource = mock(JWKSource.class); RSAKey key = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.SIGNATURE) .algorithm(JWSAlgorithm.RS384).build(); - when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Collections.singletonList(key)); + given(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Collections.singletonList(key)); Set<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource); assertThat(algorithms).containsOnly(SignatureAlgorithm.RS384); } @@ -56,7 +56,7 @@ public void getSignatureAlgorithmsWhenJwkSetSpecifiesAlgorithmThenUses() throws @Test public void getSignatureAlgorithmsWhenJwkSetIsEmptyThenIllegalArgumentException() throws Exception { JWKSource<SecurityContext> jwkSource = mock(JWKSource.class); - when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Collections.emptyList()); + given(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Collections.emptyList()); assertThatIllegalArgumentException() .isThrownBy(() -> JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource)); } @@ -68,7 +68,7 @@ public void getSignatureAlgorithmsWhenJwkSetSpecifiesFamilyThenUses() throws Exc ECKey ecKey = new ECKey.Builder(Curve.P_256, new Base64URL("3l2Da_flYc-AuUTm2QzxgyvJxYM_2TeB9DMlwz7j1PE"), new Base64URL("-kjT7Wrfhwsi9SG6H4UXiyUiVE9GHCLauslksZ3-_t0")).keyUse(KeyUse.SIGNATURE).build(); RSAKey rsaKey = new RSAKey.Builder(TestKeys.DEFAULT_PUBLIC_KEY).keyUse(KeyUse.ENCRYPTION).build(); - when(jwkSource.get(any(JWKSelector.class), isNull())).thenReturn(Arrays.asList(ecKey, rsaKey)); + given(jwkSource.get(any(JWKSelector.class), isNull())).willReturn(Arrays.asList(ecKey, rsaKey)); Set<SignatureAlgorithm> algorithms = JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource); assertThat(algorithms).contains(SignatureAlgorithm.ES256, SignatureAlgorithm.ES384, SignatureAlgorithm.ES512); } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java index bdbf96e66ec..1e65c7728c7 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -138,7 +138,7 @@ public void decodeWhenInvalidUrl() { // @formatter:off assertThatIllegalStateException() .isThrownBy(() -> this.decoder.decode(this.messageReadToken).block()) - .withRootCauseInstanceOf(UnknownHostException.class); + .withStackTraceContaining(UnknownHostException.class.getName()); // @formatter:on } diff --git a/oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle b/oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle index 3cfcaebefcc..d01583380f2 100644 --- a/oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle +++ b/oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle @@ -1,10 +1,11 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-oauth2-core') - compile project(':spring-security-web') - compile springCoreDependency + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-oauth2-core') + api project(':spring-security-web') + api springCoreDependency optional project(':spring-security-oauth2-jose') optional 'com.nimbusds:oauth2-oidc-sdk' @@ -13,9 +14,9 @@ dependencies { provided 'javax.servlet:javax.servlet-api' - testCompile project(path: ':spring-security-oauth2-jose', configuration: 'tests') - testCompile 'com.squareup.okhttp3:mockwebserver' - testCompile 'com.fasterxml.jackson.core:jackson-databind' - testCompile 'io.projectreactor.netty:reactor-netty' - testCompile 'io.projectreactor:reactor-test' + testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests') + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation 'com.fasterxml.jackson.core:jackson-databind' + testImplementation 'io.projectreactor.netty:reactor-netty' + testImplementation 'io.projectreactor:reactor-test' } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java new file mode 100644 index 00000000000..389fdb8f9b6 --- /dev/null +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.server.resource.authentication; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.authentication.AuthenticationDetailsSource; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; +import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.Assert; + +/** + * Converts from a HttpServletRequest to {@link BearerTokenAuthenticationToken} that can + * be authenticated. Null authentication possible if there was no Authorization header + * with Bearer Token. + * + * @author Jeongjin Kim + * @since 5.5 + */ +public final class BearerTokenAuthenticationConverter implements AuthenticationConverter { + + private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver(); + + private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); + + @Override + public BearerTokenAuthenticationToken convert(HttpServletRequest request) { + String token = this.bearerTokenResolver.resolve(request); + + if (token == null) { + return null; + } + + BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token); + authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + return authenticationRequest; + } + + /** + * Set the {@link BearerTokenResolver} to use. Defaults to + * {@link DefaultBearerTokenResolver}. + * @param bearerTokenResolver the {@code BearerTokenResolver} to use + */ + public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) { + Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); + this.bearerTokenResolver = bearerTokenResolver; + } + + /** + * Set the {@link AuthenticationDetailsSource} to use. Defaults to + * {@link WebAuthenticationDetailsSource}. + * @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use + */ + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) { + Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); + this.authenticationDetailsSource = authenticationDetailsSource; + } + +} diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java index 02e1d42d4bf..3c709f40e7f 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,19 +24,19 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.core.log.LogMessage; -import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManagerResolver; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; @@ -60,14 +60,16 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver; - private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); - - private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver(); - private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint(); - private AuthenticationFailureHandler authenticationFailureHandler = (request, response, - exception) -> this.authenticationEntryPoint.commence(request, response, exception); + private AuthenticationFailureHandler authenticationFailureHandler = (request, response, exception) -> { + if (exception instanceof AuthenticationServiceException) { + throw exception; + } + this.authenticationEntryPoint.commence(request, response, exception); + }; + + private AuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter(); /** * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s) @@ -101,22 +103,21 @@ public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManag @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String token; + Authentication authenticationRequest; try { - token = this.bearerTokenResolver.resolve(request); + authenticationRequest = this.authenticationConverter.convert(request); } - catch (OAuth2AuthenticationException invalid) { + catch (AuthenticationException invalid) { this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid); this.authenticationEntryPoint.commence(request, response, invalid); return; } - if (token == null) { + if (authenticationRequest == null) { this.logger.trace("Did not process request since did not find bearer token"); filterChain.doFilter(request, response); return; } - BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token); - authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + try { AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request); Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest); @@ -139,10 +140,28 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse * Set the {@link BearerTokenResolver} to use. Defaults to * {@link DefaultBearerTokenResolver}. * @param bearerTokenResolver the {@code BearerTokenResolver} to use + * @deprecated Instead, use {@link BearerTokenAuthenticationConverter} explicitly + * @see BearerTokenAuthenticationConverter */ + @Deprecated public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) { Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); - this.bearerTokenResolver = bearerTokenResolver; + Assert.isTrue(this.authenticationConverter instanceof BearerTokenAuthenticationConverter, + "bearerTokenResolver and authenticationConverter cannot both be customized in this filter. " + + "Since you've customized the authenticationConverter, " + + "please consider configuring the bearerTokenResolver there."); + ((BearerTokenAuthenticationConverter) this.authenticationConverter).setBearerTokenResolver(bearerTokenResolver); + } + + /** + * Set the {@link AuthenticationConverter} to use. Defaults to + * {@link BearerTokenAuthenticationConverter}. + * @param authenticationConverter the {@code AuthenticationConverter} to use + * @since 5.5 + */ + public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { + Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); + this.authenticationConverter = authenticationConverter; } /** diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java new file mode 100644 index 00000000000..8f46a381dd6 --- /dev/null +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.oauth2.server.resource.authentication; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.http.HttpHeaders; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link BearerTokenAuthenticationConverter} + * + * @author Jeongjin Kim + * @since 5.5 + */ +@RunWith(MockitoJUnitRunner.class) +public class BearerTokenAuthenticationConverterTests { + + private BearerTokenAuthenticationConverter converter; + + @Before + public void setup() { + this.converter = new BearerTokenAuthenticationConverter(); + } + + @Test + public void setBearerTokenResolverWithNullThenThrowsException() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.converter.setBearerTokenResolver(null)) + .withMessageContaining("bearerTokenResolver cannot be null"); + // @formatter:on + } + + @Test + public void setAuthenticationDetailsSourceWithNullThenThrowsException() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.converter.setAuthenticationDetailsSource(null)) + .withMessageContaining("authenticationDetailsSource cannot be null"); + // @formatter:on + } + + @Test + public void convertWhenNoBearerTokenHeaderThenNull() { + HttpServletRequest request = mock(HttpServletRequest.class); + + BearerTokenAuthenticationToken convert = this.converter.convert(request); + + assertThat(convert).isNull(); + } + + @Test + public void convertWhenBearerTokenThenBearerTokenAuthenticationToken() { + HttpServletRequest request = mock(HttpServletRequest.class); + given(request.getHeader(HttpHeaders.AUTHORIZATION)).willReturn("Bearer token"); + + BearerTokenAuthenticationToken token = this.converter.convert(request); + + assertThat(token.getToken()).isEqualTo("token"); + } + +} diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java index f691c36aa69..81ee467720b 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java @@ -47,9 +47,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.BDDMockito.any; +import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.verify; -import static org.mockito.BDDMockito.when; /** * Tests for {@link JwtIssuerReactiveAuthenticationManagerResolver} @@ -129,7 +129,7 @@ public void resolveWhenUsingUntrustedIssuerThenException() { public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() { Authentication token = withBearerToken(this.jwt); ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class); - when(authenticationManager.authenticate(token)).thenReturn(Mono.empty()); + given(authenticationManager.authenticate(token)).willReturn(Mono.empty()); JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver( (issuer) -> Mono.just(authenticationManager)); authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block(); @@ -147,7 +147,7 @@ public void resolveWhenUsingExternalSourceThenRespondsToChanges() { .flatMap((manager) -> manager.authenticate(token)).block()) .withMessageContaining("Invalid issuer"); ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class); - when(authenticationManager.authenticate(token)).thenReturn(Mono.empty()); + given(authenticationManager.authenticate(token)).willReturn(Mono.empty()); authenticationManagers.put("trusted", authenticationManager); authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block(); verify(authenticationManager).authenticate(token); diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java index 90a04ac322b..af4b3503703 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManagerResolver; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; import org.springframework.security.oauth2.server.resource.BearerTokenError; @@ -42,6 +43,7 @@ import org.springframework.security.web.authentication.AuthenticationFailureHandler; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -154,6 +156,17 @@ public void doFilterWhenAuthenticationFailsWithCustomHandlerThenPropagatesError( verify(this.authenticationFailureHandler).onAuthenticationFailure(this.request, this.response, exception); } + @Test + public void doFilterWhenAuthenticationServiceExceptionThenRethrows() { + AuthenticationServiceException exception = new AuthenticationServiceException("message"); + given(this.bearerTokenResolver.resolve(this.request)).willReturn("token"); + given(this.authenticationManager.authenticate(any())).willThrow(exception); + BearerTokenAuthenticationFilter filter = addMocks( + new BearerTokenAuthenticationFilter(this.authenticationManager)); + assertThatExceptionOfType(AuthenticationServiceException.class) + .isThrownBy(() -> filter.doFilter(this.request, this.response, this.filterChain)); + } + @Test public void setAuthenticationEntryPointWhenNullThenThrowsException() { BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager); @@ -174,6 +187,16 @@ public void setBearerTokenResolverWhenNullThenThrowsException() { // @formatter:on } + @Test + public void setAuthenticationConverterWhenNullThenThrowsException() { + // @formatter:off + BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager); + assertThatIllegalArgumentException() + .isThrownBy(() -> filter.setAuthenticationConverter(null)) + .withMessageContaining("authenticationConverter cannot be null"); + // @formatter:on + } + @Test public void constructorWhenNullAuthenticationManagerThenThrowsException() { // @formatter:off diff --git a/openid/spring-security-openid.gradle b/openid/spring-security-openid.gradle index 8e3820d1513..82dc1ac6590 100644 --- a/openid/spring-security-openid.gradle +++ b/openid/spring-security-openid.gradle @@ -5,25 +5,26 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-web') - compile('com.google.inject:guice') { + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-web') + api('com.google.inject:guice') { exclude group: 'aopalliance', module: 'aopalliance' } // openid4java has a compile time dep on guice with a group // name which is different from the maven central one. // We use the maven central version here instead. - compile('org.openid4java:openid4java-nodeps') { + api('org.openid4java:openid4java-nodeps') { exclude group: 'com.google.code.guice', module: 'guice' } - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-web' + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-web' provided 'javax.servlet:javax.servlet-api' - runtime 'net.sourceforge.nekohtml:nekohtml' - runtime 'org.apache.httpcomponents:httpclient' + runtimeOnly 'net.sourceforge.nekohtml:nekohtml' + runtimeOnly 'org.apache.httpcomponents:httpclient' } diff --git a/remoting/spring-security-remoting.gradle b/remoting/spring-security-remoting.gradle index f3d8a34249e..3b62b676fc4 100644 --- a/remoting/spring-security-remoting.gradle +++ b/remoting/spring-security-remoting.gradle @@ -1,12 +1,13 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-web' - testCompile project(path: ':spring-security-core', configuration: 'tests') + testImplementation project(path: ':spring-security-core', configuration: 'tests') } diff --git a/rsocket/spring-security-rsocket.gradle b/rsocket/spring-security-rsocket.gradle index fa508c27609..64ec4dcf87e 100644 --- a/rsocket/spring-security-rsocket.gradle +++ b/rsocket/spring-security-rsocket.gradle @@ -1,9 +1,10 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile 'io.rsocket:rsocket-core' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api 'io.rsocket:rsocket-core' optional project(':spring-security-oauth2-resource-server') optional 'org.springframework:spring-messaging' - testCompile 'io.projectreactor:reactor-test' + testImplementation 'io.projectreactor:reactor-test' } diff --git a/saml2/saml2-service-provider/core/saml2-service-provider-core.gradle b/saml2/saml2-service-provider/core/saml2-service-provider-core.gradle new file mode 100644 index 00000000000..a7b98dbba04 --- /dev/null +++ b/saml2/saml2-service-provider/core/saml2-service-provider-core.gradle @@ -0,0 +1,62 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/plugins-release' } + } + dependencies { + classpath 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + } +} + +plugins { + id 'java' + id 'java-library' + id 'io.spring.convention.repository' + id 'io.spring.convention.management-configuration' + id 'io.spring.convention.dependency-set' + id 'io.spring.convention.checkstyle' + id 'io.spring.convention.tests-configuration' + id 'io.spring.convention.integration-test' + id 'propdeps' +} + +configurations { + classesOnlyElements { + canBeConsumed = true + canBeResolved = false + } + sourceElements { + canBeConsumed = true + canBeResolved = false + } + javadocElements { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + classesOnlyElements(compileJava.destinationDir) + sourceSets.main.allSource.srcDirs.forEach({ dir -> + sourceElements(dir) + }) + javadocElements(javadoc.destinationDir) +} + +repositories { + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } +} + +dependencies { + management platform(project(":spring-security-dependencies")) + + api project(':spring-security-core') + api project(':spring-security-web') + + provided("org.opensaml:opensaml-core") + provided("org.opensaml:opensaml-saml-api") + provided("org.opensaml:opensaml-saml-impl") + + provided 'javax.servlet:javax.servlet-api' + + testImplementation 'com.squareup.okhttp3:mockwebserver' +} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/Saml2Exception.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/Saml2Exception.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/Saml2Exception.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/Saml2Exception.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java similarity index 99% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java index a923760be14..0b2645fe985 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/OpenSamlInitializationService.java @@ -52,7 +52,7 @@ * * <pre> * static { - * OpenSamlInitializationService.requireInitialize((registry) -> { + * OpenSamlInitializationService.requireInitialize((registry) -> { * registry.setParserPool(...); * registry.getBuilderFactory().registerBuilder(...); * }); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2Error.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2Error.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2Error.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2Error.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ErrorCodes.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2ErrorCodes.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ErrorCodes.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2ErrorCodes.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResult.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResult.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResult.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResult.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java similarity index 97% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java index 4fde34733cb..a8126d26bde 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/core/Saml2X509Credential.java @@ -85,6 +85,7 @@ public Saml2X509Credential(PrivateKey privateKey, X509Certificate certificate, S /** * Create a {@link Saml2X509Credential} that can be used for encryption. * @param certificate the certificate to use for encryption + * @return an encrypting {@link Saml2X509Credential} */ public static Saml2X509Credential encryption(X509Certificate certificate) { return new Saml2X509Credential(certificate, Saml2X509Credential.Saml2X509CredentialType.ENCRYPTION); @@ -93,6 +94,7 @@ public static Saml2X509Credential encryption(X509Certificate certificate) { /** * Create a {@link Saml2X509Credential} that can be used for verification. * @param certificate the certificate to use for verification + * @return a verifying {@link Saml2X509Credential} */ public static Saml2X509Credential verification(X509Certificate certificate) { return new Saml2X509Credential(certificate, Saml2X509Credential.Saml2X509CredentialType.VERIFICATION); @@ -102,6 +104,7 @@ public static Saml2X509Credential verification(X509Certificate certificate) { * Create a {@link Saml2X509Credential} that can be used for decryption. * @param privateKey the private key to use for decryption * @param certificate the certificate to use for decryption + * @return an decrypting {@link Saml2X509Credential} */ public static Saml2X509Credential decryption(PrivateKey privateKey, X509Certificate certificate) { return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.DECRYPTION); @@ -111,6 +114,7 @@ public static Saml2X509Credential decryption(PrivateKey privateKey, X509Certific * Create a {@link Saml2X509Credential} that can be used for signing. * @param privateKey the private key to use for signing * @param certificate the certificate to use for signing + * @return a signing {@link Saml2X509Credential} */ public static Saml2X509Credential signing(PrivateKey privateKey, X509Certificate certificate) { return new Saml2X509Credential(privateKey, certificate, Saml2X509Credential.Saml2X509CredentialType.SIGNING); diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/credentials/Saml2X509Credential.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/credentials/Saml2X509Credential.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/credentials/Saml2X509Credential.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/credentials/Saml2X509Credential.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/AbstractSaml2AuthenticationRequest.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/AbstractSaml2AuthenticationRequest.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/AbstractSaml2AuthenticationRequest.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/AbstractSaml2AuthenticationRequest.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipal.java diff --git a/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlDecryptionUtils.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlDecryptionUtils.java new file mode 100644 index 00000000000..e80bd0619b6 --- /dev/null +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlDecryptionUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.EncryptedAssertion; +import org.opensaml.saml.saml2.core.EncryptedAttribute; +import org.opensaml.saml.saml2.core.NameID; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.encryption.Decrypter; +import org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver; +import org.opensaml.security.credential.Credential; +import org.opensaml.security.credential.CredentialSupport; +import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver; +import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver; +import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver; +import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver; +import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver; +import org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver; + +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; + +/** + * Utility methods for decrypting SAML components with OpenSAML + * + * For internal use only. + * + * @author Josh Cummings + */ +final class OpenSamlDecryptionUtils { + + private static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver( + Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(), + new SimpleRetrievalMethodEncryptedKeyResolver())); + + static void decryptResponseElements(Response response, RelyingPartyRegistration registration) { + Decrypter decrypter = decrypter(registration); + for (EncryptedAssertion encryptedAssertion : response.getEncryptedAssertions()) { + try { + Assertion assertion = decrypter.decrypt(encryptedAssertion); + response.getAssertions().add(assertion); + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + } + + static void decryptAssertionElements(Assertion assertion, RelyingPartyRegistration registration) { + Decrypter decrypter = decrypter(registration); + for (AttributeStatement statement : assertion.getAttributeStatements()) { + for (EncryptedAttribute encryptedAttribute : statement.getEncryptedAttributes()) { + try { + Attribute attribute = decrypter.decrypt(encryptedAttribute); + statement.getAttributes().add(attribute); + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + } + if (assertion.getSubject() == null) { + return; + } + if (assertion.getSubject().getEncryptedID() == null) { + return; + } + try { + assertion.getSubject().setNameID((NameID) decrypter.decrypt(assertion.getSubject().getEncryptedID())); + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + + private static Decrypter decrypter(RelyingPartyRegistration registration) { + Collection<Credential> credentials = new ArrayList<>(); + for (Saml2X509Credential key : registration.getDecryptionX509Credentials()) { + Credential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey()); + credentials.add(cred); + } + KeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials); + Decrypter decrypter = new Decrypter(null, resolver, encryptedKeyResolver); + decrypter.setRootInNewDocument(true); + return decrypter; + } + + private OpenSamlDecryptionUtils() { + } + +} diff --git a/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java new file mode 100644 index 00000000000..f177a866b44 --- /dev/null +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlSigningUtils.java @@ -0,0 +1,173 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.core.xml.io.Marshaller; +import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver; +import org.opensaml.security.SecurityException; +import org.opensaml.security.credential.BasicCredential; +import org.opensaml.security.credential.Credential; +import org.opensaml.security.credential.CredentialSupport; +import org.opensaml.security.credential.UsageType; +import org.opensaml.xmlsec.SignatureSigningParameters; +import org.opensaml.xmlsec.SignatureSigningParametersResolver; +import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion; +import org.opensaml.xmlsec.crypto.XMLSigningUtil; +import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration; +import org.opensaml.xmlsec.signature.SignableXMLObject; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.opensaml.xmlsec.signature.support.SignatureSupport; +import org.w3c.dom.Element; + +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.util.Assert; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; + +/** + * Utility methods for signing SAML components with OpenSAML + * + * For internal use only. + * + * @author Josh Cummings + */ +final class OpenSamlSigningUtils { + + static String serialize(XMLObject object) { + try { + Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object); + Element element = marshaller.marshall(object); + return SerializeSupport.nodeToString(element); + } + catch (MarshallingException ex) { + throw new Saml2Exception(ex); + } + } + + static <O extends SignableXMLObject> O sign(O object, RelyingPartyRegistration relyingPartyRegistration) { + SignatureSigningParameters parameters = resolveSigningParameters(relyingPartyRegistration); + try { + SignatureSupport.signObject(object, parameters); + return object; + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + + static QueryParametersPartial sign(RelyingPartyRegistration registration) { + return new QueryParametersPartial(registration); + } + + private static SignatureSigningParameters resolveSigningParameters( + RelyingPartyRegistration relyingPartyRegistration) { + List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration); + List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); + List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256); + String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; + SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver(); + CriteriaSet criteria = new CriteriaSet(); + BasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration(); + signingConfiguration.setSigningCredentials(credentials); + signingConfiguration.setSignatureAlgorithms(algorithms); + signingConfiguration.setSignatureReferenceDigestMethods(digests); + signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization); + criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration)); + try { + SignatureSigningParameters parameters = resolver.resolveSingle(criteria); + Assert.notNull(parameters, "Failed to resolve any signing credential"); + return parameters; + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + + private static List<Credential> resolveSigningCredentials(RelyingPartyRegistration relyingPartyRegistration) { + List<Credential> credentials = new ArrayList<>(); + for (Saml2X509Credential x509Credential : relyingPartyRegistration.getSigningX509Credentials()) { + X509Certificate certificate = x509Credential.getCertificate(); + PrivateKey privateKey = x509Credential.getPrivateKey(); + BasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey); + credential.setEntityId(relyingPartyRegistration.getEntityId()); + credential.setUsageType(UsageType.SIGNING); + credentials.add(credential); + } + return credentials; + } + + static class QueryParametersPartial { + + final RelyingPartyRegistration registration; + + final Map<String, String> components = new LinkedHashMap<>(); + + QueryParametersPartial(RelyingPartyRegistration registration) { + this.registration = registration; + } + + QueryParametersPartial param(String key, String value) { + this.components.put(key, value); + return this; + } + + Map<String, String> parameters() { + SignatureSigningParameters parameters = resolveSigningParameters(this.registration); + Credential credential = parameters.getSigningCredential(); + String algorithmUri = parameters.getSignatureAlgorithm(); + this.components.put("SigAlg", algorithmUri); + UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); + for (Map.Entry<String, String> component : this.components.entrySet()) { + builder.queryParam(component.getKey(), + UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1)); + } + String queryString = builder.build(true).toString().substring(1); + try { + byte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri, + queryString.getBytes(StandardCharsets.UTF_8)); + String b64Signature = Saml2Utils.samlEncode(rawSignature); + this.components.put("Signature", b64Signature); + } + catch (SecurityException ex) { + throw new Saml2Exception(ex); + } + return this.components; + } + + } + + private OpenSamlSigningUtils() { + + } + +} diff --git a/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java new file mode 100644 index 00000000000..00dbfd7af43 --- /dev/null +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlVerificationUtils.java @@ -0,0 +1,217 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import org.opensaml.core.criterion.EntityIdCriterion; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.criterion.ProtocolCriterion; +import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.saml.saml2.core.RequestAbstractType; +import org.opensaml.saml.saml2.core.StatusResponseType; +import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; +import org.opensaml.security.credential.Credential; +import org.opensaml.security.credential.CredentialResolver; +import org.opensaml.security.credential.UsageType; +import org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion; +import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion; +import org.opensaml.security.credential.impl.CollectionCredentialResolver; +import org.opensaml.security.criteria.UsageCriterion; +import org.opensaml.security.x509.BasicX509Credential; +import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; +import org.opensaml.xmlsec.signature.Signature; +import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; +import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine; + +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.web.util.UriUtils; + +/** + * Utility methods for verifying SAML component signatures with OpenSAML + * + * For internal use only. + * + * @author Josh Cummings + */ + +final class OpenSamlVerificationUtils { + + static VerifierPartial verifySignature(StatusResponseType object, RelyingPartyRegistration registration) { + return new VerifierPartial(object, registration); + } + + static VerifierPartial verifySignature(RequestAbstractType object, RelyingPartyRegistration registration) { + return new VerifierPartial(object, registration); + } + + static SignatureTrustEngine trustEngine(RelyingPartyRegistration registration) { + Set<Credential> credentials = new HashSet<>(); + Collection<Saml2X509Credential> keys = registration.getAssertingPartyDetails().getVerificationX509Credentials(); + for (Saml2X509Credential key : keys) { + BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); + cred.setUsageType(UsageType.SIGNING); + cred.setEntityId(registration.getAssertingPartyDetails().getEntityId()); + credentials.add(cred); + } + CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); + return new ExplicitKeySignatureTrustEngine(credentialsResolver, + DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver()); + } + + static class VerifierPartial { + + private final String id; + + private final CriteriaSet criteria; + + private final SignatureTrustEngine trustEngine; + + VerifierPartial(StatusResponseType object, RelyingPartyRegistration registration) { + this.id = object.getID(); + this.criteria = verificationCriteria(object.getIssuer()); + this.trustEngine = trustEngine(registration); + } + + VerifierPartial(RequestAbstractType object, RelyingPartyRegistration registration) { + this.id = object.getID(); + this.criteria = verificationCriteria(object.getIssuer()); + this.trustEngine = trustEngine(registration); + } + + Saml2ResponseValidatorResult redirect(HttpServletRequest request, String objectParameterName) { + RedirectSignature signature = new RedirectSignature(request, objectParameterName); + if (signature.getAlgorithm() == null) { + return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Missing signature algorithm for object [" + this.id + "]")); + } + if (!signature.hasSignature()) { + return Saml2ResponseValidatorResult.failure(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Missing signature for object [" + this.id + "]")); + } + Collection<Saml2Error> errors = new ArrayList<>(); + String algorithmUri = signature.getAlgorithm(); + try { + if (!this.trustEngine.validate(signature.getSignature(), signature.getContent(), algorithmUri, + this.criteria, null)) { + errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Invalid signature for object [" + this.id + "]")); + } + } + catch (Exception ex) { + errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Invalid signature for object [" + this.id + "]: ")); + } + return Saml2ResponseValidatorResult.failure(errors); + } + + Saml2ResponseValidatorResult post(Signature signature) { + Collection<Saml2Error> errors = new ArrayList<>(); + SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); + try { + profileValidator.validate(signature); + } + catch (Exception ex) { + errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Invalid signature for object [" + this.id + "]: ")); + } + + try { + if (!this.trustEngine.validate(signature, this.criteria)) { + errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Invalid signature for object [" + this.id + "]")); + } + } + catch (Exception ex) { + errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, + "Invalid signature for object [" + this.id + "]: ")); + } + + return Saml2ResponseValidatorResult.failure(errors); + } + + private CriteriaSet verificationCriteria(Issuer issuer) { + CriteriaSet criteria = new CriteriaSet(); + criteria.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer.getValue()))); + criteria.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion(SAMLConstants.SAML20P_NS))); + criteria.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING))); + return criteria; + } + + private static class RedirectSignature { + + private final HttpServletRequest request; + + private final String objectParameterName; + + RedirectSignature(HttpServletRequest request, String objectParameterName) { + this.request = request; + this.objectParameterName = objectParameterName; + } + + String getAlgorithm() { + return this.request.getParameter("SigAlg"); + } + + byte[] getContent() { + if (this.request.getParameter("RelayState") != null) { + return String.format("%s=%s&RelayState=%s&SigAlg=%s", this.objectParameterName, + UriUtils.encode(this.request.getParameter(this.objectParameterName), + StandardCharsets.ISO_8859_1), + UriUtils.encode(this.request.getParameter("RelayState"), StandardCharsets.ISO_8859_1), + UriUtils.encode(getAlgorithm(), StandardCharsets.ISO_8859_1)) + .getBytes(StandardCharsets.UTF_8); + } + else { + return String + .format("%s=%s&SigAlg=%s", this.objectParameterName, + UriUtils.encode(this.request.getParameter(this.objectParameterName), + StandardCharsets.ISO_8859_1), + UriUtils.encode(getAlgorithm(), StandardCharsets.ISO_8859_1)) + .getBytes(StandardCharsets.UTF_8); + } + } + + byte[] getSignature() { + return Saml2Utils.samlDecode(this.request.getParameter("Signature")); + } + + boolean hasSignature() { + return this.request.getParameter("Signature") != null; + } + + } + + } + + private OpenSamlVerificationUtils() { + + } + +} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Authentication.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Authentication.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Authentication.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Authentication.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationException.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationException.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationException.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationException.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java similarity index 95% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java index 1cac7d9fb54..8007cbca9db 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequest.java @@ -100,7 +100,8 @@ public List<Saml2X509Credential> getCredentials() { } /** - * A builder for {@link Saml2AuthenticationRequest}. returns a builder object + * A builder for {@link Saml2AuthenticationRequest}. + * @return a {@link Builder} for constructing a {@link Saml2AuthenticationRequest} */ public static Builder builder() { return new Builder(); @@ -109,6 +110,7 @@ public static Builder builder() { /** * A builder for {@link Saml2AuthenticationRequest}. * @param context a context object to copy values from. returns a builder object + * @return a {@link Builder} for constructing a {@link Saml2AuthenticationRequest} */ public static Builder withAuthenticationRequestContext(Saml2AuthenticationRequestContext context) { return new Builder().assertionConsumerServiceUrl(context.getAssertionConsumerServiceUrl()) @@ -148,7 +150,7 @@ public Builder issuer(String issuer) { * request. For example: <code> * Saml2X509Credential credential = ...; * return Saml2AuthenticationRequest.withLocalSpEntityId("id") - * .credentials((c) -> c.add(credential)) + * .credentials((c) -> c.add(credential)) * ... * .build(); * </code> diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestContext.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestContext.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestContext.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestContext.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactory.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactory.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactory.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactory.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Error.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Error.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Error.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Error.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ErrorCodes.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ErrorCodes.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ErrorCodes.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2ErrorCodes.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2PostAuthenticationRequest.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2RedirectAuthenticationRequest.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2Utils.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResolver.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResolver.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/metadata/Saml2MetadataResolver.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/InMemoryRelyingPartyRegistrationRepository.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverter.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverter.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java similarity index 98% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java index 3af9371561b..320bfe35b5f 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java @@ -59,11 +59,11 @@ * RelyingPartyRegistration rp = RelyingPartyRegistration.withRegistrationId(registrationId) * .entityId(relyingPartyEntityId) * .assertionConsumerServiceLocation(assertingConsumerServiceLocation) - * .signingX509Credentials((c) -> c.add(relyingPartySigningCredential)) - * .assertingPartyDetails((details) -> details + * .signingX509Credentials((c) -> c.add(relyingPartySigningCredential)) + * .assertingPartyDetails((details) -> details * .entityId(assertingPartyEntityId)); * .singleSignOnServiceLocation(singleSignOnServiceLocation)) - * .verifyingX509Credentials((c) -> c.add(assertingPartyVerificationCredential)) + * .verifyingX509Credentials((c) -> c.add(assertingPartyVerificationCredential)) * .build(); * </pre> * @@ -857,6 +857,7 @@ public Builder registrationId(String id) { * This value may contain a number of placeholders. They are {@code baseUrl}, * {@code registrationId}, {@code baseScheme}, {@code baseHost}, and * {@code basePort}. + * @param entityId the relying party's EntityID * @return the {@link Builder} for further configuration * @since 5.4 */ @@ -906,7 +907,7 @@ public Builder decryptionX509Credentials(Consumer<Collection<Saml2X509Credential * This value may contain a number of placeholders. They are {@code baseUrl}, * {@code registrationId}, {@code baseScheme}, {@code baseHost}, and * {@code basePort}. - * @param assertionConsumerServiceLocation + * @param assertionConsumerServiceLocation the AssertionConsumerService location * @return the {@link Builder} for further configuration * @since 5.4 */ @@ -923,7 +924,7 @@ public Builder assertionConsumerServiceLocation(String assertionConsumerServiceL * <p> * Equivalent to the value found in <AssertionConsumerService * Binding="..."/> in the relying party's <SPSSODescriptor> - * @param assertionConsumerServiceBinding + * @param assertionConsumerServiceBinding the AssertionConsumerService binding * @return the {@link Builder} for further configuration * @since 5.4 */ @@ -948,7 +949,7 @@ public Builder assertingPartyDetails(Consumer<AssertingPartyDetails.Builder> ass * communication between IDP and SP For example: <code> * Saml2X509Credential credential = ...; * return RelyingPartyRegistration.withRegistrationId("id") - * .credentials((c) -> c.add(credential)) + * .credentials((c) -> c.add(credential)) * ... * .build(); * </code> @@ -1018,6 +1019,7 @@ public Builder idpWebSsoUrl(String url) { * {@code registrationId}, {@code baseScheme}, {@code baseHost}, and * {@code basePort}, for example * {@code {baseUrl}/saml2/service-provider-metadata/{registrationId}} + * @param template the entity id * @return a string containing the entity ID or entity ID template * @deprecated Use {@link #entityId} instead */ diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java similarity index 78% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java index 958f608e7e2..015aa81d96b 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2MessageBinding.java @@ -43,4 +43,19 @@ public String getUrn() { return this.urn; } + /** + * Attempt to resolve the provided algorithm name to a {@code Saml2MessageBinding}. + * @param name the algorithm name + * @return the resolved {@code Saml2MessageBinding}, or {@code null} if not found + * @since 5.5 + */ + public static Saml2MessageBinding from(String name) { + for (Saml2MessageBinding value : values()) { + if (value.getUrn().equals(name)) { + return value; + } + } + return null; + } + } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java similarity index 92% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java index 731c6a3c669..453101489c9 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java @@ -24,6 +24,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.opensaml.core.Version; + import org.springframework.http.MediaType; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory; @@ -39,6 +41,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.HtmlUtils; @@ -88,8 +91,21 @@ public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter public Saml2WebSsoAuthenticationRequestFilter( RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) { this(new DefaultSaml2AuthenticationRequestContextResolver( - new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), - new org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory()); + new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), requestFactory()); + } + + private static Saml2AuthenticationRequestFactory requestFactory() { + String opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory"; + if (Version.getVersion().startsWith("4")) { + opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory"; + } + try { + return (Saml2AuthenticationRequestFactory) ClassUtils.forName(opensamlClassName, null) + .getDeclaredConstructor().newInstance(); + } + catch (Exception ex) { + throw new IllegalStateException(ex); + } } /** @@ -97,6 +113,8 @@ public Saml2WebSsoAuthenticationRequestFilter( * parameters * @param authenticationRequestContextResolver a strategy for formulating a * {@link Saml2AuthenticationRequestContext} + * @param authenticationRequestFactory strategy for formulating a + * <saml2:AuthnRequest> * @since 5.4 */ public Saml2WebSsoAuthenticationRequestFilter( diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolver.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolver.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolver.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolver.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolver.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolver.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationRequestContextResolver.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationRequestContextResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationRequestContextResolver.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationRequestContextResolver.java diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java similarity index 81% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java index bcce7e6fa87..274f78617fb 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.springframework.security.saml2.provider.service.web; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.zip.Inflater; import java.util.zip.InflaterOutputStream; @@ -28,7 +27,9 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpMethod; -import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.web.authentication.AuthenticationConverter; @@ -82,8 +83,14 @@ private String inflateIfRequired(HttpServletRequest request, byte[] b) { return new String(b, StandardCharsets.UTF_8); } - private byte[] samlDecode(String s) { - return BASE64.decode(s); + private byte[] samlDecode(String base64EncodedPayload) { + try { + return BASE64.decode(base64EncodedPayload); + } + catch (Exception ex) { + throw new Saml2AuthenticationException( + new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, "Failed to decode SAMLResponse"), ex); + } } private String samlInflate(byte[] b) { @@ -92,10 +99,11 @@ private String samlInflate(byte[] b) { InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(out, new Inflater(true)); inflaterOutputStream.write(b); inflaterOutputStream.finish(); - return new String(out.toByteArray(), StandardCharsets.UTF_8); + return out.toString(StandardCharsets.UTF_8.name()); } - catch (IOException ex) { - throw new Saml2Exception("Unable to inflate string", ex); + catch (Exception ex) { + throw new Saml2AuthenticationException( + new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, "Unable to inflate string"), ex); } } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java similarity index 73% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java rename to saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java index 9e328cb6c3d..57ec493bc8b 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java +++ b/saml2/saml2-service-provider/core/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.springframework.security.saml2.provider.service.web; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -42,10 +44,14 @@ */ public final class Saml2MetadataFilter extends OncePerRequestFilter { + public static final String DEFAULT_METADATA_FILE_NAME = "saml-{registrationId}-metadata.xml"; + private final Converter<HttpServletRequest, RelyingPartyRegistration> relyingPartyRegistrationConverter; private final Saml2MetadataResolver saml2MetadataResolver; + private String metadataFilename = DEFAULT_METADATA_FILE_NAME; + private RequestMatcher requestMatcher = new AntPathRequestMatcher( "/saml2/service-provider-metadata/{registrationId}"); @@ -78,8 +84,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void writeMetadataToResponse(HttpServletResponse response, String registrationId, String metadata) throws IOException { response.setContentType(MediaType.APPLICATION_XML_VALUE); - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, - "attachment; filename=\"saml-" + registrationId + "-metadata.xml\""); + String fileName = this.metadataFilename.replace("{registrationId}", registrationId); + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); + String format = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format(format, fileName, encodedFileName)); response.setContentLength(metadata.length()); response.getWriter().write(metadata); } @@ -87,11 +95,27 @@ private void writeMetadataToResponse(HttpServletResponse response, String regist /** * Set the {@link RequestMatcher} that determines whether this filter should handle * the incoming {@link HttpServletRequest} - * @param requestMatcher + * @param requestMatcher the {@link RequestMatcher} to identify requests for metadata */ public void setRequestMatcher(RequestMatcher requestMatcher) { Assert.notNull(requestMatcher, "requestMatcher cannot be null"); this.requestMatcher = requestMatcher; } + /** + * Sets the metadata filename template containing the {@code {registrationId}} + * template variable. + * + * <p> + * The default value is {@code saml-{registrationId}-metadata.xml} + * @param metadataFilename metadata filename, must contain a {registrationId} + * @since 5.5 + */ + public void setMetadataFilename(String metadataFilename) { + Assert.hasText(metadataFilename, "metadataFilename cannot be empty"); + Assert.isTrue(metadataFilename.contains("{registrationId}"), + "metadataFilename must contain a {registrationId} match variable"); + this.metadataFilename = metadataFilename; + } + } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/OpenSamlInitializationServiceTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/OpenSamlInitializationServiceTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/OpenSamlInitializationServiceTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/OpenSamlInitializationServiceTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResultTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResultTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResultTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2ResponseValidatorResultTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java similarity index 94% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java index a518b911a32..6f5d9e48d06 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java +++ b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ public static String samlInflate(byte[] b) { InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(out, new Inflater(true)); inflaterOutputStream.write(b); inflaterOutputStream.finish(); - return new String(out.toByteArray(), StandardCharsets.UTF_8); + return out.toString(StandardCharsets.UTF_8.name()); } catch (IOException ex) { throw new Saml2Exception("Unable to inflate string", ex); diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2X509CredentialTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2X509CredentialTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/Saml2X509CredentialTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/Saml2X509CredentialTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/TestSaml2X509Credentials.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/TestSaml2X509Credentials.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/core/TestSaml2X509Credentials.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/core/TestSaml2X509Credentials.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/Saml2X509CredentialTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/credentials/Saml2X509CredentialTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/Saml2X509CredentialTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/credentials/Saml2X509CredentialTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/TestSaml2X509Credentials.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/credentials/TestSaml2X509Credentials.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/credentials/TestSaml2X509Credentials.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/credentials/TestSaml2X509Credentials.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactoryTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactoryTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactoryTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationRequestFactoryTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java similarity index 92% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java index 40e1d8bfe14..d7a1fcdc34e 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java +++ b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java @@ -28,21 +28,17 @@ import javax.xml.namespace.QName; import org.apache.xml.security.encryption.XMLCipherParameters; -import org.joda.time.DateTime; -import org.joda.time.Duration; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.core.xml.io.MarshallingException; import org.opensaml.core.xml.schema.XSAny; import org.opensaml.core.xml.schema.XSBoolean; import org.opensaml.core.xml.schema.XSBooleanValue; -import org.opensaml.core.xml.schema.XSDateTime; import org.opensaml.core.xml.schema.XSInteger; import org.opensaml.core.xml.schema.XSString; import org.opensaml.core.xml.schema.XSURI; import org.opensaml.core.xml.schema.impl.XSAnyBuilder; import org.opensaml.core.xml.schema.impl.XSBooleanBuilder; -import org.opensaml.core.xml.schema.impl.XSDateTimeBuilder; import org.opensaml.core.xml.schema.impl.XSIntegerBuilder; import org.opensaml.core.xml.schema.impl.XSStringBuilder; import org.opensaml.core.xml.schema.impl.XSURIBuilder; @@ -114,7 +110,6 @@ static Response response() { static Response response(String destination, String issuerEntityId) { Response response = build(Response.DEFAULT_ELEMENT_NAME); response.setID("R" + UUID.randomUUID().toString()); - response.setIssueInstant(DateTime.now()); response.setVersion(SAMLVersion.VERSION_20); response.setID("_" + UUID.randomUUID().toString()); response.setDestination(destination); @@ -141,9 +136,7 @@ static Assertion assertion() { static Assertion assertion(String username, String issuerEntityId, String recipientEntityId, String recipientUri) { Assertion assertion = build(Assertion.DEFAULT_ELEMENT_NAME); assertion.setID("A" + UUID.randomUUID().toString()); - assertion.setIssueInstant(DateTime.now()); assertion.setVersion(SAMLVersion.VERSION_20); - assertion.setIssueInstant(DateTime.now()); assertion.setIssuer(issuer(issuerEntityId)); assertion.setSubject(subject(username)); assertion.setConditions(conditions()); @@ -183,16 +176,11 @@ static SubjectConfirmation subjectConfirmation() { static SubjectConfirmationData subjectConfirmationData(String recipient) { SubjectConfirmationData subject = build(SubjectConfirmationData.DEFAULT_ELEMENT_NAME); subject.setRecipient(recipient); - subject.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); - subject.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); return subject; } static Conditions conditions() { - Conditions conditions = build(Conditions.DEFAULT_ELEMENT_NAME); - conditions.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); - conditions.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); - return conditions; + return build(Conditions.DEFAULT_ELEMENT_NAME); } public static AuthnRequest authnRequest() { @@ -338,13 +326,6 @@ static List<AttributeStatement> attributeStatements() { registered.setValue(new XSBooleanValue(true, false)); registeredAttr.getAttributeValues().add(registered); attrStmt2.getAttributes().add(registeredAttr); - Attribute registeredDateAttr = attributeBuilder.buildObject(); - registeredDateAttr.setName("registeredDate"); - XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, - XSDateTime.TYPE_NAME); - registeredDate.setValue(DateTime.parse("1970-01-01T00:00:00Z")); - registeredDateAttr.getAttributeValues().add(registeredDate); - attrStmt2.getAttributes().add(registeredDateAttr); attributeStatements.add(attrStmt2); return attributeStatements; } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2AuthenticationRequestContexts.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2AuthenticationRequestContexts.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2AuthenticationRequestContexts.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestSaml2AuthenticationRequestContexts.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolverTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolverTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolverTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverterTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlAssertingPartyMetadataConverterTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java similarity index 58% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java index 6079de5bcb9..3f0bf6cf843 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java +++ b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java @@ -28,8 +28,10 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.saml2.credentials.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory; import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest; +import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationRequestContexts; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; @@ -68,7 +70,7 @@ public class Saml2WebSsoAuthenticationRequestFilterTests { @Before public void setup() { - this.filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); + this.filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver, this.factory); this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); this.request.setPathInfo("/saml2/authenticate/registration-id"); @@ -81,25 +83,48 @@ public void setup() { @Test public void doFilterWhenNoRelayStateThenRedirectDoesNotContainParameter() throws ServletException, IOException { - given(this.repository.findByRegistrationId("registration-id")).willReturn(this.rpBuilder.build()); + Saml2AuthenticationRequestContext context = authenticationRequestContext().relayState(null).build(); + Saml2RedirectAuthenticationRequest request = redirectAuthenticationRequest(context).build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createRedirectAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); assertThat(this.response.getHeader("Location")).doesNotContain("RelayState=").startsWith(IDP_SSO_URL); } + private static Saml2AuthenticationRequestContext.Builder authenticationRequestContext() { + return TestSaml2AuthenticationRequestContexts.authenticationRequestContext(); + } + + private static Saml2RedirectAuthenticationRequest.Builder redirectAuthenticationRequest( + Saml2AuthenticationRequestContext context) { + return Saml2RedirectAuthenticationRequest.withAuthenticationRequestContext(context).samlRequest("request") + .authenticationRequestUri(IDP_SSO_URL); + } + + private static Saml2PostAuthenticationRequest.Builder postAuthenticationRequest( + Saml2AuthenticationRequestContext context) { + return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context).samlRequest("request") + .authenticationRequestUri(IDP_SSO_URL); + } + @Test public void doFilterWhenRelayStateThenRedirectDoesContainParameter() throws ServletException, IOException { - given(this.repository.findByRegistrationId("registration-id")).willReturn(this.rpBuilder.build()); - this.request.setParameter("RelayState", "my-relay-state"); + Saml2AuthenticationRequestContext context = authenticationRequestContext().build(); + Saml2RedirectAuthenticationRequest request = redirectAuthenticationRequest(context).build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createRedirectAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); - assertThat(this.response.getHeader("Location")).contains("RelayState=my-relay-state").startsWith(IDP_SSO_URL); + assertThat(this.response.getHeader("Location")).contains("RelayState=relayState").startsWith(IDP_SSO_URL); } @Test public void doFilterWhenRelayStateThatRequiresEncodingThenRedirectDoesContainsEncodedParameter() throws Exception { - given(this.repository.findByRegistrationId("registration-id")).willReturn(this.rpBuilder.build()); - final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param"; - final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1); - this.request.setParameter("RelayState", relayStateValue); + String relayStateValue = "https://my-relay-state.example.com?with=param&other=param"; + String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1); + Saml2AuthenticationRequestContext context = authenticationRequestContext().relayState(relayStateValue).build(); + Saml2RedirectAuthenticationRequest request = redirectAuthenticationRequest(context).build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createRedirectAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); assertThat(this.response.getHeader("Location")).contains("RelayState=" + relayStateEncoded) .startsWith(IDP_SSO_URL); @@ -107,34 +132,39 @@ public void doFilterWhenRelayStateThatRequiresEncodingThenRedirectDoesContainsEn @Test public void doFilterWhenSimpleSignatureSpecifiedThenSignatureParametersAreInTheRedirectURL() throws Exception { - given(this.repository.findByRegistrationId("registration-id")).willReturn(this.rpBuilder.build()); - final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param"; - final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1); - this.request.setParameter("RelayState", relayStateValue); + Saml2AuthenticationRequestContext context = authenticationRequestContext().build(); + Saml2RedirectAuthenticationRequest request = redirectAuthenticationRequest(context).sigAlg("sigalg") + .signature("signature").build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createRedirectAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); - assertThat(this.response.getHeader("Location")).contains("RelayState=" + relayStateEncoded).contains("SigAlg=") - .contains("Signature=").startsWith(IDP_SSO_URL); + assertThat(this.response.getHeader("Location")).contains("SigAlg=").contains("Signature=") + .startsWith(IDP_SSO_URL); } @Test public void doFilterWhenSignatureIsDisabledThenSignatureParametersAreNotInTheRedirectURL() throws Exception { - given(this.repository.findByRegistrationId("registration-id")) - .willReturn(this.rpBuilder.providerDetails((c) -> c.signAuthNRequest(false)).build()); - final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param"; - final String relayStateEncoded = UriUtils.encode(relayStateValue, StandardCharsets.ISO_8859_1); - this.request.setParameter("RelayState", relayStateValue); + Saml2AuthenticationRequestContext context = authenticationRequestContext().build(); + Saml2RedirectAuthenticationRequest request = redirectAuthenticationRequest(context).build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createRedirectAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); - assertThat(this.response.getHeader("Location")).contains("RelayState=" + relayStateEncoded) - .doesNotContain("SigAlg=").doesNotContain("Signature=").startsWith(IDP_SSO_URL); + assertThat(this.response.getHeader("Location")).doesNotContain("SigAlg=").doesNotContain("Signature=") + .startsWith(IDP_SSO_URL); } @Test public void doFilterWhenPostFormDataIsPresent() throws Exception { - given(this.repository.findByRegistrationId("registration-id")) - .willReturn(this.rpBuilder.providerDetails((c) -> c.binding(Saml2MessageBinding.POST)).build()); - final String relayStateValue = "https://my-relay-state.example.com?with=param&other=param&javascript{alert('1');}"; - final String relayStateEncoded = HtmlUtils.htmlEscape(relayStateValue); - this.request.setParameter("RelayState", relayStateValue); + String relayStateValue = "https://my-relay-state.example.com?with=param&other=param&javascript{alert('1');}"; + String relayStateEncoded = HtmlUtils.htmlEscape(relayStateValue); + RelyingPartyRegistration registration = this.rpBuilder + .assertingPartyDetails((asserting) -> asserting.singleSignOnServiceBinding(Saml2MessageBinding.POST)) + .build(); + Saml2AuthenticationRequestContext context = authenticationRequestContext().relayState(relayStateValue) + .relyingPartyRegistration(registration).build(); + Saml2PostAuthenticationRequest request = postAuthenticationRequest(context).build(); + given(this.resolver.resolve(any())).willReturn(context); + given(this.factory.createPostAuthenticationRequest(any())).willReturn(request); this.filter.doFilterInternal(this.request, this.response, this.filterChain); assertThat(this.response.getHeader("Location")).isNull(); assertThat(this.response.getContentAsString()) @@ -145,66 +175,43 @@ public void doFilterWhenPostFormDataIsPresent() throws Exception { @Test public void doFilterWhenSetAuthenticationRequestFactoryThenUses() throws Exception { - RelyingPartyRegistration relyingParty = this.rpBuilder - .providerDetails((c) -> c.binding(Saml2MessageBinding.POST)).build(); - Saml2PostAuthenticationRequest authenticationRequest = mock(Saml2PostAuthenticationRequest.class); - given(authenticationRequest.getAuthenticationRequestUri()).willReturn("uri"); - given(authenticationRequest.getRelayState()).willReturn("relay"); - given(authenticationRequest.getSamlRequest()).willReturn("saml"); - given(this.repository.findByRegistrationId("registration-id")).willReturn(relyingParty); - given(this.factory.createPostAuthenticationRequest(any())).willReturn(authenticationRequest); - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); - filter.setAuthenticationRequestFactory(this.factory); - filter.doFilterInternal(this.request, this.response, this.filterChain); - assertThat(this.response.getContentAsString()).contains("<form action=\"uri\" method=\"post\">") - .contains("<input type=\"hidden\" name=\"SAMLRequest\" value=\"saml\"") - .contains("<input type=\"hidden\" name=\"RelayState\" value=\"relay\""); - verify(this.factory).createPostAuthenticationRequest(any()); + Saml2AuthenticationRequestContext context = authenticationRequestContext().build(); + Saml2RedirectAuthenticationRequest authenticationRequest = redirectAuthenticationRequest(context).build(); + Saml2AuthenticationRequestFactory factory = mock(Saml2AuthenticationRequestFactory.class); + given(this.resolver.resolve(any())).willReturn(context); + given(factory.createRedirectAuthenticationRequest(any())).willReturn(authenticationRequest); + this.filter.setAuthenticationRequestFactory(factory); + this.filter.doFilterInternal(this.request, this.response, this.filterChain); + verify(factory).createRedirectAuthenticationRequest(any()); } @Test - public void doFilterWhenCustomAuthenticationRequestFactoryThenUses() throws Exception { - RelyingPartyRegistration relyingParty = this.rpBuilder - .providerDetails((c) -> c.binding(Saml2MessageBinding.POST)).build(); - Saml2PostAuthenticationRequest authenticationRequest = mock(Saml2PostAuthenticationRequest.class); - given(authenticationRequest.getAuthenticationRequestUri()).willReturn("uri"); - given(authenticationRequest.getRelayState()).willReturn("relay"); - given(authenticationRequest.getSamlRequest()).willReturn("saml"); - given(this.resolver.resolve(this.request)).willReturn(TestSaml2AuthenticationRequestContexts - .authenticationRequestContext().relyingPartyRegistration(relyingParty).build()); - given(this.factory.createPostAuthenticationRequest(any())).willReturn(authenticationRequest); + public void setRequestMatcherWhenNullThenException() { Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver, this.factory); - filter.doFilterInternal(this.request, this.response, this.filterChain); - assertThat(this.response.getContentAsString()).contains("<form action=\"uri\" method=\"post\">") - .contains("<input type=\"hidden\" name=\"SAMLRequest\" value=\"saml\"") - .contains("<input type=\"hidden\" name=\"RelayState\" value=\"relay\""); - verify(this.factory).createPostAuthenticationRequest(any()); - } - - @Test - public void setRequestMatcherWhenNullThenException() { - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); assertThatIllegalArgumentException().isThrownBy(() -> filter.setRedirectMatcher(null)); } @Test public void setAuthenticationRequestFactoryWhenNullThenException() { - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); + Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver, + this.factory); assertThatIllegalArgumentException().isThrownBy(() -> filter.setAuthenticationRequestFactory(null)); } @Test public void doFilterWhenRequestMatcherFailsThenSkipsFilter() throws Exception { - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); + Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver, + this.factory); filter.setRedirectMatcher((request) -> false); filter.doFilter(this.request, this.response, this.filterChain); - verifyNoInteractions(this.repository); + verifyNoInteractions(this.resolver, this.factory); } @Test public void doFilterWhenRelyingPartyRegistrationNotFoundThenUnauthorized() throws Exception { - Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.repository); + Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(this.resolver, + this.factory); filter.doFilter(this.request, this.response, this.filterChain); assertThat(this.response.getStatus()).isEqualTo(401); } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolverTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolverTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultRelyingPartyRegistrationResolverTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolverTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolverTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/DefaultSaml2AuthenticationRequestContextResolverTests.java diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java similarity index 72% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java index 91038de6ae8..4a5e9e2feaf 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java +++ b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2AuthenticationTokenConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,9 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.io.ClassPathResource; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2Utils; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; @@ -37,6 +39,7 @@ import org.springframework.web.util.UriUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -64,6 +67,22 @@ public void convertWhenSamlResponseThenToken() { .isEqualTo(this.relyingPartyRegistration.getRegistrationId()); } + @Test + public void convertWhenSamlResponseInvalidBase64ThenSaml2AuthenticationException() { + Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter( + this.relyingPartyRegistrationResolver); + given(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class))) + .willReturn(this.relyingPartyRegistration); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setParameter("SAMLResponse", "invalid"); + assertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request)) + .withCauseInstanceOf(IllegalArgumentException.class) + .satisfies((ex) -> assertThat(ex.getSaml2Error().getErrorCode()) + .isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE)) + .satisfies((ex) -> assertThat(ex.getSaml2Error().getDescription()) + .isEqualTo("Failed to decode SAMLResponse")); + } + @Test public void convertWhenNoSamlResponseThenNull() { Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter( @@ -100,6 +119,25 @@ public void convertWhenGetRequestThenInflates() { .isEqualTo(this.relyingPartyRegistration.getRegistrationId()); } + @Test + public void convertWhenGetRequestInvalidDeflatedThenSaml2AuthenticationException() { + Saml2AuthenticationTokenConverter converter = new Saml2AuthenticationTokenConverter( + this.relyingPartyRegistrationResolver); + given(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class))) + .willReturn(this.relyingPartyRegistration); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + byte[] invalidDeflated = "invalid".getBytes(); + String encoded = Saml2Utils.samlEncode(invalidDeflated); + request.setParameter("SAMLResponse", encoded); + assertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> converter.convert(request)) + .withCauseInstanceOf(IOException.class) + .satisfies((ex) -> assertThat(ex.getSaml2Error().getErrorCode()) + .isEqualTo(Saml2ErrorCodes.INVALID_RESPONSE)) + .satisfies( + (ex) -> assertThat(ex.getSaml2Error().getDescription()).isEqualTo("Unable to inflate string")); + } + @Test public void constructorWhenResolverIsNullThenIllegalArgument() { assertThatIllegalArgumentException().isThrownBy(() -> new Saml2AuthenticationTokenConverter(null)); diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java similarity index 72% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java rename to saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java index 12e024a3a14..f71bfcea89b 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java +++ b/saml2/saml2-service-provider/core/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2MetadataFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,15 @@ package org.springframework.security.saml2.provider.service.web; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + import javax.servlet.FilterChain; import org.junit.Before; import org.junit.Test; +import org.springframework.http.HttpHeaders; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.saml2.core.TestSaml2X509Credentials; @@ -31,6 +35,7 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -115,9 +120,38 @@ public void doFilterWhenCustomRequestMatcherThenUses() throws Exception { verify(this.repository).findByRegistrationId("path"); } + @Test + public void doFilterWhenSetMetadataFilenameThenUses() throws Exception { + RelyingPartyRegistration validRegistration = TestRelyingPartyRegistrations.full().build(); + String testMetadataFilename = "test-{registrationId}-metadata.xml"; + String fileName = testMetadataFilename.replace("{registrationId}", validRegistration.getRegistrationId()); + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); + String generatedMetadata = "<xml>test</xml>"; + this.request.setPathInfo("/saml2/service-provider-metadata/registration-id"); + given(this.resolver.resolve(validRegistration)).willReturn(generatedMetadata); + this.filter = new Saml2MetadataFilter((request) -> validRegistration, this.resolver); + this.filter.setMetadataFilename(testMetadataFilename); + this.filter.doFilter(this.request, this.response, this.chain); + assertThat(this.response.getHeaderValue(HttpHeaders.CONTENT_DISPOSITION)).asString() + .isEqualTo("attachment; filename=\"%s\"; filename*=UTF-8''%s", fileName, encodedFileName); + } + @Test public void setRequestMatcherWhenNullThenIllegalArgument() { assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null)); } + @Test + public void setMetadataFilenameWhenEmptyThenThrowsException() { + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.filter.setMetadataFilename(" ")) + .withMessage("metadataFilename cannot be empty"); + } + + @Test + public void setMetadataFilenameWhenMissingRegistrationIdVariableThenThrowsException() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> this.filter.setMetadataFilename("metadata-filename.xml")) + .withMessage("metadataFilename must contain a {registrationId} match variable"); + } + } diff --git a/saml2/saml2-service-provider/src/test/resources/logback-test.xml b/saml2/saml2-service-provider/core/src/test/resources/logback-test.xml similarity index 100% rename from saml2/saml2-service-provider/src/test/resources/logback-test.xml rename to saml2/saml2-service-provider/core/src/test/resources/logback-test.xml diff --git a/saml2/saml2-service-provider/src/test/resources/saml2-response-sso-circle.encoded b/saml2/saml2-service-provider/core/src/test/resources/saml2-response-sso-circle.encoded similarity index 100% rename from saml2/saml2-service-provider/src/test/resources/saml2-response-sso-circle.encoded rename to saml2/saml2-service-provider/core/src/test/resources/saml2-response-sso-circle.encoded diff --git a/saml2/saml2-service-provider/src/test/resources/test-metadata.xml b/saml2/saml2-service-provider/core/src/test/resources/test-metadata.xml similarity index 100% rename from saml2/saml2-service-provider/src/test/resources/test-metadata.xml rename to saml2/saml2-service-provider/core/src/test/resources/test-metadata.xml diff --git a/saml2/saml2-service-provider/opensaml3/saml2-service-provider-opensaml3.gradle b/saml2/saml2-service-provider/opensaml3/saml2-service-provider-opensaml3.gradle new file mode 100644 index 00000000000..4771290e3f0 --- /dev/null +++ b/saml2/saml2-service-provider/opensaml3/saml2-service-provider-opensaml3.gradle @@ -0,0 +1,61 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/plugins-release' } + } + dependencies { + classpath 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + } +} + +plugins { + id 'java' + id 'java-library' + id 'io.spring.convention.repository' + id 'io.spring.convention.management-configuration' + id 'io.spring.convention.dependency-set' + id 'io.spring.convention.checkstyle' + id 'io.spring.convention.tests-configuration' + id 'io.spring.convention.integration-test' + id 'propdeps' +} + +configurations { + classesOnlyElements { + canBeConsumed = true + canBeResolved = false + } + sourceElements { + canBeConsumed = true + canBeResolved = false + } + javadocElements { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + classesOnlyElements(compileJava.destinationDir) + sourceSets.main.allSource.srcDirs.forEach({ dir -> + sourceElements(dir) + }) + javadocElements(javadoc.destinationDir) +} + +repositories { + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } +} + +dependencies { + management platform(project(":spring-security-dependencies")) + api project(':saml2-service-provider-core') + + api("org.opensaml:opensaml-core") + api("org.opensaml:opensaml-saml-api") + api("org.opensaml:opensaml-saml-impl") + + provided 'javax.servlet:javax.servlet-api' + + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation project(path : ':saml2-service-provider-core', configuration : 'tests') +} diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java b/saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java similarity index 81% rename from saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java rename to saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java index 46bc579bbf2..e8531b1d397 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java +++ b/saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java @@ -21,27 +21,21 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.xml.namespace.QName; -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; import net.shibboleth.utilities.java.support.xml.ParserPool; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.joda.time.DateTime; import org.opensaml.core.config.ConfigurationService; -import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.config.XMLObjectProviderRegistry; import org.opensaml.core.xml.schema.XSAny; @@ -51,11 +45,9 @@ import org.opensaml.core.xml.schema.XSInteger; import org.opensaml.core.xml.schema.XSString; import org.opensaml.core.xml.schema.XSURI; +import org.opensaml.saml.common.assertion.AssertionValidationException; import org.opensaml.saml.common.assertion.ValidationContext; import org.opensaml.saml.common.assertion.ValidationResult; -import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.criterion.ProtocolCriterion; -import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion; import org.opensaml.saml.saml2.assertion.ConditionValidator; import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator; import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; @@ -69,35 +61,15 @@ import org.opensaml.saml.saml2.core.AttributeStatement; import org.opensaml.saml.saml2.core.Condition; import org.opensaml.saml.saml2.core.EncryptedAssertion; -import org.opensaml.saml.saml2.core.EncryptedAttribute; -import org.opensaml.saml.saml2.core.NameID; import org.opensaml.saml.saml2.core.OneTimeUse; import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.core.StatusCode; import org.opensaml.saml.saml2.core.SubjectConfirmation; import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; import org.opensaml.saml.saml2.encryption.Decrypter; -import org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver; import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; -import org.opensaml.security.credential.Credential; -import org.opensaml.security.credential.CredentialResolver; -import org.opensaml.security.credential.CredentialSupport; -import org.opensaml.security.credential.UsageType; -import org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion; -import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion; -import org.opensaml.security.credential.impl.CollectionCredentialResolver; -import org.opensaml.security.criteria.UsageCriterion; -import org.opensaml.security.x509.BasicX509Credential; -import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; -import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver; -import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver; -import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver; -import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver; -import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver; -import org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver; import org.opensaml.xmlsec.signature.support.SignaturePrevalidator; import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; -import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -115,7 +87,7 @@ import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; -import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -131,18 +103,15 @@ * {@link Saml2AuthenticationToken#getSaml2Response()} along with the information about * the asserting party, the identity provider (IDP), as well as the relying party, the * service provider (SP, this application). - * </p> * <p> * The {@link Saml2AuthenticationToken} will be processed into a SAML Response object. The * SAML response object can be signed. If the Response is signed, a signature will not be * required on the assertion. - * </p> * <p> * While a response object can contain a list of assertion, this provider will only * leverage the first valid assertion for the purpose of authentication. Assertions that * do not pass validation will be ignored. If no valid assertions are found a * {@link Saml2AuthenticationException} is thrown. - * </p> * <p> * This provider supports two types of encrypted SAML elements * <ul> @@ -153,11 +122,9 @@ * </ul> * If the assertion is encrypted, then signature validation on the assertion is no longer * required. - * </p> * <p> * This provider does not perform an X509 certificate validation on the configured * asserting party, IDP, verification certificates. - * </p> * * @author Ryan Cassar * @since 5.2 @@ -165,6 +132,8 @@ * "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38">SAML 2 * StatusResponse</a> * @see <a href="https://wiki.shibboleth.net/confluence/display/OS30/Home">OpenSAML 3</a> + * @deprecated Because OpenSAML 3 has reached End-of-Life, please update to + * {@code OpenSaml4AuthenticationProvider} */ public final class OpenSamlAuthenticationProvider implements AuthenticationProvider { @@ -201,10 +170,6 @@ public final class OpenSamlAuthenticationProvider implements AuthenticationProvi private Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter = createCompatibleResponseAuthenticationConverter(); - private Converter<Saml2AuthenticationToken, SignatureTrustEngine> signatureTrustEngineConverter = new SignatureTrustEngineConverter(); - - private Converter<Saml2AuthenticationToken, Decrypter> decrypterConverter = new DecrypterConverter(); - /** * Creates an {@link OpenSamlAuthenticationProvider} */ @@ -225,7 +190,7 @@ public OpenSamlAuthenticationProvider() { * * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - * provider.setResponseElementsDecrypter((responseToken) -> { + * provider.setResponseElementsDecrypter((responseToken) -> { * DecrypterParameters parameters = new DecrypterParameters(); * // ... set parameters as needed * Decrypter decrypter = new Decrypter(parameters); @@ -246,7 +211,7 @@ public OpenSamlAuthenticationProvider() { * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); * Converter<EncryptedAssertion, Assertion> myService = ... - * provider.setResponseDecrypter((responseToken) -> { + * provider.setResponseDecrypter((responseToken) -> { * Response response = responseToken.getResponse(); * response.getEncryptedAssertions().stream() * .map(service::decrypt).forEach(response.getAssertions()::add); @@ -272,7 +237,7 @@ public void setResponseElementsDecrypter(Consumer<ResponseToken> responseElement * * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - * provider.setAssertionValidator(assertionToken -> { + * provider.setAssertionValidator(assertionToken -> { * Saml2ResponseValidatorResult result = createDefaultAssertionValidator() * .convert(assertionToken) * return result.concat(myCustomValidator.convert(assertionToken)); @@ -285,7 +250,7 @@ public void setResponseElementsDecrypter(Consumer<ResponseToken> responseElement * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); * provider.setAssertionValidator( - * createDefaultAssertionValidator(assertionToken -> { + * createDefaultAssertionValidator(assertionToken -> { * Map<String, Object> params = new HashMap<>(); * params.put(CLOCK_SKEW, 2 * 60 * 1000); * // other parameters @@ -301,7 +266,7 @@ public void setResponseElementsDecrypter(Consumer<ResponseToken> responseElement * step from this validator. * * This method takes precedence over {@link #setResponseTimeValidationSkew}. - * @param assertionValidator + * @param assertionValidator the strategy for validating a given assertion * @since 5.4 */ public void setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator) { @@ -317,7 +282,7 @@ public void setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidat * * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - * provider.setResponseDecrypter((assertionToken) -> { + * provider.setResponseDecrypter((assertionToken) -> { * DecrypterParameters parameters = new DecrypterParameters(); * // ... set parameters as needed * Decrypter decrypter = new Decrypter(parameters); @@ -337,7 +302,7 @@ public void setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidat * <pre> * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); * MyDecryptionService myService = ... - * provider.setResponseDecrypter((responseToken) -> { + * provider.setResponseDecrypter((responseToken) -> { * Assertion assertion = assertionToken.getAssertion(); * EncryptedID encrypted = assertion.getSubject().getEncryptedID(); * NameID name = myService.decrypt(encrypted); @@ -363,7 +328,7 @@ public void setAssertionElementsDecrypter(Consumer<AssertionToken> assertionDecr * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); * Converter<ResponseToken, Saml2Authentication> authenticationConverter = * createDefaultResponseAuthenticationConverter(); - * provider.setResponseAuthenticationConverter(responseToken -> { + * provider.setResponseAuthenticationConverter(responseToken -> { * Saml2Authentication authentication = authenticationConverter.convert(responseToken); * User user = myUserRepository.findByUsername(authentication.getName()); * return new MyAuthentication(authentication, user); @@ -560,53 +525,23 @@ else if (logger.isDebugEnabled()) { private Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseSignatureValidator() { return (responseToken) -> { Response response = responseToken.getResponse(); - Saml2AuthenticationToken token = responseToken.getToken(); - Collection<Saml2Error> errors = new ArrayList<>(); - String issuer = response.getIssuer().getValue(); + RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); if (response.isSigned()) { - SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); - try { - profileValidator.validate(response.getSignature()); - } - catch (Exception ex) { - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - "Invalid signature for SAML Response [" + response.getID() + "]: ")); - } - - try { - CriteriaSet criteriaSet = new CriteriaSet(); - criteriaSet.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(issuer))); - criteriaSet.add(new EvaluableProtocolRoleDescriptorCriterion( - new ProtocolCriterion(SAMLConstants.SAML20P_NS))); - criteriaSet.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING))); - if (!this.signatureTrustEngineConverter.convert(token).validate(response.getSignature(), - criteriaSet)) { - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - "Invalid signature for SAML Response [" + response.getID() + "]")); - } - } - catch (Exception ex) { - errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - "Invalid signature for SAML Response [" + response.getID() + "]: ")); - } + return OpenSamlVerificationUtils.verifySignature(response, registration).post(response.getSignature()); } - - return Saml2ResponseValidatorResult.failure(errors); + return Saml2ResponseValidatorResult.success(); }; } private Consumer<ResponseToken> createDefaultResponseElementsDecrypter() { return (responseToken) -> { - Decrypter decrypter = this.decrypterConverter.convert(responseToken.getToken()); Response response = responseToken.getResponse(); - for (EncryptedAssertion encryptedAssertion : responseToken.getResponse().getEncryptedAssertions()) { - try { - Assertion assertion = decrypter.decrypt(encryptedAssertion); - response.getAssertions().add(assertion); - } - catch (Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); - } + RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); + try { + OpenSamlDecryptionUtils.decryptResponseElements(response, registration); + } + catch (Saml2Exception ex) { + throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); } }; } @@ -656,7 +591,8 @@ private String getStatusCode(Response response) { private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() { return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> { - SignatureTrustEngine engine = this.signatureTrustEngineConverter.convert(assertionToken.token); + RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); + SignatureTrustEngine engine = OpenSamlVerificationUtils.trustEngine(registration); return SAML20AssertionValidators.createSignatureValidator(engine); }, (assertionToken) -> new ValidationContext( Collections.singletonMap(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false))); @@ -664,29 +600,12 @@ private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAss private Consumer<AssertionToken> createDefaultAssertionElementsDecrypter() { return (assertionToken) -> { - Decrypter decrypter = this.decrypterConverter.convert(assertionToken.getToken()); Assertion assertion = assertionToken.getAssertion(); - for (AttributeStatement statement : assertion.getAttributeStatements()) { - for (EncryptedAttribute encryptedAttribute : statement.getEncryptedAttributes()) { - try { - Attribute attribute = decrypter.decrypt(encryptedAttribute); - statement.getAttributes().add(attribute); - } - catch (Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); - } - } - } - if (assertion.getSubject() == null) { - return; - } - if (assertion.getSubject().getEncryptedID() == null) { - return; - } + RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); try { - assertion.getSubject().setNameID((NameID) decrypter.decrypt(assertion.getSubject().getEncryptedID())); + OpenSamlDecryptionUtils.decryptAssertionElements(assertion, registration); } - catch (Exception ex) { + catch (Saml2Exception ex) { throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); } }; @@ -765,8 +684,7 @@ private static Object getXmlObjectValue(XMLObject xmlObject) { return (xsBooleanValue != null) ? xsBooleanValue.getValue() : null; } if (xmlObject instanceof XSDateTime) { - DateTime dateTime = ((XSDateTime) xmlObject).getValue(); - return (dateTime != null) ? Instant.ofEpochMilli(dateTime.getMillis()) : null; + return Instant.ofEpochMilli(((XSDateTime) xmlObject).getValue().getMillis()); } return null; } @@ -812,27 +730,6 @@ private static ValidationContext createValidationContext(AssertionToken assertio return new ValidationContext(params); } - private static class SignatureTrustEngineConverter - implements Converter<Saml2AuthenticationToken, SignatureTrustEngine> { - - @Override - public SignatureTrustEngine convert(Saml2AuthenticationToken token) { - Set<Credential> credentials = new HashSet<>(); - Collection<Saml2X509Credential> keys = token.getRelyingPartyRegistration().getAssertingPartyDetails() - .getVerificationX509Credentials(); - for (Saml2X509Credential key : keys) { - BasicX509Credential cred = new BasicX509Credential(key.getCertificate()); - cred.setUsageType(UsageType.SIGNING); - cred.setEntityId(token.getRelyingPartyRegistration().getAssertingPartyDetails().getEntityId()); - credentials.add(cred); - } - CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); - return new ExplicitKeySignatureTrustEngine(credentialsResolver, - DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver()); - } - - } - private static class SAML20AssertionValidators { private static final Collection<ConditionValidator> conditions = new ArrayList<>(); @@ -861,10 +758,9 @@ public ValidationResult validate(Condition condition, Assertion assertion, Valid } }); subjects.add(new BearerSubjectConfirmationValidator() { - @Nonnull @Override - protected ValidationResult validateAddress(@Nonnull SubjectConfirmation confirmation, - @Nonnull Assertion assertion, @Nonnull ValidationContext context) { + protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion, + ValidationContext context) throws AssertionValidationException { // applications should validate their own addresses - gh-7514 return ValidationResult.VALID; } @@ -906,27 +802,6 @@ protected ValidationResult validateStatements(Assertion assertion, ValidationCon } - private static class DecrypterConverter implements Converter<Saml2AuthenticationToken, Decrypter> { - - private final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver( - Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(), - new SimpleRetrievalMethodEncryptedKeyResolver())); - - @Override - public Decrypter convert(Saml2AuthenticationToken token) { - Collection<Credential> credentials = new ArrayList<>(); - for (Saml2X509Credential key : token.getRelyingPartyRegistration().getDecryptionX509Credentials()) { - Credential cred = CredentialSupport.getSimpleCredential(key.getCertificate(), key.getPrivateKey()); - credentials.add(cred); - } - KeyInfoCredentialResolver resolver = new CollectionKeyInfoCredentialResolver(credentials); - Decrypter decrypter = new Decrypter(null, resolver, this.encryptedKeyResolver); - decrypter.setRootInNewDocument(true); - return decrypter; - } - - } - /** * A tuple containing an OpenSAML {@link Response} and its associated authentication * token. diff --git a/saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java b/saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java new file mode 100644 index 00000000000..849a95a4c9d --- /dev/null +++ b/saml2/saml2-service-provider/opensaml3/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java @@ -0,0 +1,204 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +import org.joda.time.DateTime; +import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder; +import org.opensaml.saml.saml2.core.impl.IssuerBuilder; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.saml2.core.OpenSamlInitializationService; +import org.springframework.security.saml2.provider.service.authentication.OpenSamlSigningUtils.QueryParametersPartial; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * A {@link Saml2AuthenticationRequestFactory} that generates, signs, and serializes a + * SAML 2.0 AuthnRequest using OpenSAML 3 + * + * @author Filip Hanik + * @author Josh Cummings + * @since 5.2 + * @deprecated Because OpenSAML 3 has reached End-of-Life, please update to + * {@code OpenSaml4AuthenticationRequestFactory} + */ +public class OpenSamlAuthenticationRequestFactory implements Saml2AuthenticationRequestFactory { + + static { + OpenSamlInitializationService.initialize(); + } + + private AuthnRequestBuilder authnRequestBuilder; + + private IssuerBuilder issuerBuilder; + + private Clock clock = Clock.systemUTC(); + + private Converter<Saml2AuthenticationRequestContext, Saml2MessageBinding> protocolBindingResolver = (context) -> { + if (context == null) { + return Saml2MessageBinding.POST; + } + return context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding(); + }; + + private Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter; + + /** + * Creates an {@link OpenSamlAuthenticationRequestFactory} + */ + public OpenSamlAuthenticationRequestFactory() { + this.authenticationRequestContextConverter = this::createAuthnRequest; + XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); + this.authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory() + .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); + this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + } + + @Override + @Deprecated + public String createAuthenticationRequest(Saml2AuthenticationRequest request) { + Saml2MessageBinding binding = this.protocolBindingResolver.convert(null); + RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("noId") + .assertionConsumerServiceBinding(binding) + .assertionConsumerServiceLocation(request.getAssertionConsumerServiceUrl()) + .entityId(request.getIssuer()).remoteIdpEntityId("noIssuer").idpWebSsoUrl("noUrl") + .credentials((credentials) -> credentials.addAll(request.getCredentials())).build(); + Saml2AuthenticationRequestContext context = Saml2AuthenticationRequestContext.builder() + .relyingPartyRegistration(registration).issuer(request.getIssuer()) + .assertionConsumerServiceUrl(request.getAssertionConsumerServiceUrl()).build(); + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + return OpenSamlSigningUtils.serialize(OpenSamlSigningUtils.sign(authnRequest, registration)); + } + + @Override + public Saml2PostAuthenticationRequest createPostAuthenticationRequest(Saml2AuthenticationRequestContext context) { + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + RelyingPartyRegistration registration = context.getRelyingPartyRegistration(); + if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned()) { + OpenSamlSigningUtils.sign(authnRequest, registration); + } + String xml = OpenSamlSigningUtils.serialize(authnRequest); + return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context) + .samlRequest(Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8))).build(); + } + + @Override + public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest( + Saml2AuthenticationRequestContext context) { + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + RelyingPartyRegistration registration = context.getRelyingPartyRegistration(); + String xml = OpenSamlSigningUtils.serialize(authnRequest); + Saml2RedirectAuthenticationRequest.Builder result = Saml2RedirectAuthenticationRequest + .withAuthenticationRequestContext(context); + String deflatedAndEncoded = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(xml)); + result.samlRequest(deflatedAndEncoded).relayState(context.getRelayState()); + if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned()) { + QueryParametersPartial partial = OpenSamlSigningUtils.sign(registration).param("SAMLRequest", + deflatedAndEncoded); + if (StringUtils.hasText(context.getRelayState())) { + partial.param("RelayState", context.getRelayState()); + } + Map<String, String> parameters = partial.parameters(); + return result.sigAlg(parameters.get("SigAlg")).signature(parameters.get("Signature")).build(); + } + return result.build(); + } + + private AuthnRequest createAuthnRequest(Saml2AuthenticationRequestContext context) { + String issuer = context.getIssuer(); + String destination = context.getDestination(); + String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl(); + Saml2MessageBinding protocolBinding = this.protocolBindingResolver.convert(context); + AuthnRequest auth = this.authnRequestBuilder.buildObject(); + if (auth.getID() == null) { + auth.setID("ARQ" + UUID.randomUUID().toString().substring(1)); + } + if (auth.getIssueInstant() == null) { + auth.setIssueInstant(new DateTime(this.clock.millis())); + } + if (auth.isForceAuthn() == null) { + auth.setForceAuthn(Boolean.FALSE); + } + if (auth.isPassive() == null) { + auth.setIsPassive(Boolean.FALSE); + } + if (auth.getProtocolBinding() == null) { + auth.setProtocolBinding(protocolBinding.getUrn()); + } + Issuer iss = this.issuerBuilder.buildObject(); + iss.setValue(issuer); + auth.setIssuer(iss); + auth.setDestination(destination); + auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl); + return auth; + } + + /** + * Set the {@link AuthnRequest} post-processor resolver + * @param authenticationRequestContextConverter a strategy for creating an + * {@link AuthnRequest} + * @since 5.4 + */ + public void setAuthenticationRequestContextConverter( + Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter) { + Assert.notNull(authenticationRequestContextConverter, "authenticationRequestContextConverter cannot be null"); + this.authenticationRequestContextConverter = authenticationRequestContextConverter; + } + + /** + * ' Use this {@link Clock} with {@link Instant#now()} for generating timestamps + * @param clock the {@link Clock} to use + */ + public void setClock(Clock clock) { + Assert.notNull(clock, "clock cannot be null"); + this.clock = clock; + } + + /** + * Sets the {@code protocolBinding} to use when generating authentication requests. + * Acceptable values are {@link SAMLConstants#SAML2_POST_BINDING_URI} and + * {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI} The IDP will be reading this value + * in the {@code AuthNRequest} to determine how to send the Response/Assertion to the + * ACS URL, assertion consumer service URL. + * @param protocolBinding either {@link SAMLConstants#SAML2_POST_BINDING_URI} or + * {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI} + * @throws IllegalArgumentException if the protocolBinding is not valid + * @deprecated Use + * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder#assertionConsumerServiceBinding(Saml2MessageBinding)} + * instead + */ + @Deprecated + public void setProtocolBinding(String protocolBinding) { + Saml2MessageBinding binding = Saml2MessageBinding.from(protocolBinding); + Assert.notNull(binding, "Invalid protocol binding: " + protocolBinding); + this.protocolBindingResolver = (context) -> binding; + } + +} diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java similarity index 88% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java rename to saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java index 3e9f1995ba2..93e263a2e31 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java +++ b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java @@ -38,10 +38,15 @@ import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.core.xml.io.Marshaller; import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.schema.XSDateTime; +import org.opensaml.core.xml.schema.impl.XSDateTimeBuilder; import org.opensaml.saml.common.assertion.ValidationContext; import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.AttributeValue; +import org.opensaml.saml.saml2.core.Conditions; import org.opensaml.saml.saml2.core.EncryptedAssertion; import org.opensaml.saml.saml2.core.EncryptedAttribute; import org.opensaml.saml.saml2.core.EncryptedID; @@ -49,6 +54,9 @@ import org.opensaml.saml.saml2.core.OneTimeUse; import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.core.StatusCode; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml.saml2.core.impl.AttributeBuilder; import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder; import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder; import org.opensaml.saml.saml2.core.impl.NameIDBuilder; @@ -134,8 +142,8 @@ public void authenticateWhenXmlErrorThenThrowAuthenticationException() { @Test public void authenticateWhenInvalidDestinationThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(DESTINATION + "invalid", ASSERTING_PARTY_ENTITY_ID); - response.getAssertions().add(TestOpenSamlObjects.assertion()); + Response response = response(DESTINATION + "invalid", ASSERTING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion()); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); Saml2AuthenticationToken token = token(response, verifying(registration())); @@ -154,8 +162,8 @@ public void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException( @Test public void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - response.getAssertions().add(TestOpenSamlObjects.assertion()); + Response response = response(); + response.getAssertions().add(assertion()); Saml2AuthenticationToken token = token(response, verifying(registration())); assertThatExceptionOfType(Saml2AuthenticationException.class) .isThrownBy(() -> this.provider.authenticate(token)) @@ -164,8 +172,8 @@ public void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationEx @Test public void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); assertion.getSubject().getSubjectConfirmations().get(0).getSubjectConfirmationData() .setNotOnOrAfter(DateTime.now().minus(Duration.standardDays(3))); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -179,8 +187,8 @@ public void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationExcept @Test public void authenticateWhenMissingSubjectThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); assertion.setSubject(null); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); @@ -193,8 +201,8 @@ public void authenticateWhenMissingSubjectThenThrowAuthenticationException() { @Test public void authenticateWhenUsernameMissingThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); assertion.getSubject().getNameID().setValue(null); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); @@ -207,8 +215,8 @@ public void authenticateWhenUsernameMissingThenThrowAuthenticationException() { @Test public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); assertion.getSubject().getSubjectConfirmations() .forEach((sc) -> sc.getSubjectConfirmationData().setAddress("10.10.10.10")); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -220,9 +228,9 @@ public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() { @Test public void authenticateWhenAssertionContainsAttributesThenItSucceeds() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); - List<AttributeStatement> attributes = TestOpenSamlObjects.attributeStatements(); + Response response = response(); + Assertion assertion = assertion(); + List<AttributeStatement> attributes = attributeStatements(); assertion.getAttributeStatements().addAll(attributes); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); @@ -244,8 +252,8 @@ public void authenticateWhenAssertionContainsAttributesThenItSucceeds() { @Test public void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() { - Response response = TestOpenSamlObjects.response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(TestOpenSamlObjects.assertion(), + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), TestSaml2X509Credentials.assertingPartyEncryptingCredential()); response.getEncryptedAssertions().add(encryptedAssertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -258,8 +266,8 @@ public void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() { @Test public void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.signed(TestOpenSamlObjects.assertion(), + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, TestSaml2X509Credentials.assertingPartyEncryptingCredential()); @@ -272,8 +280,8 @@ public void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() { @Test public void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceeds() { - Response response = TestOpenSamlObjects.response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(TestOpenSamlObjects.assertion(), + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), TestSaml2X509Credentials.assertingPartyEncryptingCredential()); response.getEncryptedAssertions().add(encryptedAssertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -284,8 +292,8 @@ public void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceed @Test public void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); NameID nameId = assertion.getSubject().getNameID(); EncryptedID encryptedID = TestOpenSamlObjects.encrypted(nameId, TestSaml2X509Credentials.assertingPartyEncryptingCredential()); @@ -300,8 +308,8 @@ public void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() { @Test public void authenticateWhenEncryptedAttributeThenDecrypts() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); EncryptedAttribute attribute = TestOpenSamlObjects.encrypted("name", "value", TestSaml2X509Credentials.assertingPartyEncryptingCredential()); AttributeStatement statement = build(AttributeStatement.DEFAULT_ELEMENT_NAME); @@ -318,8 +326,8 @@ public void authenticateWhenEncryptedAttributeThenDecrypts() { @Test public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(TestOpenSamlObjects.assertion(), + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), TestSaml2X509Credentials.assertingPartyEncryptingCredential()); response.getEncryptedAssertions().add(encryptedAssertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -332,8 +340,8 @@ public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationExcep @Test public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() { - Response response = TestOpenSamlObjects.response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(TestOpenSamlObjects.assertion(), + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), TestSaml2X509Credentials.assertingPartyEncryptingCredential()); response.getEncryptedAssertions().add(encryptedAssertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), @@ -347,8 +355,8 @@ public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationExcepti @Test public void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.signed(TestOpenSamlObjects.assertion(), + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, TestSaml2X509Credentials.assertingPartyEncryptingCredential()); @@ -384,8 +392,8 @@ public void authenticateWhenDelegatingToDefaultAssertionValidatorThenUses() { .concat(new Saml2Error("wrong error", "wrong error")) ); // @formatter:on - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); OneTimeUse oneTimeUse = build(OneTimeUse.DEFAULT_ELEMENT_NAME); assertion.getConditions().getConditions().add(oneTimeUse); response.getAssertions().add(assertion); @@ -410,8 +418,8 @@ public void authenticateWhenCustomAssertionValidatorThenUses() { .concat(validator.convert(assertionToken)) ); // @formatter:on - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); response.getAssertions().add(assertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), ASSERTING_PARTY_ENTITY_ID); @@ -426,8 +434,8 @@ public void authenticateWhenCustomAssertionValidatorThenUses() { public void authenticateWhenDefaultConditionValidatorNotUsedThenSignatureStillChecked() { OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); provider.setAssertionValidator((assertionToken) -> Saml2ResponseValidatorResult.success()); - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.relyingPartyDecryptingCredential(), RELYING_PARTY_ENTITY_ID); // broken // signature @@ -451,8 +459,8 @@ public void authenticateWhenValidationContextCustomizedThenUsers() { OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); provider.setAssertionValidator( OpenSamlAuthenticationProvider.createDefaultAssertionValidator((assertionToken) -> context)); - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); response.getAssertions().add(assertion); TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), ASSERTING_PARTY_ENTITY_ID); @@ -467,8 +475,8 @@ public void authenticateWhenValidationContextCustomizedThenUsers() { @Test public void authenticateWithSHA1SignatureThenItSucceeds() throws Exception { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.signed(TestOpenSamlObjects.assertion(), + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); response.getAssertions().add(assertion); @@ -525,8 +533,8 @@ public void setAssertionElementsDecrypterWhenNullThenIllegalArgument() { @Test public void authenticateWhenCustomResponseElementsDecrypterThenDecryptsResponse() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); response.getEncryptedAssertions().add(new EncryptedAssertionBuilder().buildObject()); @@ -540,8 +548,8 @@ public void authenticateWhenCustomResponseElementsDecrypterThenDecryptsResponse( @Test public void authenticateWhenCustomAssertionElementsDecrypterThenDecryptsAssertion() { - Response response = TestOpenSamlObjects.response(); - Assertion assertion = TestOpenSamlObjects.assertion(); + Response response = response(); + Assertion assertion = assertion(); EncryptedID id = new EncryptedIDBuilder().buildObject(); id.setEncryptedData(new EncryptedDataBuilder().buildObject()); assertion.getSubject().setEncryptedID(id); @@ -600,13 +608,52 @@ private Consumer<Saml2AuthenticationException> errorOf(String errorCode, String return (ex) -> { assertThat(ex.getError().getErrorCode()).isEqualTo(errorCode); if (StringUtils.hasText(description)) { - assertThat(ex.getError().getDescription()).isEqualTo(description); + assertThat(ex.getError().getDescription()).contains(description); } }; } - private Saml2AuthenticationToken token() { + private Response response() { Response response = TestOpenSamlObjects.response(); + response.setIssueInstant(DateTime.now()); + return response; + } + + private Response response(String destination, String issuerEntityId) { + Response response = TestOpenSamlObjects.response(destination, issuerEntityId); + response.setIssueInstant(DateTime.now()); + return response; + } + + private Assertion assertion() { + Assertion assertion = TestOpenSamlObjects.assertion(); + assertion.setIssueInstant(DateTime.now()); + for (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) { + SubjectConfirmationData data = confirmation.getSubjectConfirmationData(); + data.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); + data.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); + } + Conditions conditions = assertion.getConditions(); + conditions.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); + conditions.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); + return assertion; + } + + private List<AttributeStatement> attributeStatements() { + List<AttributeStatement> attributeStatements = TestOpenSamlObjects.attributeStatements(); + AttributeBuilder attributeBuilder = new AttributeBuilder(); + Attribute registeredDateAttr = attributeBuilder.buildObject(); + registeredDateAttr.setName("registeredDate"); + XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, + XSDateTime.TYPE_NAME); + registeredDate.setValue(DateTime.parse("1970-01-01T00:00:00Z")); + registeredDateAttr.getAttributeValues().add(registeredDate); + attributeStatements.get(0).getAttributes().add(registeredDateAttr); + return attributeStatements; + } + + private Saml2AuthenticationToken token() { + Response response = response(); RelyingPartyRegistration registration = verifying(registration()).build(); return new Saml2AuthenticationToken(registration, serialize(response)); } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java similarity index 98% rename from saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java rename to saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java index f0f03819fec..4b222b01d1e 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java +++ b/saml2/saml2-service-provider/opensaml3/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java @@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; +import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -200,8 +201,7 @@ public void createAuthenticationRequestWhenSetUnsupportredUriThenThrowsIllegalAr public void createPostAuthenticationRequestWhenAuthnRequestConsumerThenUses() { Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter = mock( Converter.class); - given(authenticationRequestContextConverter.convert(this.context)) - .willReturn(TestOpenSamlObjects.authnRequest()); + given(authenticationRequestContextConverter.convert(this.context)).willReturn(authnRequest()); this.factory.setAuthenticationRequestContextConverter(authenticationRequestContextConverter); this.factory.createPostAuthenticationRequest(this.context); @@ -212,8 +212,7 @@ public void createPostAuthenticationRequestWhenAuthnRequestConsumerThenUses() { public void createRedirectAuthenticationRequestWhenAuthnRequestConsumerThenUses() { Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter = mock( Converter.class); - given(authenticationRequestContextConverter.convert(this.context)) - .willReturn(TestOpenSamlObjects.authnRequest()); + given(authenticationRequestContextConverter.convert(this.context)).willReturn(authnRequest()); this.factory.setAuthenticationRequestContextConverter(authenticationRequestContextConverter); this.factory.createRedirectAuthenticationRequest(this.context); @@ -256,6 +255,12 @@ public void createRedirectAuthenticationRequestWhenSHA1SignRequestThenSignatureI assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); } + private AuthnRequest authnRequest() { + AuthnRequest authnRequest = TestOpenSamlObjects.authnRequest(); + authnRequest.setIssueInstant(DateTime.now()); + return authnRequest; + } + private AuthnRequest getAuthNRequest(Saml2MessageBinding binding) { AbstractSaml2AuthenticationRequest result = (binding == Saml2MessageBinding.REDIRECT) ? this.factory.createRedirectAuthenticationRequest(this.context) diff --git a/saml2/saml2-service-provider/opensaml4/saml2-service-provider-opensaml4.gradle b/saml2/saml2-service-provider/opensaml4/saml2-service-provider-opensaml4.gradle new file mode 100644 index 00000000000..ca313a9e4e4 --- /dev/null +++ b/saml2/saml2-service-provider/opensaml4/saml2-service-provider-opensaml4.gradle @@ -0,0 +1,72 @@ +buildscript { + repositories { + maven { url 'https://repo.spring.io/plugins-release' } + } + dependencies { + classpath 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' + } +} + +plugins { + id 'java' + id 'java-library' + id 'io.spring.convention.repository' + id 'io.spring.convention.management-configuration' + id 'io.spring.convention.dependency-set' + id 'io.spring.convention.checkstyle' + id 'io.spring.convention.tests-configuration' + id 'io.spring.convention.integration-test' + id 'propdeps' +} + +configurations { + classesOnlyElements { + canBeConsumed = true + canBeResolved = false + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 11) + } + } + sourceElements { + canBeConsumed = true + canBeResolved = false + } + javadocElements { + canBeConsumed = true + canBeResolved = false + } +} + +artifacts { + classesOnlyElements(compileJava.destinationDir) + sourceSets.main.allSource.srcDirs.forEach({ dir -> + sourceElements(dir) + }) + javadocElements(javadoc.destinationDir) +} + +sourceCompatibility = '11' + +repositories { + maven { url "https://build.shibboleth.net/nexus/content/repositories/releases/" } +} + +dependencies { + management platform(project(":spring-security-dependencies")) + constraints { + management("org.opensaml:opensaml-core:4.1.0") + management("org.opensaml:opensaml-saml-api:4.1.0") + management("org.opensaml:opensaml-saml-impl:4.1.0") + } + + api project(':saml2-service-provider-core') + + api("org.opensaml:opensaml-core") + api("org.opensaml:opensaml-saml-api") + api("org.opensaml:opensaml-saml-impl") + + provided 'javax.servlet:javax.servlet-api' + + testImplementation 'com.squareup.okhttp3:mockwebserver' + testImplementation project(path : ':saml2-service-provider-core', configuration : 'tests') +} diff --git a/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java new file mode 100644 index 00000000000..f66dbe8dc01 --- /dev/null +++ b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java @@ -0,0 +1,765 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import javax.annotation.Nonnull; +import javax.xml.namespace.QName; + +import net.shibboleth.utilities.java.support.xml.ParserPool; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.core.xml.schema.XSAny; +import org.opensaml.core.xml.schema.XSBoolean; +import org.opensaml.core.xml.schema.XSBooleanValue; +import org.opensaml.core.xml.schema.XSDateTime; +import org.opensaml.core.xml.schema.XSInteger; +import org.opensaml.core.xml.schema.XSString; +import org.opensaml.core.xml.schema.XSURI; +import org.opensaml.saml.common.assertion.ValidationContext; +import org.opensaml.saml.common.assertion.ValidationResult; +import org.opensaml.saml.saml2.assertion.ConditionValidator; +import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator; +import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; +import org.opensaml.saml.saml2.assertion.StatementValidator; +import org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator; +import org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator; +import org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator; +import org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.Condition; +import org.opensaml.saml.saml2.core.EncryptedAssertion; +import org.opensaml.saml.saml2.core.OneTimeUse; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; +import org.opensaml.saml.saml2.encryption.Decrypter; +import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; +import org.opensaml.xmlsec.signature.support.SignaturePrevalidator; +import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.log.LogMessage; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.OpenSamlInitializationService; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/** + * Implementation of {@link AuthenticationProvider} for SAML authentications when + * receiving a {@code Response} object containing an {@code Assertion}. This + * implementation uses the {@code OpenSAML 4} library. + * + * <p> + * The {@link OpenSaml4AuthenticationProvider} supports {@link Saml2AuthenticationToken} + * objects that contain a SAML response in its decoded XML format + * {@link Saml2AuthenticationToken#getSaml2Response()} along with the information about + * the asserting party, the identity provider (IDP), as well as the relying party, the + * service provider (SP, this application). + * <p> + * The {@link Saml2AuthenticationToken} will be processed into a SAML Response object. The + * SAML response object can be signed. If the Response is signed, a signature will not be + * required on the assertion. + * <p> + * While a response object can contain a list of assertion, this provider will only + * leverage the first valid assertion for the purpose of authentication. Assertions that + * do not pass validation will be ignored. If no valid assertions are found a + * {@link Saml2AuthenticationException} is thrown. + * <p> + * This provider supports two types of encrypted SAML elements + * <ul> + * <li><a href= + * "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=17">EncryptedAssertion</a></li> + * <li><a href= + * "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=14">EncryptedID</a></li> + * </ul> + * If the assertion is encrypted, then signature validation on the assertion is no longer + * required. + * <p> + * This provider does not perform an X509 certificate validation on the configured + * asserting party, IDP, verification certificates. + * + * @author Josh Cummings + * @since 5.5 + * @see <a href= + * "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=38">SAML 2 + * StatusResponse</a> + * @see <a href="https://wiki.shibboleth.net/confluence/display/OS30/Home">OpenSAML 3</a> + */ +public final class OpenSaml4AuthenticationProvider implements AuthenticationProvider { + + static { + OpenSamlInitializationService.initialize(); + } + + private final Log logger = LogFactory.getLog(this.getClass()); + + private final ResponseUnmarshaller responseUnmarshaller; + + private final ParserPool parserPool; + + private final Converter<ResponseToken, Saml2ResponseValidatorResult> responseSignatureValidator = createDefaultResponseSignatureValidator(); + + private Consumer<ResponseToken> responseElementsDecrypter = createDefaultResponseElementsDecrypter(); + + private final Converter<ResponseToken, Saml2ResponseValidatorResult> responseValidator = createDefaultResponseValidator(); + + private final Converter<AssertionToken, Saml2ResponseValidatorResult> assertionSignatureValidator = createDefaultAssertionSignatureValidator(); + + private Consumer<AssertionToken> assertionElementsDecrypter = createDefaultAssertionElementsDecrypter(); + + private Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator = createDefaultAssertionValidator(); + + private Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter = createDefaultResponseAuthenticationConverter(); + + /** + * Creates an {@link OpenSaml4AuthenticationProvider} + */ + public OpenSaml4AuthenticationProvider() { + XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); + this.responseUnmarshaller = (ResponseUnmarshaller) registry.getUnmarshallerFactory() + .getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); + this.parserPool = registry.getParserPool(); + } + + /** + * Set the {@link Consumer} strategy to use for decrypting elements of a validated + * {@link Response}. The default strategy decrypts all {@link EncryptedAssertion}s + * using OpenSAML's {@link Decrypter}, adding the results to + * {@link Response#getAssertions()}. + * + * You can use this method to configure the {@link Decrypter} instance like so: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * provider.setResponseElementsDecrypter((responseToken) -> { + * DecrypterParameters parameters = new DecrypterParameters(); + * // ... set parameters as needed + * Decrypter decrypter = new Decrypter(parameters); + * Response response = responseToken.getResponse(); + * EncryptedAssertion encrypted = response.getEncryptedAssertions().get(0); + * try { + * Assertion assertion = decrypter.decrypt(encrypted); + * response.getAssertions().add(assertion); + * } catch (Exception e) { + * throw new Saml2AuthenticationException(...); + * } + * }); + * </pre> + * + * Or, in the event that you have your own custom decryption interface, the same + * pattern applies: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * Converter<EncryptedAssertion, Assertion> myService = ... + * provider.setResponseDecrypter((responseToken) -> { + * Response response = responseToken.getResponse(); + * response.getEncryptedAssertions().stream() + * .map(service::decrypt).forEach(response.getAssertions()::add); + * }); + * </pre> + * + * This is valuable when using an external service to perform the decryption. + * @param responseElementsDecrypter the {@link Consumer} for decrypting response + * elements + * @since 5.5 + */ + public void setResponseElementsDecrypter(Consumer<ResponseToken> responseElementsDecrypter) { + Assert.notNull(responseElementsDecrypter, "responseElementsDecrypter cannot be null"); + this.responseElementsDecrypter = responseElementsDecrypter; + } + + /** + * Set the {@link Converter} to use for validating each {@link Assertion} in the SAML + * 2.0 Response. + * + * You can still invoke the default validator by delgating to + * {@link #createAssertionValidator}, like so: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * provider.setAssertionValidator(assertionToken -> { + * Saml2ResponseValidatorResult result = createDefaultAssertionValidator() + * .convert(assertionToken) + * return result.concat(myCustomValidator.convert(assertionToken)); + * }); + * </pre> + * + * You can also use this method to configure the provider to use a different + * {@link ValidationContext} from the default, like so: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * provider.setAssertionValidator( + * createDefaultAssertionValidator(assertionToken -> { + * Map<String, Object> params = new HashMap<>(); + * params.put(CLOCK_SKEW, 2 * 60 * 1000); + * // other parameters + * return new ValidationContext(params); + * })); + * </pre> + * + * Consider taking a look at {@link #createValidationContext} to see how it constructs + * a {@link ValidationContext}. + * + * It is not necessary to delegate to the default validator. You can safely replace it + * entirely with your own. Note that signature verification is performed as a separate + * step from this validator. + * @param assertionValidator the validator to use + * @since 5.4 + */ + public void setAssertionValidator(Converter<AssertionToken, Saml2ResponseValidatorResult> assertionValidator) { + Assert.notNull(assertionValidator, "assertionValidator cannot be null"); + this.assertionValidator = assertionValidator; + } + + /** + * Set the {@link Consumer} strategy to use for decrypting elements of a validated + * {@link Assertion}. + * + * You can use this method to configure the {@link Decrypter} used like so: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * provider.setResponseDecrypter((assertionToken) -> { + * DecrypterParameters parameters = new DecrypterParameters(); + * // ... set parameters as needed + * Decrypter decrypter = new Decrypter(parameters); + * Assertion assertion = assertionToken.getAssertion(); + * EncryptedID encrypted = assertion.getSubject().getEncryptedID(); + * try { + * NameID name = decrypter.decrypt(encrypted); + * assertion.getSubject().setNameID(name); + * } catch (Exception e) { + * throw new Saml2AuthenticationException(...); + * } + * }); + * </pre> + * + * Or, in the event that you have your own custom interface, the same pattern applies: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * MyDecryptionService myService = ... + * provider.setResponseDecrypter((responseToken) -> { + * Assertion assertion = assertionToken.getAssertion(); + * EncryptedID encrypted = assertion.getSubject().getEncryptedID(); + * NameID name = myService.decrypt(encrypted); + * assertion.getSubject().setNameID(name); + * }); + * </pre> + * @param assertionDecrypter the {@link Consumer} for decrypting assertion elements + * @since 5.5 + */ + public void setAssertionElementsDecrypter(Consumer<AssertionToken> assertionDecrypter) { + Assert.notNull(assertionDecrypter, "assertionDecrypter cannot be null"); + this.assertionElementsDecrypter = assertionDecrypter; + } + + /** + * Set the {@link Converter} to use for converting a validated {@link Response} into + * an {@link AbstractAuthenticationToken}. + * + * You can delegate to the default behavior by calling + * {@link #createDefaultResponseAuthenticationConverter()} like so: + * + * <pre> + * OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); + * Converter<ResponseToken, Saml2Authentication> authenticationConverter = + * createDefaultResponseAuthenticationConverter(); + * provider.setResponseAuthenticationConverter(responseToken -> { + * Saml2Authentication authentication = authenticationConverter.convert(responseToken); + * User user = myUserRepository.findByUsername(authentication.getName()); + * return new MyAuthentication(authentication, user); + * }); + * </pre> + * @param responseAuthenticationConverter the {@link Converter} to use + * @since 5.4 + */ + public void setResponseAuthenticationConverter( + Converter<ResponseToken, ? extends AbstractAuthenticationToken> responseAuthenticationConverter) { + Assert.notNull(responseAuthenticationConverter, "responseAuthenticationConverter cannot be null"); + this.responseAuthenticationConverter = responseAuthenticationConverter; + } + + /** + * Construct a default strategy for validating each SAML 2.0 Assertion and associated + * {@link Authentication} token + * @return the default assertion validator strategy + */ + public static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidator() { + + return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, + (assertionToken) -> SAML20AssertionValidators.attributeValidator, + (assertionToken) -> createValidationContext(assertionToken, (params) -> params + .put(SAML2AssertionValidationParameters.CLOCK_SKEW, Duration.ofMinutes(5).toMillis()))); + } + + /** + * Construct a default strategy for validating each SAML 2.0 Assertion and associated + * {@link Authentication} token + * @param contextConverter the conversion strategy to use to generate a + * {@link ValidationContext} for each assertion being validated + * @return the default assertion validator strategy + */ + public static Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionValidator( + Converter<AssertionToken, ValidationContext> contextConverter) { + + return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, + (assertionToken) -> SAML20AssertionValidators.attributeValidator, contextConverter); + } + + /** + * Construct a default strategy for converting a SAML 2.0 Response and + * {@link Authentication} token into a {@link Saml2Authentication} + * @return the default response authentication converter strategy + */ + public static Converter<ResponseToken, Saml2Authentication> createDefaultResponseAuthenticationConverter() { + return (responseToken) -> { + Response response = responseToken.response; + Saml2AuthenticationToken token = responseToken.token; + Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); + String username = assertion.getSubject().getNameID().getValue(); + Map<String, List<Object>> attributes = getAssertionAttributes(assertion); + return new Saml2Authentication(new DefaultSaml2AuthenticatedPrincipal(username, attributes), + token.getSaml2Response(), AuthorityUtils.createAuthorityList("ROLE_USER")); + }; + } + + /** + * @param authentication the authentication request object, must be of type + * {@link Saml2AuthenticationToken} + * @return {@link Saml2Authentication} if the assertion is valid + * @throws AuthenticationException if a validation exception occurs + */ + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + try { + Saml2AuthenticationToken token = (Saml2AuthenticationToken) authentication; + String serializedResponse = token.getSaml2Response(); + Response response = parse(serializedResponse); + process(token, response); + return this.responseAuthenticationConverter.convert(new ResponseToken(response, token)); + } + catch (Saml2AuthenticationException ex) { + throw ex; + } + catch (Exception ex) { + throw createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex); + } + } + + @Override + public boolean supports(Class<?> authentication) { + return authentication != null && Saml2AuthenticationToken.class.isAssignableFrom(authentication); + } + + private Response parse(String response) throws Saml2Exception, Saml2AuthenticationException { + try { + Document document = this.parserPool + .parse(new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8))); + Element element = document.getDocumentElement(); + return (Response) this.responseUnmarshaller.unmarshall(element); + } + catch (Exception ex) { + throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, ex.getMessage(), ex); + } + } + + private void process(Saml2AuthenticationToken token, Response response) { + String issuer = response.getIssuer().getValue(); + this.logger.debug(LogMessage.format("Processing SAML response from %s", issuer)); + boolean responseSigned = response.isSigned(); + + ResponseToken responseToken = new ResponseToken(response, token); + Saml2ResponseValidatorResult result = this.responseSignatureValidator.convert(responseToken); + if (responseSigned) { + this.responseElementsDecrypter.accept(responseToken); + } + result = result.concat(this.responseValidator.convert(responseToken)); + boolean allAssertionsSigned = true; + for (Assertion assertion : response.getAssertions()) { + AssertionToken assertionToken = new AssertionToken(assertion, token); + result = result.concat(this.assertionSignatureValidator.convert(assertionToken)); + allAssertionsSigned = allAssertionsSigned && assertion.isSigned(); + if (responseSigned || assertion.isSigned()) { + this.assertionElementsDecrypter.accept(new AssertionToken(assertion, token)); + } + result = result.concat(this.assertionValidator.convert(assertionToken)); + } + if (!responseSigned && !allAssertionsSigned) { + String description = "Either the response or one of the assertions is unsigned. " + + "Please either sign the response or all of the assertions."; + throw createAuthenticationException(Saml2ErrorCodes.INVALID_SIGNATURE, description, null); + } + Assertion firstAssertion = CollectionUtils.firstElement(response.getAssertions()); + if (!hasName(firstAssertion)) { + Saml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, + "Assertion [" + firstAssertion.getID() + "] is missing a subject"); + result = result.concat(error); + } + + if (result.hasErrors()) { + Collection<Saml2Error> errors = result.getErrors(); + if (this.logger.isTraceEnabled()) { + this.logger.debug("Found " + errors.size() + " validation errors in SAML response [" + response.getID() + + "]: " + errors); + } + else if (this.logger.isDebugEnabled()) { + this.logger.debug( + "Found " + errors.size() + " validation errors in SAML response [" + response.getID() + "]"); + } + Saml2Error first = errors.iterator().next(); + throw createAuthenticationException(first.getErrorCode(), first.getDescription(), null); + } + else { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Successfully processed SAML Response [" + response.getID() + "]"); + } + } + } + + private Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseSignatureValidator() { + return (responseToken) -> { + Response response = responseToken.getResponse(); + RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); + if (response.isSigned()) { + return OpenSamlVerificationUtils.verifySignature(response, registration).post(response.getSignature()); + } + return Saml2ResponseValidatorResult.success(); + }; + } + + private Consumer<ResponseToken> createDefaultResponseElementsDecrypter() { + return (responseToken) -> { + Response response = responseToken.getResponse(); + RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); + try { + OpenSamlDecryptionUtils.decryptResponseElements(response, registration); + } + catch (Exception ex) { + throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); + } + }; + } + + private Converter<ResponseToken, Saml2ResponseValidatorResult> createDefaultResponseValidator() { + return (responseToken) -> { + Response response = responseToken.getResponse(); + Saml2AuthenticationToken token = responseToken.getToken(); + Saml2ResponseValidatorResult result = Saml2ResponseValidatorResult.success(); + String issuer = response.getIssuer().getValue(); + String destination = response.getDestination(); + String location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation(); + if (StringUtils.hasText(destination) && !destination.equals(location)) { + String message = "Invalid destination [" + destination + "] for SAML response [" + response.getID() + + "]"; + result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message)); + } + String assertingPartyEntityId = token.getRelyingPartyRegistration().getAssertingPartyDetails() + .getEntityId(); + if (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) { + String message = String.format("Invalid issuer [%s] for SAML response [%s]", issuer, response.getID()); + result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, message)); + } + if (response.getAssertions().isEmpty()) { + throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, + "No assertions found in response.", null); + } + return result; + }; + } + + private Converter<AssertionToken, Saml2ResponseValidatorResult> createDefaultAssertionSignatureValidator() { + return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> { + RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); + SignatureTrustEngine engine = OpenSamlVerificationUtils.trustEngine(registration); + return SAML20AssertionValidators.createSignatureValidator(engine); + }, (assertionToken) -> new ValidationContext( + Collections.singletonMap(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false))); + } + + private Consumer<AssertionToken> createDefaultAssertionElementsDecrypter() { + return (assertionToken) -> { + Assertion assertion = assertionToken.getAssertion(); + RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); + try { + OpenSamlDecryptionUtils.decryptAssertionElements(assertion, registration); + } + catch (Exception ex) { + throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); + } + }; + } + + private boolean hasName(Assertion assertion) { + if (assertion == null) { + return false; + } + if (assertion.getSubject() == null) { + return false; + } + if (assertion.getSubject().getNameID() == null) { + return false; + } + return assertion.getSubject().getNameID().getValue() != null; + } + + private static Map<String, List<Object>> getAssertionAttributes(Assertion assertion) { + Map<String, List<Object>> attributeMap = new LinkedHashMap<>(); + for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) { + for (Attribute attribute : attributeStatement.getAttributes()) { + List<Object> attributeValues = new ArrayList<>(); + for (XMLObject xmlObject : attribute.getAttributeValues()) { + Object attributeValue = getXmlObjectValue(xmlObject); + if (attributeValue != null) { + attributeValues.add(attributeValue); + } + } + attributeMap.put(attribute.getName(), attributeValues); + } + } + return attributeMap; + } + + private static Object getXmlObjectValue(XMLObject xmlObject) { + if (xmlObject instanceof XSAny) { + return ((XSAny) xmlObject).getTextContent(); + } + if (xmlObject instanceof XSString) { + return ((XSString) xmlObject).getValue(); + } + if (xmlObject instanceof XSInteger) { + return ((XSInteger) xmlObject).getValue(); + } + if (xmlObject instanceof XSURI) { + return ((XSURI) xmlObject).getURI(); + } + if (xmlObject instanceof XSBoolean) { + XSBooleanValue xsBooleanValue = ((XSBoolean) xmlObject).getValue(); + return (xsBooleanValue != null) ? xsBooleanValue.getValue() : null; + } + if (xmlObject instanceof XSDateTime) { + return ((XSDateTime) xmlObject).getValue(); + } + return null; + } + + private static Saml2AuthenticationException createAuthenticationException(String code, String message, + Exception cause) { + return new Saml2AuthenticationException(new Saml2Error(code, message), cause); + } + + private static Converter<AssertionToken, Saml2ResponseValidatorResult> createAssertionValidator(String errorCode, + Converter<AssertionToken, SAML20AssertionValidator> validatorConverter, + Converter<AssertionToken, ValidationContext> contextConverter) { + + return (assertionToken) -> { + Assertion assertion = assertionToken.assertion; + SAML20AssertionValidator validator = validatorConverter.convert(assertionToken); + ValidationContext context = contextConverter.convert(assertionToken); + try { + ValidationResult result = validator.validate(assertion, context); + if (result == ValidationResult.VALID) { + return Saml2ResponseValidatorResult.success(); + } + } + catch (Exception ex) { + String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), + ((Response) assertion.getParent()).getID(), ex.getMessage()); + return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); + } + String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), + ((Response) assertion.getParent()).getID(), context.getValidationFailureMessage()); + return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); + }; + } + + private static ValidationContext createValidationContext(AssertionToken assertionToken, + Consumer<Map<String, Object>> paramsConsumer) { + String audience = assertionToken.token.getRelyingPartyRegistration().getEntityId(); + String recipient = assertionToken.token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation(); + Map<String, Object> params = new HashMap<>(); + params.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience)); + params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient)); + paramsConsumer.accept(params); + return new ValidationContext(params); + } + + private static class SAML20AssertionValidators { + + private static final Collection<ConditionValidator> conditions = new ArrayList<>(); + + private static final Collection<SubjectConfirmationValidator> subjects = new ArrayList<>(); + + private static final Collection<StatementValidator> statements = new ArrayList<>(); + + private static final SignaturePrevalidator validator = new SAMLSignatureProfileValidator(); + + static { + conditions.add(new AudienceRestrictionConditionValidator()); + conditions.add(new DelegationRestrictionConditionValidator()); + conditions.add(new ConditionValidator() { + @Nonnull + @Override + public QName getServicedCondition() { + return OneTimeUse.DEFAULT_ELEMENT_NAME; + } + + @Nonnull + @Override + public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) { + // applications should validate their own OneTimeUse conditions + return ValidationResult.VALID; + } + }); + subjects.add(new BearerSubjectConfirmationValidator() { + @Override + protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion, + ValidationContext context, boolean required) { + // applications should validate their own addresses - gh-7514 + return ValidationResult.VALID; + } + + @Override + protected ValidationResult validateInResponseTo(SubjectConfirmation confirmation, Assertion assertion, + ValidationContext context, boolean required) { + // applications should validate their own in response to + return ValidationResult.VALID; + } + }); + } + + private static final SAML20AssertionValidator attributeValidator = new SAML20AssertionValidator(conditions, + subjects, statements, null, null, null) { + @Nonnull + @Override + protected ValidationResult validateSignature(Assertion token, ValidationContext context) { + return ValidationResult.VALID; + } + }; + + static SAML20AssertionValidator createSignatureValidator(SignatureTrustEngine engine) { + return new SAML20AssertionValidator(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), null, engine, + validator) { + @Nonnull + @Override + protected ValidationResult validateConditions(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + + @Nonnull + @Override + protected ValidationResult validateSubjectConfirmation(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + + @Nonnull + @Override + protected ValidationResult validateStatements(Assertion assertion, ValidationContext context) { + return ValidationResult.VALID; + } + }; + + } + + } + + /** + * A tuple containing an OpenSAML {@link Response} and its associated authentication + * token. + * + * @since 5.4 + */ + public static class ResponseToken { + + private final Saml2AuthenticationToken token; + + private final Response response; + + ResponseToken(Response response, Saml2AuthenticationToken token) { + this.token = token; + this.response = response; + } + + public Response getResponse() { + return this.response; + } + + public Saml2AuthenticationToken getToken() { + return this.token; + } + + } + + /** + * A tuple containing an OpenSAML {@link Assertion} and its associated authentication + * token. + * + * @since 5.4 + */ + public static class AssertionToken { + + private final Saml2AuthenticationToken token; + + private final Assertion assertion; + + AssertionToken(Assertion assertion, Saml2AuthenticationToken token) { + this.token = token; + this.assertion = assertion; + } + + public Assertion getAssertion() { + return this.assertion; + } + + public Saml2AuthenticationToken getToken() { + return this.token; + } + + } + +} diff --git a/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactory.java b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactory.java new file mode 100644 index 00000000000..30a3af18351 --- /dev/null +++ b/saml2/saml2-service-provider/opensaml4/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactory.java @@ -0,0 +1,180 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder; +import org.opensaml.saml.saml2.core.impl.IssuerBuilder; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.saml2.core.OpenSamlInitializationService; +import org.springframework.security.saml2.provider.service.authentication.OpenSamlSigningUtils.QueryParametersPartial; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * A {@link Saml2AuthenticationRequestFactory} that generates, signs, and serializes a + * SAML 2.0 AuthnRequest using OpenSAML 4 + * + * @author Josh Cummings + * @since 5.5 + */ +public final class OpenSaml4AuthenticationRequestFactory implements Saml2AuthenticationRequestFactory { + + static { + OpenSamlInitializationService.initialize(); + } + + private final AuthnRequestBuilder authnRequestBuilder; + + private final IssuerBuilder issuerBuilder; + + private Clock clock = Clock.systemUTC(); + + private Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter; + + /** + * Creates an {@link OpenSaml4AuthenticationRequestFactory} + */ + public OpenSaml4AuthenticationRequestFactory() { + this.authenticationRequestContextConverter = this::createAuthnRequest; + XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); + this.authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory() + .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); + this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public String createAuthenticationRequest(Saml2AuthenticationRequest request) { + RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("noId") + .assertionConsumerServiceBinding(Saml2MessageBinding.POST) + .assertionConsumerServiceLocation(request.getAssertionConsumerServiceUrl()) + .entityId(request.getIssuer()).remoteIdpEntityId("noIssuer").idpWebSsoUrl("noUrl") + .credentials((credentials) -> credentials.addAll(request.getCredentials())).build(); + Saml2AuthenticationRequestContext context = Saml2AuthenticationRequestContext.builder() + .relyingPartyRegistration(registration).issuer(request.getIssuer()) + .assertionConsumerServiceUrl(request.getAssertionConsumerServiceUrl()).build(); + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + return OpenSamlSigningUtils.serialize(OpenSamlSigningUtils.sign(authnRequest, registration)); + } + + /** + * {@inheritDoc} + */ + @Override + public Saml2PostAuthenticationRequest createPostAuthenticationRequest(Saml2AuthenticationRequestContext context) { + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + RelyingPartyRegistration registration = context.getRelyingPartyRegistration(); + if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned()) { + OpenSamlSigningUtils.sign(authnRequest, registration); + } + String xml = OpenSamlSigningUtils.serialize(authnRequest); + return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context) + .samlRequest(Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8))).build(); + } + + /** + * {@inheritDoc} + */ + @Override + public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest( + Saml2AuthenticationRequestContext context) { + AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); + RelyingPartyRegistration registration = context.getRelyingPartyRegistration(); + String xml = OpenSamlSigningUtils.serialize(authnRequest); + Saml2RedirectAuthenticationRequest.Builder result = Saml2RedirectAuthenticationRequest + .withAuthenticationRequestContext(context); + String deflatedAndEncoded = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(xml)); + result.samlRequest(deflatedAndEncoded).relayState(context.getRelayState()); + if (registration.getAssertingPartyDetails().getWantAuthnRequestsSigned()) { + QueryParametersPartial partial = OpenSamlSigningUtils.sign(registration).param("SAMLRequest", + deflatedAndEncoded); + if (StringUtils.hasText(context.getRelayState())) { + partial.param("RelayState", context.getRelayState()); + } + Map<String, String> parameters = partial.parameters(); + return result.sigAlg(parameters.get("SigAlg")).signature(parameters.get("Signature")).build(); + } + return result.build(); + } + + private AuthnRequest createAuthnRequest(Saml2AuthenticationRequestContext context) { + String issuer = context.getIssuer(); + String destination = context.getDestination(); + String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl(); + String protocolBinding = context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn(); + AuthnRequest auth = this.authnRequestBuilder.buildObject(); + if (auth.getID() == null) { + auth.setID("ARQ" + UUID.randomUUID().toString().substring(1)); + } + if (auth.getIssueInstant() == null) { + auth.setIssueInstant(Instant.now(this.clock)); + } + if (auth.isForceAuthn() == null) { + auth.setForceAuthn(Boolean.FALSE); + } + if (auth.isPassive() == null) { + auth.setIsPassive(Boolean.FALSE); + } + if (auth.getProtocolBinding() == null) { + auth.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI); + } + auth.setProtocolBinding(protocolBinding); + Issuer iss = this.issuerBuilder.buildObject(); + iss.setValue(issuer); + auth.setIssuer(iss); + auth.setDestination(destination); + auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl); + return auth; + } + + /** + * Set the strategy for building an {@link AuthnRequest} from a given context + * @param authenticationRequestContextConverter the conversion strategy to use + */ + public void setAuthenticationRequestContextConverter( + Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter) { + Assert.notNull(authenticationRequestContextConverter, "authenticationRequestContextConverter cannot be null"); + this.authenticationRequestContextConverter = authenticationRequestContextConverter; + } + + /** + * Use this {@link Clock} with {@link Instant#now()} for generating timestamps + * @param clock the {@link Clock} to use + */ + public void setClock(Clock clock) { + Assert.notNull(clock, "clock cannot be null"); + this.clock = clock; + } + +} diff --git a/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java new file mode 100644 index 00000000000..bc94bc3b2b8 --- /dev/null +++ b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java @@ -0,0 +1,661 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import javax.xml.namespace.QName; + +import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import org.junit.Test; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.core.xml.io.Marshaller; +import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.schema.XSDateTime; +import org.opensaml.core.xml.schema.impl.XSDateTimeBuilder; +import org.opensaml.saml.common.assertion.ValidationContext; +import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.AttributeValue; +import org.opensaml.saml.saml2.core.Conditions; +import org.opensaml.saml.saml2.core.EncryptedAssertion; +import org.opensaml.saml.saml2.core.EncryptedAttribute; +import org.opensaml.saml.saml2.core.EncryptedID; +import org.opensaml.saml.saml2.core.NameID; +import org.opensaml.saml.saml2.core.OneTimeUse; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml.saml2.core.impl.AttributeBuilder; +import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder; +import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder; +import org.opensaml.saml.saml2.core.impl.NameIDBuilder; +import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.w3c.dom.Element; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2Error; +import org.springframework.security.saml2.core.Saml2ErrorCodes; +import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; +import org.springframework.security.saml2.core.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; +import org.springframework.util.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link OpenSaml4AuthenticationProvider} + * + * @author Filip Hanik + * @author Josh Cummings + */ +public class OpenSaml4AuthenticationProviderTests { + + private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias"; + + private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias"; + + private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp"; + + private OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + + private Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("name", + Collections.emptyMap()); + + private Saml2Authentication authentication = new Saml2Authentication(this.principal, "response", + Collections.emptyList()); + + @Test + public void supportsWhenSaml2AuthenticationTokenThenReturnTrue() { + assertThat(this.provider.supports(Saml2AuthenticationToken.class)) + .withFailMessage( + OpenSaml4AuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class) + .isTrue(); + } + + @Test + public void supportsWhenNotSaml2AuthenticationTokenThenReturnFalse() { + assertThat(!this.provider.supports(Authentication.class)) + .withFailMessage(OpenSaml4AuthenticationProvider.class + "should not support " + Authentication.class) + .isTrue(); + } + + @Test + public void authenticateWhenUnknownDataClassThenThrowAuthenticationException() { + Assertion assertion = (Assertion) XMLObjectProviderRegistrySupport.getBuilderFactory() + .getBuilder(Assertion.DEFAULT_ELEMENT_NAME).buildObject(Assertion.DEFAULT_ELEMENT_NAME); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate( + new Saml2AuthenticationToken(verifying(registration()).build(), serialize(assertion)))) + .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA)); + } + + @Test + public void authenticateWhenXmlErrorThenThrowAuthenticationException() { + Saml2AuthenticationToken token = new Saml2AuthenticationToken(verifying(registration()).build(), "invalid xml"); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA)); + } + + @Test + public void authenticateWhenInvalidDestinationThenThrowAuthenticationException() { + Response response = response(DESTINATION + "invalid", ASSERTING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion()); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.INVALID_DESTINATION)); + } + + @Test + public void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException() { + Saml2AuthenticationToken token = token(); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, "No assertions found in response.")); + } + + @Test + public void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationException() { + Response response = response(); + response.getAssertions().add(assertion()); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE)); + } + + @Test + public void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationException() { + Response response = response(); + Assertion assertion = assertion(); + assertion.getSubject().getSubjectConfirmations().get(0).getSubjectConfirmationData() + .setNotOnOrAfter(Instant.now().minus(Duration.ofDays(3))); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.INVALID_ASSERTION)); + } + + @Test + public void authenticateWhenMissingSubjectThenThrowAuthenticationException() { + Response response = response(); + Assertion assertion = assertion(); + assertion.setSubject(null); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND)); + } + + @Test + public void authenticateWhenUsernameMissingThenThrowAuthenticationException() { + Response response = response(); + Assertion assertion = assertion(); + assertion.getSubject().getNameID().setValue(null); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND)); + } + + @Test + public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() { + Response response = response(); + Assertion assertion = assertion(); + assertion.getSubject().getSubjectConfirmations() + .forEach((sc) -> sc.getSubjectConfirmationData().setAddress("10.10.10.10")); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + this.provider.authenticate(token); + } + + @Test + public void authenticateWhenAssertionContainsAttributesThenItSucceeds() { + Response response = response(); + Assertion assertion = assertion(); + List<AttributeStatement> attributes = attributeStatements(); + assertion.getAttributeStatements().addAll(attributes); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + Authentication authentication = this.provider.authenticate(token); + Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); + Map<String, Object> expected = new LinkedHashMap<>(); + expected.put("email", Arrays.asList("john.doe@example.com", "doe.john@example.com")); + expected.put("name", Collections.singletonList("John Doe")); + expected.put("age", Collections.singletonList(21)); + expected.put("website", Collections.singletonList("https://johndoe.com/")); + expected.put("registered", Collections.singletonList(true)); + Instant registeredDate = Instant.parse("1970-01-01T00:00:00Z"); + expected.put("registeredDate", Collections.singletonList(registeredDate)); + assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe"); + assertThat(principal.getAttributes()).isEqualTo(expected); + } + + @Test + public void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() { + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE)); + } + + @Test + public void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() { + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), + TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); + this.provider.authenticate(token); + } + + @Test + public void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceeds() { + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); + this.provider.authenticate(token); + } + + @Test + public void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() { + Response response = response(); + Assertion assertion = assertion(); + NameID nameId = assertion.getSubject().getNameID(); + EncryptedID encryptedID = TestOpenSamlObjects.encrypted(nameId, + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + assertion.getSubject().setNameID(null); + assertion.getSubject().setEncryptedID(encryptedID); + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); + this.provider.authenticate(token); + } + + @Test + public void authenticateWhenEncryptedAttributeThenDecrypts() { + Response response = response(); + Assertion assertion = assertion(); + EncryptedAttribute attribute = TestOpenSamlObjects.encrypted("name", "value", + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + AttributeStatement statement = build(AttributeStatement.DEFAULT_ELEMENT_NAME); + statement.getEncryptedAttributes().add(attribute); + assertion.getAttributeStatements().add(statement); + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); + Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token); + Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); + assertThat(principal.getAttribute("name")).containsExactly("value"); + } + + @Test + public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() { + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")); + } + + @Test + public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() { + Response response = response(); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, registration() + .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyPrivateCredential()))); + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> this.provider.authenticate(token)) + .satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")); + } + + @Test + public void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException { + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), + TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); + EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, + TestSaml2X509Credentials.assertingPartyEncryptingCredential()); + response.getEncryptedAssertions().add(encryptedAssertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); + Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token); + // the following code will throw an exception if authentication isn't serializable + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream); + objectOutputStream.writeObject(authentication); + objectOutputStream.flush(); + } + + @Test + public void createDefaultAssertionValidatorWhenAssertionThenValidates() { + Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); + Assertion assertion = response.getAssertions().get(0); + OpenSaml4AuthenticationProvider.AssertionToken assertionToken = new OpenSaml4AuthenticationProvider.AssertionToken( + assertion, token()); + assertThat( + OpenSaml4AuthenticationProvider.createDefaultAssertionValidator().convert(assertionToken).hasErrors()) + .isFalse(); + } + + @Test + public void authenticateWhenDelegatingToDefaultAssertionValidatorThenUses() { + OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + // @formatter:off + provider.setAssertionValidator((assertionToken) -> OpenSaml4AuthenticationProvider + .createDefaultAssertionValidator((token) -> new ValidationContext()) + .convert(assertionToken) + .concat(new Saml2Error("wrong error", "wrong error")) + ); + // @formatter:on + Response response = response(); + Assertion assertion = assertion(); + OneTimeUse oneTimeUse = build(OneTimeUse.DEFAULT_ELEMENT_NAME); + assertion.getConditions().getConditions().add(oneTimeUse); + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + ASSERTING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + // @formatter:off + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class) + .satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_ASSERTION)); + // @formatter:on + } + + @Test + public void authenticateWhenCustomAssertionValidatorThenUses() { + Converter<OpenSaml4AuthenticationProvider.AssertionToken, Saml2ResponseValidatorResult> validator = mock( + Converter.class); + OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + // @formatter:off + provider.setAssertionValidator((assertionToken) -> OpenSaml4AuthenticationProvider.createDefaultAssertionValidator() + .convert(assertionToken) + .concat(validator.convert(assertionToken)) + ); + // @formatter:on + Response response = response(); + Assertion assertion = assertion(); + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + ASSERTING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + given(validator.convert(any(OpenSaml4AuthenticationProvider.AssertionToken.class))) + .willReturn(Saml2ResponseValidatorResult.success()); + provider.authenticate(token); + verify(validator).convert(any(OpenSaml4AuthenticationProvider.AssertionToken.class)); + } + + @Test + public void authenticateWhenDefaultConditionValidatorNotUsedThenSignatureStillChecked() { + OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + provider.setAssertionValidator((assertionToken) -> Saml2ResponseValidatorResult.success()); + Response response = response(); + Assertion assertion = assertion(); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.relyingPartyDecryptingCredential(), + RELYING_PARTY_ENTITY_ID); // broken + // signature + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + ASSERTING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + // @formatter:off + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> provider.authenticate(token)) + .satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_SIGNATURE)); + // @formatter:on + } + + @Test + public void authenticateWhenValidationContextCustomizedThenUsers() { + Map<String, Object> parameters = new HashMap<>(); + parameters.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton("blah")); + ValidationContext context = mock(ValidationContext.class); + given(context.getStaticParameters()).willReturn(parameters); + OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + provider.setAssertionValidator( + OpenSaml4AuthenticationProvider.createDefaultAssertionValidator((assertionToken) -> context)); + Response response = response(); + Assertion assertion = assertion(); + response.getAssertions().add(assertion); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + ASSERTING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + // @formatter:off + assertThatExceptionOfType(Saml2AuthenticationException.class) + .isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class) + .satisfies((error) -> assertThat(error).hasMessageContaining("Invalid assertion")); + // @formatter:on + verify(context, atLeastOnce()).getStaticParameters(); + } + + @Test + public void authenticateWithSHA1SignatureThenItSucceeds() throws Exception { + Response response = response(); + Assertion assertion = TestOpenSamlObjects.signed(assertion(), + TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID, + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + this.provider.authenticate(token); + } + + @Test + public void setAssertionValidatorWhenNullThenIllegalArgument() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.provider.setAssertionValidator(null)); + // @formatter:on + } + + @Test + public void createDefaultResponseAuthenticationConverterWhenResponseThenConverts() { + Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); + Saml2AuthenticationToken token = token(response, verifying(registration())); + ResponseToken responseToken = new ResponseToken(response, token); + Saml2Authentication authentication = OpenSaml4AuthenticationProvider + .createDefaultResponseAuthenticationConverter().convert(responseToken); + assertThat(authentication.getName()).isEqualTo("test@saml.user"); + } + + @Test + public void authenticateWhenResponseAuthenticationConverterConfiguredThenUses() { + Converter<ResponseToken, Saml2Authentication> authenticationConverter = mock(Converter.class); + OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); + provider.setResponseAuthenticationConverter(authenticationConverter); + Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); + Saml2AuthenticationToken token = token(response, verifying(registration())); + provider.authenticate(token); + verify(authenticationConverter).convert(any()); + } + + @Test + public void setResponseAuthenticationConverterWhenNullThenIllegalArgument() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.provider.setResponseAuthenticationConverter(null)); + // @formatter:on + } + + @Test + public void setResponseElementsDecrypterWhenNullThenIllegalArgument() { + assertThatIllegalArgumentException().isThrownBy(() -> this.provider.setResponseElementsDecrypter(null)); + } + + @Test + public void setAssertionElementsDecrypterWhenNullThenIllegalArgument() { + assertThatIllegalArgumentException().isThrownBy(() -> this.provider.setAssertionElementsDecrypter(null)); + } + + @Test + public void authenticateWhenCustomResponseElementsDecrypterThenDecryptsResponse() { + Response response = response(); + Assertion assertion = assertion(); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getEncryptedAssertions().add(new EncryptedAssertionBuilder().buildObject()); + TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + Saml2AuthenticationToken token = token(response, verifying(registration())); + this.provider.setResponseElementsDecrypter((tuple) -> tuple.getResponse().getAssertions().add(assertion)); + Authentication authentication = this.provider.authenticate(token); + assertThat(authentication.getName()).isEqualTo("test@saml.user"); + } + + @Test + public void authenticateWhenCustomAssertionElementsDecrypterThenDecryptsAssertion() { + Response response = response(); + Assertion assertion = assertion(); + EncryptedID id = new EncryptedIDBuilder().buildObject(); + id.setEncryptedData(new EncryptedDataBuilder().buildObject()); + assertion.getSubject().setEncryptedID(id); + TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), + RELYING_PARTY_ENTITY_ID); + response.getAssertions().add(assertion); + Saml2AuthenticationToken token = token(response, verifying(registration())); + this.provider.setAssertionElementsDecrypter((tuple) -> { + NameID name = new NameIDBuilder().buildObject(); + name.setValue("decrypted name"); + tuple.getAssertion().getSubject().setNameID(name); + }); + Authentication authentication = this.provider.authenticate(token); + assertThat(authentication.getName()).isEqualTo("decrypted name"); + } + + private <T extends XMLObject> T build(QName qName) { + return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName); + } + + private String serialize(XMLObject object) { + try { + Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object); + Element element = marshaller.marshall(object); + return SerializeSupport.nodeToString(element); + } + catch (MarshallingException ex) { + throw new Saml2Exception(ex); + } + } + + private Consumer<Saml2AuthenticationException> errorOf(String errorCode) { + return errorOf(errorCode, null); + } + + private Consumer<Saml2AuthenticationException> errorOf(String errorCode, String description) { + return (ex) -> { + assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(errorCode); + if (StringUtils.hasText(description)) { + assertThat(ex.getSaml2Error().getDescription()).contains(description); + } + }; + } + + private Response response() { + Response response = TestOpenSamlObjects.response(); + response.setIssueInstant(Instant.now()); + return response; + } + + private Response response(String destination, String issuerEntityId) { + Response response = TestOpenSamlObjects.response(destination, issuerEntityId); + response.setIssueInstant(Instant.now()); + return response; + } + + private Assertion assertion() { + Assertion assertion = TestOpenSamlObjects.assertion(); + assertion.setIssueInstant(Instant.now()); + for (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) { + SubjectConfirmationData data = confirmation.getSubjectConfirmationData(); + data.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000))); + data.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000))); + } + Conditions conditions = assertion.getConditions(); + conditions.setNotBefore(Instant.now().minus(Duration.ofMillis(5 * 60 * 1000))); + conditions.setNotOnOrAfter(Instant.now().plus(Duration.ofMillis(5 * 60 * 1000))); + return assertion; + } + + private List<AttributeStatement> attributeStatements() { + List<AttributeStatement> attributeStatements = TestOpenSamlObjects.attributeStatements(); + AttributeBuilder attributeBuilder = new AttributeBuilder(); + Attribute registeredDateAttr = attributeBuilder.buildObject(); + registeredDateAttr.setName("registeredDate"); + XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, + XSDateTime.TYPE_NAME); + registeredDate.setValue(Instant.parse("1970-01-01T00:00:00Z")); + registeredDateAttr.getAttributeValues().add(registeredDate); + attributeStatements.iterator().next().getAttributes().add(registeredDateAttr); + return attributeStatements; + } + + private Saml2AuthenticationToken token() { + Response response = response(); + RelyingPartyRegistration registration = verifying(registration()).build(); + return new Saml2AuthenticationToken(registration, serialize(response)); + } + + private Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration) { + return new Saml2AuthenticationToken(registration.build(), serialize(response)); + } + + private RelyingPartyRegistration.Builder registration() { + return TestRelyingPartyRegistrations.noCredentials().entityId(RELYING_PARTY_ENTITY_ID) + .assertionConsumerServiceLocation(DESTINATION) + .assertingPartyDetails((party) -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); + } + + private RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { + return builder.assertingPartyDetails((party) -> party + .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); + } + + private RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) { + return builder + .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential())); + } + +} diff --git a/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactoryTests.java b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactoryTests.java new file mode 100644 index 00000000000..0297d10f7fe --- /dev/null +++ b/saml2/saml2-service-provider/opensaml4/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationRequestFactoryTests.java @@ -0,0 +1,274 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.authentication; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.time.Instant; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.impl.AuthnRequestUnmarshaller; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.saml2.Saml2Exception; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.credentials.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link OpenSaml4AuthenticationRequestFactory} + */ +public class OpenSaml4AuthenticationRequestFactoryTests { + + private OpenSaml4AuthenticationRequestFactory factory; + + private Saml2AuthenticationRequestContext.Builder contextBuilder; + + private Saml2AuthenticationRequestContext context; + + private RelyingPartyRegistration.Builder relyingPartyRegistrationBuilder; + + private RelyingPartyRegistration relyingPartyRegistration; + + private AuthnRequestUnmarshaller unmarshaller; + + @Before + public void setUp() { + this.relyingPartyRegistrationBuilder = RelyingPartyRegistration.withRegistrationId("id") + .assertionConsumerServiceLocation("template") + .providerDetails((c) -> c.webSsoUrl("https://destination/sso")) + .providerDetails((c) -> c.entityId("remote-entity-id")).localEntityIdTemplate("local-entity-id") + .credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartySigningCredential())); + this.relyingPartyRegistration = this.relyingPartyRegistrationBuilder.build(); + this.contextBuilder = Saml2AuthenticationRequestContext.builder().issuer("https://issuer") + .relyingPartyRegistration(this.relyingPartyRegistration) + .assertionConsumerServiceUrl("https://issuer/sso"); + this.context = this.contextBuilder.build(); + this.factory = new OpenSaml4AuthenticationRequestFactory(); + this.unmarshaller = (AuthnRequestUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory() + .getUnmarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME); + } + + @Test + public void createAuthenticationRequestWhenInvokingDeprecatedMethodThenReturnsXML() { + Saml2AuthenticationRequest request = Saml2AuthenticationRequest.withAuthenticationRequestContext(this.context) + .build(); + String result = this.factory.createAuthenticationRequest(request); + assertThat(result.replace("\n", "")) + .startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?><saml2p:AuthnRequest"); + } + + @Test + public void createRedirectAuthenticationRequestWhenUsingContextThenAllValuesAreSet() { + this.context = this.contextBuilder.relayState("Relay State Value").build(); + Saml2RedirectAuthenticationRequest result = this.factory.createRedirectAuthenticationRequest(this.context); + assertThat(result.getSamlRequest()).isNotEmpty(); + assertThat(result.getRelayState()).isEqualTo("Relay State Value"); + assertThat(result.getSigAlg()).isNotEmpty(); + assertThat(result.getSignature()).isNotEmpty(); + assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); + } + + @Test + public void createRedirectAuthenticationRequestWhenNotSignRequestThenNoSignatureIsPresent() { + this.context = this.contextBuilder.relayState("Relay State Value") + .relyingPartyRegistration( + RelyingPartyRegistration.withRelyingPartyRegistration(this.relyingPartyRegistration) + .providerDetails((c) -> c.signAuthNRequest(false)).build()) + .build(); + Saml2RedirectAuthenticationRequest result = this.factory.createRedirectAuthenticationRequest(this.context); + assertThat(result.getSamlRequest()).isNotEmpty(); + assertThat(result.getRelayState()).isEqualTo("Relay State Value"); + assertThat(result.getSigAlg()).isNull(); + assertThat(result.getSignature()).isNull(); + assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); + } + + @Test + public void createRedirectAuthenticationRequestWhenSignRequestThenSignatureIsPresent() { + this.context = this.contextBuilder.relayState("Relay State Value") + .relyingPartyRegistration(this.relyingPartyRegistration).build(); + Saml2RedirectAuthenticationRequest request = this.factory.createRedirectAuthenticationRequest(this.context); + assertThat(request.getRelayState()).isEqualTo("Relay State Value"); + assertThat(request.getSigAlg()).isEqualTo(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256); + assertThat(request.getSignature()).isNotNull(); + } + + @Test + public void createRedirectAuthenticationRequestWhenSignRequestThenCredentialIsRequired() { + Saml2X509Credential credential = org.springframework.security.saml2.core.TestSaml2X509Credentials + .relyingPartyVerifyingCredential(); + RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials() + .assertingPartyDetails((party) -> party.verificationX509Credentials((c) -> c.add(credential))).build(); + this.context = this.contextBuilder.relayState("Relay State Value").relyingPartyRegistration(registration) + .build(); + assertThatExceptionOfType(Saml2Exception.class) + .isThrownBy(() -> this.factory.createPostAuthenticationRequest(this.context)); + } + + @Test + public void createPostAuthenticationRequestWhenNotSignRequestThenNoSignatureIsPresent() { + this.context = this.contextBuilder.relayState("Relay State Value") + .relyingPartyRegistration( + RelyingPartyRegistration.withRelyingPartyRegistration(this.relyingPartyRegistration) + .providerDetails((c) -> c.signAuthNRequest(false)).build()) + .build(); + Saml2PostAuthenticationRequest result = this.factory.createPostAuthenticationRequest(this.context); + assertThat(result.getSamlRequest()).isNotEmpty(); + assertThat(result.getRelayState()).isEqualTo("Relay State Value"); + assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.POST); + assertThat(new String(Saml2Utils.samlDecode(result.getSamlRequest()), StandardCharsets.UTF_8)) + .doesNotContain("ds:Signature"); + } + + @Test + public void createPostAuthenticationRequestWhenSignRequestThenSignatureIsPresent() { + this.context = this.contextBuilder.relayState("Relay State Value") + .relyingPartyRegistration( + RelyingPartyRegistration.withRelyingPartyRegistration(this.relyingPartyRegistration).build()) + .build(); + Saml2PostAuthenticationRequest result = this.factory.createPostAuthenticationRequest(this.context); + assertThat(result.getSamlRequest()).isNotEmpty(); + assertThat(result.getRelayState()).isEqualTo("Relay State Value"); + assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.POST); + assertThat(new String(Saml2Utils.samlDecode(result.getSamlRequest()), StandardCharsets.UTF_8)) + .contains("ds:Signature"); + } + + @Test + public void createPostAuthenticationRequestWhenSignRequestThenCredentialIsRequired() { + Saml2X509Credential credential = org.springframework.security.saml2.core.TestSaml2X509Credentials + .relyingPartyVerifyingCredential(); + RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials() + .assertingPartyDetails((party) -> party.verificationX509Credentials((c) -> c.add(credential))).build(); + this.context = this.contextBuilder.relayState("Relay State Value").relyingPartyRegistration(registration) + .build(); + assertThatExceptionOfType(Saml2Exception.class) + .isThrownBy(() -> this.factory.createPostAuthenticationRequest(this.context)); + } + + @Test + public void createAuthenticationRequestWhenDefaultThenReturnsPostBinding() { + AuthnRequest authn = getAuthNRequest(Saml2MessageBinding.POST); + Assert.assertEquals(SAMLConstants.SAML2_POST_BINDING_URI, authn.getProtocolBinding()); + } + + @Test + public void createPostAuthenticationRequestWhenAuthnRequestConsumerThenUses() { + Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter = mock( + Converter.class); + given(authenticationRequestContextConverter.convert(this.context)).willReturn(authnRequest()); + this.factory.setAuthenticationRequestContextConverter(authenticationRequestContextConverter); + + this.factory.createPostAuthenticationRequest(this.context); + verify(authenticationRequestContextConverter).convert(this.context); + } + + @Test + public void createRedirectAuthenticationRequestWhenAuthnRequestConsumerThenUses() { + Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter = mock( + Converter.class); + given(authenticationRequestContextConverter.convert(this.context)).willReturn(authnRequest()); + this.factory.setAuthenticationRequestContextConverter(authenticationRequestContextConverter); + + this.factory.createRedirectAuthenticationRequest(this.context); + verify(authenticationRequestContextConverter).convert(this.context); + } + + @Test + public void setAuthenticationRequestContextConverterWhenNullThenException() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.factory.setAuthenticationRequestContextConverter(null)); + // @formatter:on + } + + @Test + public void createPostAuthenticationRequestWhenAssertionConsumerServiceBindingThenUses() { + RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationBuilder + .assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT).build(); + Saml2AuthenticationRequestContext context = this.contextBuilder + .relyingPartyRegistration(relyingPartyRegistration).build(); + Saml2PostAuthenticationRequest request = this.factory.createPostAuthenticationRequest(context); + String samlRequest = request.getSamlRequest(); + String inflated = new String(Saml2Utils.samlDecode(samlRequest)); + assertThat(inflated).contains("ProtocolBinding=\"" + SAMLConstants.SAML2_REDIRECT_BINDING_URI + "\""); + } + + @Test + public void createRedirectAuthenticationRequestWhenSHA1SignRequestThenSignatureIsPresent() { + RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationBuilder + .assertingPartyDetails( + (a) -> a.signingAlgorithms((algs) -> algs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1))) + .build(); + Saml2AuthenticationRequestContext context = this.contextBuilder.relayState("Relay State Value") + .relyingPartyRegistration(relyingPartyRegistration).build(); + Saml2RedirectAuthenticationRequest result = this.factory.createRedirectAuthenticationRequest(context); + assertThat(result.getSamlRequest()).isNotEmpty(); + assertThat(result.getRelayState()).isEqualTo("Relay State Value"); + assertThat(result.getSigAlg()).isEqualTo(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); + assertThat(result.getSignature()).isNotNull(); + assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); + } + + private AuthnRequest authnRequest() { + AuthnRequest authnRequest = TestOpenSamlObjects.authnRequest(); + authnRequest.setIssueInstant(Instant.now()); + return authnRequest; + } + + private AuthnRequest getAuthNRequest(Saml2MessageBinding binding) { + AbstractSaml2AuthenticationRequest result = (binding == Saml2MessageBinding.REDIRECT) + ? this.factory.createRedirectAuthenticationRequest(this.context) + : this.factory.createPostAuthenticationRequest(this.context); + String samlRequest = result.getSamlRequest(); + assertThat(samlRequest).isNotEmpty(); + if (result.getBinding() == Saml2MessageBinding.REDIRECT) { + samlRequest = Saml2Utils.samlInflate(Saml2Utils.samlDecode(samlRequest)); + } + else { + samlRequest = new String(Saml2Utils.samlDecode(samlRequest), StandardCharsets.UTF_8); + } + try { + Document document = XMLObjectProviderRegistrySupport.getParserPool() + .parse(new ByteArrayInputStream(samlRequest.getBytes(StandardCharsets.UTF_8))); + Element element = document.getDocumentElement(); + return (AuthnRequest) this.unmarshaller.unmarshall(element); + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + } + +} diff --git a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle index c10aadf7082..ae7fe74bc23 100644 --- a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle +++ b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle @@ -1,14 +1,87 @@ apply plugin: 'io.spring.convention.spring-module' +tasks.forEach({ task -> + if (project(":saml2-service-provider-core").tasks.findByName(task.name)) { + task.dependsOn(project(":saml2-service-provider-core").tasks[task.name]) + } + if (project(":saml2-service-provider-opensaml3").tasks.findByName(task.name)) { + task.dependsOn(project(":saml2-service-provider-opensaml3").tasks[task.name]) + } + if (project(":saml2-service-provider-opensaml4").tasks.findByName(task.name)) { + task.dependsOn(project(":saml2-service-provider-opensaml4").tasks[task.name]) + } +}) + +configurations { + coreSource { + canBeConsumed = false + canBeResolved = true + } + opensaml3Source { + canBeConsumed = false + canBeResolved = true + } + opensaml4Source { + canBeConsumed = false + canBeResolved = true + } + coreClasses { + canBeConsumed = false + canBeResolved = true + } + opensaml3Classes { + canBeConsumed = false + canBeResolved = true + } + opensaml4Classes { + canBeConsumed = false + canBeResolved = true + } + coreJavadoc { + canBeConsumed = false + canBeResolved = true + } + opensaml3Javadoc { + canBeConsumed = false + canBeResolved = true + } + opensaml4Javadoc { + canBeConsumed = false + canBeResolved = true + } +} + dependencies { - compile project(':spring-security-core') - compile project(':spring-security-web') + management platform(project(":spring-security-dependencies")) + api("org.opensaml:opensaml-core") + api("org.opensaml:opensaml-saml-api") + api("org.opensaml:opensaml-saml-impl") + coreSource(project(path: ":saml2-service-provider-core", configuration: 'sourceElements')) + opensaml3Source(project(path: ":saml2-service-provider-opensaml3", configuration: 'sourceElements')) + opensaml4Source(project(path: ":saml2-service-provider-opensaml4", configuration: 'sourceElements')) + coreClasses(project(path: ":saml2-service-provider-core", configuration: 'classesOnlyElements')) + opensaml3Classes(project(path: ":saml2-service-provider-opensaml3", configuration: 'classesOnlyElements')) + opensaml4Classes(project(path: ":saml2-service-provider-opensaml4", configuration: 'classesOnlyElements')) + coreJavadoc(project(path: ":saml2-service-provider-core", configuration: 'javadocElements')) + opensaml3Javadoc(project(path: ":saml2-service-provider-opensaml3", configuration: 'javadocElements')) + opensaml4Javadoc(project(path: ":saml2-service-provider-opensaml4", configuration: 'javadocElements')) +} - compile("org.opensaml:opensaml-core") - compile("org.opensaml:opensaml-saml-api") - compile("org.opensaml:opensaml-saml-impl") +jar { + from configurations.coreClasses + from configurations.opensaml3Classes + from configurations.opensaml4Classes +} - provided 'javax.servlet:javax.servlet-api' +javadocJar { + from configurations.coreJavadoc + from configurations.opensaml3Javadoc + from configurations.opensaml4Javadoc + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} - testCompile 'com.squareup.okhttp3:mockwebserver' +sourcesJar { + from configurations.coreSource + from configurations.opensaml3Source + from configurations.opensaml4Source } diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java deleted file mode 100644 index 26bfd1a8489..00000000000 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.saml2.provider.service.authentication; - -import java.nio.charset.StandardCharsets; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.time.Clock; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; -import org.joda.time.DateTime; -import org.opensaml.core.config.ConfigurationService; -import org.opensaml.core.xml.config.XMLObjectProviderRegistry; -import org.opensaml.core.xml.io.MarshallingException; -import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.opensaml.saml.saml2.core.Issuer; -import org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder; -import org.opensaml.saml.saml2.core.impl.AuthnRequestMarshaller; -import org.opensaml.saml.saml2.core.impl.IssuerBuilder; -import org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver; -import org.opensaml.security.SecurityException; -import org.opensaml.security.credential.BasicCredential; -import org.opensaml.security.credential.Credential; -import org.opensaml.security.credential.CredentialSupport; -import org.opensaml.security.credential.UsageType; -import org.opensaml.xmlsec.SignatureSigningParameters; -import org.opensaml.xmlsec.SignatureSigningParametersResolver; -import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion; -import org.opensaml.xmlsec.crypto.XMLSigningUtil; -import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration; -import org.opensaml.xmlsec.signature.support.SignatureConstants; -import org.opensaml.xmlsec.signature.support.SignatureSupport; -import org.w3c.dom.Element; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.security.saml2.Saml2Exception; -import org.springframework.security.saml2.core.OpenSamlInitializationService; -import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest.Builder; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.web.util.UriUtils; - -/** - * @since 5.2 - */ -public class OpenSamlAuthenticationRequestFactory implements Saml2AuthenticationRequestFactory { - - static { - OpenSamlInitializationService.initialize(); - } - - private Clock clock = Clock.systemUTC(); - - private AuthnRequestMarshaller marshaller; - - private AuthnRequestBuilder authnRequestBuilder; - - private IssuerBuilder issuerBuilder; - - private Converter<Saml2AuthenticationRequestContext, String> protocolBindingResolver = (context) -> { - if (context == null) { - return SAMLConstants.SAML2_POST_BINDING_URI; - } - return context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn(); - }; - - private Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter = this::createAuthnRequest; - - /** - * Creates an {@link OpenSamlAuthenticationRequestFactory} - */ - public OpenSamlAuthenticationRequestFactory() { - XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); - this.marshaller = (AuthnRequestMarshaller) registry.getMarshallerFactory() - .getMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME); - this.authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory() - .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); - this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME); - } - - @Override - @Deprecated - public String createAuthenticationRequest(Saml2AuthenticationRequest request) { - AuthnRequest authnRequest = createAuthnRequest(request.getIssuer(), request.getDestination(), - request.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(null)); - for (org.springframework.security.saml2.credentials.Saml2X509Credential credential : request.getCredentials()) { - if (credential.isSigningCredential()) { - X509Certificate certificate = credential.getCertificate(); - PrivateKey privateKey = credential.getPrivateKey(); - BasicCredential cred = CredentialSupport.getSimpleCredential(certificate, privateKey); - cred.setEntityId(request.getIssuer()); - cred.setUsageType(UsageType.SIGNING); - SignatureSigningParameters parameters = new SignatureSigningParameters(); - parameters.setSigningCredential(cred); - parameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256); - parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256); - parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); - return serialize(sign(authnRequest, parameters)); - } - } - throw new IllegalArgumentException("No signing credential provided"); - } - - @Override - public Saml2PostAuthenticationRequest createPostAuthenticationRequest(Saml2AuthenticationRequestContext context) { - AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); - String xml = context.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned() - ? serialize(sign(authnRequest, context.getRelyingPartyRegistration())) : serialize(authnRequest); - - return Saml2PostAuthenticationRequest.withAuthenticationRequestContext(context) - .samlRequest(Saml2Utils.samlEncode(xml.getBytes(StandardCharsets.UTF_8))).build(); - } - - @Override - public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest( - Saml2AuthenticationRequestContext context) { - AuthnRequest authnRequest = this.authenticationRequestContextConverter.convert(context); - String xml = serialize(authnRequest); - Builder result = Saml2RedirectAuthenticationRequest.withAuthenticationRequestContext(context); - String deflatedAndEncoded = Saml2Utils.samlEncode(Saml2Utils.samlDeflate(xml)); - result.samlRequest(deflatedAndEncoded).relayState(context.getRelayState()); - if (context.getRelyingPartyRegistration().getAssertingPartyDetails().getWantAuthnRequestsSigned()) { - Map<String, String> parameters = new LinkedHashMap<>(); - parameters.put("SAMLRequest", deflatedAndEncoded); - if (StringUtils.hasText(context.getRelayState())) { - parameters.put("RelayState", context.getRelayState()); - } - sign(parameters, context.getRelyingPartyRegistration()); - return result.sigAlg(parameters.get("SigAlg")).signature(parameters.get("Signature")).build(); - } - return result.build(); - } - - private AuthnRequest createAuthnRequest(Saml2AuthenticationRequestContext context) { - return createAuthnRequest(context.getIssuer(), context.getDestination(), - context.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(context)); - } - - private AuthnRequest createAuthnRequest(String issuer, String destination, String assertionConsumerServiceUrl, - String protocolBinding) { - AuthnRequest auth = this.authnRequestBuilder.buildObject(); - auth.setID("ARQ" + UUID.randomUUID().toString().substring(1)); - auth.setIssueInstant(new DateTime(this.clock.millis())); - auth.setForceAuthn(Boolean.FALSE); - auth.setIsPassive(Boolean.FALSE); - auth.setProtocolBinding(protocolBinding); - Issuer iss = this.issuerBuilder.buildObject(); - iss.setValue(issuer); - auth.setIssuer(iss); - auth.setDestination(destination); - auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl); - return auth; - } - - /** - * Set the {@link AuthnRequest} post-processor resolver - * @param authenticationRequestContextConverter - * @since 5.4 - */ - public void setAuthenticationRequestContextConverter( - Converter<Saml2AuthenticationRequestContext, AuthnRequest> authenticationRequestContextConverter) { - Assert.notNull(authenticationRequestContextConverter, "authenticationRequestContextConverter cannot be null"); - this.authenticationRequestContextConverter = authenticationRequestContextConverter; - } - - /** - * ' Use this {@link Clock} with {@link Instant#now()} for generating timestamps - * @param clock - */ - public void setClock(Clock clock) { - Assert.notNull(clock, "clock cannot be null"); - this.clock = clock; - } - - /** - * Sets the {@code protocolBinding} to use when generating authentication requests. - * Acceptable values are {@link SAMLConstants#SAML2_POST_BINDING_URI} and - * {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI} The IDP will be reading this value - * in the {@code AuthNRequest} to determine how to send the Response/Assertion to the - * ACS URL, assertion consumer service URL. - * @param protocolBinding either {@link SAMLConstants#SAML2_POST_BINDING_URI} or - * {@link SAMLConstants#SAML2_REDIRECT_BINDING_URI} - * @throws IllegalArgumentException if the protocolBinding is not valid - * @deprecated Use - * {@link org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.Builder#assertionConsumerServiceBinding(Saml2MessageBinding)} - * instead - */ - @Deprecated - public void setProtocolBinding(String protocolBinding) { - boolean isAllowedBinding = SAMLConstants.SAML2_POST_BINDING_URI.equals(protocolBinding) - || SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(protocolBinding); - if (!isAllowedBinding) { - throw new IllegalArgumentException("Invalid protocol binding: " + protocolBinding); - } - this.protocolBindingResolver = (context) -> protocolBinding; - } - - private AuthnRequest sign(AuthnRequest authnRequest, RelyingPartyRegistration relyingPartyRegistration) { - SignatureSigningParameters parameters = resolveSigningParameters(relyingPartyRegistration); - return sign(authnRequest, parameters); - } - - private AuthnRequest sign(AuthnRequest authnRequest, SignatureSigningParameters parameters) { - try { - SignatureSupport.signObject(authnRequest, parameters); - return authnRequest; - } - catch (Exception ex) { - throw new Saml2Exception(ex); - } - } - - private void sign(Map<String, String> components, RelyingPartyRegistration relyingPartyRegistration) { - SignatureSigningParameters parameters = resolveSigningParameters(relyingPartyRegistration); - sign(components, parameters); - } - - private void sign(Map<String, String> components, SignatureSigningParameters parameters) { - Credential credential = parameters.getSigningCredential(); - String algorithmUri = parameters.getSignatureAlgorithm(); - components.put("SigAlg", algorithmUri); - UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); - for (Map.Entry<String, String> component : components.entrySet()) { - builder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1)); - } - String queryString = builder.build(true).toString().substring(1); - try { - byte[] rawSignature = XMLSigningUtil.signWithURI(credential, algorithmUri, - queryString.getBytes(StandardCharsets.UTF_8)); - String b64Signature = Saml2Utils.samlEncode(rawSignature); - components.put("Signature", b64Signature); - } - catch (SecurityException ex) { - throw new Saml2Exception(ex); - } - } - - private String serialize(AuthnRequest authnRequest) { - try { - Element element = this.marshaller.marshall(authnRequest); - return SerializeSupport.nodeToString(element); - } - catch (MarshallingException ex) { - throw new Saml2Exception(ex); - } - } - - private SignatureSigningParameters resolveSigningParameters(RelyingPartyRegistration relyingPartyRegistration) { - List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration); - List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms(); - List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256); - String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; - SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver(); - CriteriaSet criteria = new CriteriaSet(); - BasicSignatureSigningConfiguration signingConfiguration = new BasicSignatureSigningConfiguration(); - signingConfiguration.setSigningCredentials(credentials); - signingConfiguration.setSignatureAlgorithms(algorithms); - signingConfiguration.setSignatureReferenceDigestMethods(digests); - signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization); - criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration)); - try { - SignatureSigningParameters parameters = resolver.resolveSingle(criteria); - Assert.notNull(parameters, "Failed to resolve any signing credential"); - return parameters; - } - catch (Exception ex) { - throw new Saml2Exception(ex); - } - } - - private List<Credential> resolveSigningCredentials(RelyingPartyRegistration relyingPartyRegistration) { - List<Credential> credentials = new ArrayList<>(); - for (Saml2X509Credential x509Credential : relyingPartyRegistration.getSigningX509Credentials()) { - X509Certificate certificate = x509Credential.getCertificate(); - PrivateKey privateKey = x509Credential.getPrivateKey(); - BasicCredential credential = CredentialSupport.getSimpleCredential(certificate, privateKey); - credential.setEntityId(relyingPartyRegistration.getEntityId()); - credential.setUsageType(UsageType.SIGNING); - credentials.add(credential); - } - return credentials; - } - -} diff --git a/samples/boot/hellorsocket/spring-security-samples-boot-hellorsocket.gradle b/samples/boot/hellorsocket/spring-security-samples-boot-hellorsocket.gradle deleted file mode 100644 index f2b788a0e27..00000000000 --- a/samples/boot/hellorsocket/spring-security-samples-boot-hellorsocket.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-rsocket') - compile 'org.springframework.boot:spring-boot-starter-rsocket' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/hellorsocket/src/integration-test/java/sample/HelloRSocketApplicationITests.java b/samples/boot/hellorsocket/src/integration-test/java/sample/HelloRSocketApplicationITests.java deleted file mode 100644 index 0709dd93bcf..00000000000 --- a/samples/boot/hellorsocket/src/integration-test/java/sample/HelloRSocketApplicationITests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.rsocket.context.LocalRSocketServerPort; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.messaging.rsocket.RSocketRequester; -import org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder; -import org.springframework.security.rsocket.metadata.UsernamePasswordMetadata; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; - -import org.junit.Test; -import org.junit.runner.RunWith; -import reactor.core.publisher.Mono; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.security.rsocket.metadata.UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE; - -/** - * @author Rob Winch - * @author Eddú Meléndez - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@TestPropertySource(properties = "spring.rsocket.server.port=0") -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class HelloRSocketApplicationITests { - - @Autowired - RSocketRequester.Builder requester; - - @LocalRSocketServerPort - int port; - - @Test - public void messageWhenAuthenticatedThenSuccess() { - UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password"); - RSocketRequester requester = this.requester - .rsocketStrategies((builder) -> builder.encoder(new BasicAuthenticationEncoder())) - .setupMetadata(credentials, BASIC_AUTHENTICATION_MIME_TYPE) - .connectTcp("localhost", this.port) - .block(); - - String message = requester.route("message") - .data(Mono.empty()) - .retrieveMono(String.class) - .block(); - - assertThat(message).isEqualTo("Hello"); - } - - @Test - public void messageWhenNotAuthenticatedThenError() { - RSocketRequester requester = this.requester - .connectTcp("localhost", this.port) - .block(); - - assertThatThrownBy(() -> requester.route("message") - .data(Mono.empty()) - .retrieveMono(String.class) - .block()) - .isNotNull(); - } - -} diff --git a/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketApplication.java b/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketApplication.java deleted file mode 100644 index fbbc6df27d7..00000000000 --- a/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - * @since 5.2 - */ -@SpringBootApplication -public class HelloRSocketApplication { - - public static void main(String[] args) { - SpringApplication.run(HelloRSocketApplication.class, args); - } - -} diff --git a/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketSecurityConfig.java b/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketSecurityConfig.java deleted file mode 100644 index 9f469f08e7f..00000000000 --- a/samples/boot/hellorsocket/src/main/java/sample/HelloRSocketSecurityConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.rsocket.EnableRSocketSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * @author Rob Winch - * @since 5.2 - */ -@Configuration -@EnableRSocketSecurity -public class HelloRSocketSecurityConfig { - - @Bean - MapReactiveUserDetailsService userDetailsService() { - UserDetails user = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("SETUP") - .build(); - return new MapReactiveUserDetailsService(user); - } - -} diff --git a/samples/boot/hellorsocket/src/main/java/sample/MessageController.java b/samples/boot/hellorsocket/src/main/java/sample/MessageController.java deleted file mode 100644 index 0150b3f8213..00000000000 --- a/samples/boot/hellorsocket/src/main/java/sample/MessageController.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.stereotype.Controller; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.2 - */ -@Controller -public class MessageController { - - @MessageMapping("message") - public Mono<String> message() { - return Mono.just("Hello"); - } -} diff --git a/samples/boot/hellorsocket/src/main/resources/application.properties b/samples/boot/hellorsocket/src/main/resources/application.properties deleted file mode 100644 index 0d91b628f69..00000000000 --- a/samples/boot/hellorsocket/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.rsocket.server.port=8080 diff --git a/samples/boot/hellowebflux-method/spring-security-samples-boot-hellowebflux-method.gradle b/samples/boot/hellowebflux-method/spring-security-samples-boot-hellowebflux-method.gradle deleted file mode 100644 index c21781806f8..00000000000 --- a/samples/boot/hellowebflux-method/spring-security-samples-boot-hellowebflux-method.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-webflux' - - testCompile project(':spring-security-test') - testCompile 'io.projectreactor:reactor-test' - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/hellowebflux-method/src/integration-test/java/sample/HelloWebfluxMethodApplicationITests.java b/samples/boot/hellowebflux-method/src/integration-test/java/sample/HelloWebfluxMethodApplicationITests.java deleted file mode 100644 index d95956b2d62..00000000000 --- a/samples/boot/hellowebflux-method/src/integration-test/java/sample/HelloWebfluxMethodApplicationITests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class HelloWebfluxMethodApplicationITests { - - @Autowired - WebTestClient rest; - - - @Test - public void messageWhenNotAuthenticated() { - this.rest - .get() - .uri("/message") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void messageWhenUserThenForbidden() { - this.rest - .get() - .uri("/message") - .headers(robsCredentials()) - .exchange() - .expectStatus().isEqualTo(HttpStatus.FORBIDDEN); - } - - @Test - public void messageWhenAdminThenOk() { - this.rest - .get() - .uri("/message") - .headers(adminCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello World!"); - } - - private Consumer<HttpHeaders> robsCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("rob", "rob"); - } - - private Consumer<HttpHeaders> adminCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("admin", "admin"); - } -} - diff --git a/samples/boot/hellowebflux-method/src/main/java/sample/HelloWebfluxMethodApplication.java b/samples/boot/hellowebflux-method/src/main/java/sample/HelloWebfluxMethodApplication.java deleted file mode 100644 index 37aec1d99f4..00000000000 --- a/samples/boot/hellowebflux-method/src/main/java/sample/HelloWebfluxMethodApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - * @since 5.0 - */ -@SpringBootApplication -public class HelloWebfluxMethodApplication { - - public static void main(String[] args) { - SpringApplication.run(HelloWebfluxMethodApplication.class, args); - } -} diff --git a/samples/boot/hellowebflux-method/src/main/java/sample/HelloWorldMessageService.java b/samples/boot/hellowebflux-method/src/main/java/sample/HelloWorldMessageService.java deleted file mode 100644 index c6184c830d4..00000000000 --- a/samples/boot/hellowebflux-method/src/main/java/sample/HelloWorldMessageService.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.stereotype.Component; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Component -public class HelloWorldMessageService { - @PreAuthorize("hasRole('ADMIN')") - public Mono<String> findMessage() { - return Mono.just("Hello World!"); - } -} diff --git a/samples/boot/hellowebflux-method/src/main/java/sample/MessageController.java b/samples/boot/hellowebflux-method/src/main/java/sample/MessageController.java deleted file mode 100644 index b8e96e6a458..00000000000 --- a/samples/boot/hellowebflux-method/src/main/java/sample/MessageController.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RestController -public class MessageController { - private final HelloWorldMessageService messages; - - public MessageController(HelloWorldMessageService messages) { - this.messages = messages; - } - - @GetMapping("/message") - public Mono<String> message() { - return this.messages.findMessage(); - } -} diff --git a/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java b/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java deleted file mode 100644 index fbbcdc835e8..00000000000 --- a/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Rob Winch - * @since 5.0 - */ -@EnableWebFluxSecurity -@EnableReactiveMethodSecurity -public class SecurityConfig { - - @Bean - SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) { - return http - // Demonstrate that method security works - // Best practice to use both for defense in depth - .authorizeExchange((exchanges) -> exchanges - .anyExchange().permitAll() - ) - .httpBasic(withDefaults()) - .build(); - } - - @Bean - public MapReactiveUserDetailsService userDetailsService() { - User.UserBuilder userBuilder = User.withDefaultPasswordEncoder(); - UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build(); - UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build(); - return new MapReactiveUserDetailsService(rob, admin); - } - -} diff --git a/samples/boot/hellowebflux-method/src/test/java/sample/HelloWebfluxMethodApplicationTests.java b/samples/boot/hellowebflux-method/src/test/java/sample/HelloWebfluxMethodApplicationTests.java deleted file mode 100644 index 6a2e2898eda..00000000000 --- a/samples/boot/hellowebflux-method/src/test/java/sample/HelloWebfluxMethodApplicationTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -public class HelloWebfluxMethodApplicationTests { - WebTestClient rest; - - @Autowired - public void setup(ApplicationContext context) { - this.rest = WebTestClient - .bindToApplicationContext(context) - .apply(springSecurity()) - .configureClient() - .build(); - } - - @Test - public void messageWhenNotAuthenticated() { - this.rest - .get() - .uri("/message") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void messageWhenUserThenForbidden() { - this.rest - .get() - .uri("/message") - .headers(robsCredentials()) - .exchange() - .expectStatus().isEqualTo(HttpStatus.FORBIDDEN); - } - - @Test - public void messageWhenAdminThenOk() { - this.rest - .get() - .uri("/message") - .headers(adminCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello World!"); - } - - @Test - @WithMockUser - public void messageWhenWithMockUserThenForbidden() { - this.rest - .get() - .uri("/message") - .exchange() - .expectStatus().isEqualTo(HttpStatus.FORBIDDEN); - } - - @Test - @WithMockUser(roles = "ADMIN") - public void messageWhenWithMockAdminThenOk() { - this.rest - .get() - .uri("/message") - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello World!"); - } - - @Test - public void messageWhenMutateWithMockUserThenForbidden() { - this.rest - .mutateWith(mockUser()) - .get() - .uri("/message") - .exchange() - .expectStatus().isEqualTo(HttpStatus.FORBIDDEN); - } - - @Test - public void messageWhenMutateWithMockAdminThenOk() { - this.rest - .mutateWith(mockUser().roles("ADMIN")) - .get() - .uri("/message") - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello World!"); - } - - private Consumer<HttpHeaders> robsCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("rob", "rob"); - } - - private Consumer<HttpHeaders> adminCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("admin", "admin"); - } -} diff --git a/samples/boot/hellowebflux-method/src/test/java/sample/HelloWorldMessageServiceTests.java b/samples/boot/hellowebflux-method/src/test/java/sample/HelloWorldMessageServiceTests.java deleted file mode 100644 index d8f2aae925b..00000000000 --- a/samples/boot/hellowebflux-method/src/test/java/sample/HelloWorldMessageServiceTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; -import reactor.test.StepVerifier; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -public class HelloWorldMessageServiceTests { - @Autowired - HelloWorldMessageService messages; - - @Test - public void messagesWhenNotAuthenticatedThenDenied() { - StepVerifier.create(this.messages.findMessage()) - .expectError(AccessDeniedException.class) - .verify(); - } - - @Test - @WithMockUser - public void messagesWhenUserThenDenied() { - StepVerifier.create(this.messages.findMessage()) - .expectError(AccessDeniedException.class) - .verify(); - } - - @Test - @WithMockUser(roles = "ADMIN") - public void messagesWhenAdminThenOk() { - StepVerifier.create(this.messages.findMessage()) - .expectNext("Hello World!") - .verifyComplete(); - } -} diff --git a/samples/boot/hellowebflux/spring-security-samples-boot-hellowebflux.gradle b/samples/boot/hellowebflux/spring-security-samples-boot-hellowebflux.gradle deleted file mode 100644 index d65d168ad11..00000000000 --- a/samples/boot/hellowebflux/spring-security-samples-boot-hellowebflux.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-webflux' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java b/samples/boot/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java deleted file mode 100644 index 6d1dda2fac4..00000000000 --- a/samples/boot/hellowebflux/src/integration-test/java/sample/HelloWebfluxApplicationITests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class HelloWebfluxApplicationITests { - - @Autowired - WebTestClient rest; - - @Test - public void basicWhenNoCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void basicWhenValidCredentialsThenOk() { - this.rest - .get() - .uri("/") - .headers(userCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - public void basicWhenInvalidCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .headers(invalidCredentials()) - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); - } - - private Consumer<HttpHeaders> userCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "user"); - } - - private Consumer<HttpHeaders> invalidCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "INVALID"); - } -} diff --git a/samples/boot/hellowebflux/src/main/java/sample/HelloUserController.java b/samples/boot/hellowebflux/src/main/java/sample/HelloUserController.java deleted file mode 100644 index e06932f222d..00000000000 --- a/samples/boot/hellowebflux/src/main/java/sample/HelloUserController.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.security.Principal; -import java.util.Collections; -import java.util.Map; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Mono; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RestController -public class HelloUserController { - - @GetMapping("/") - public Mono<Map<String, String>> hello(Mono<Principal> principal) { - return principal - .map(Principal::getName) - .map(this::helloMessage); - } - - private Map<String, String> helloMessage(String username) { - return Collections.singletonMap("message", "Hello " + username + "!"); - } -} diff --git a/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java b/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java deleted file mode 100644 index b3b27fdf73a..00000000000 --- a/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - * @since 5.0 - */ -@SpringBootApplication -public class HelloWebfluxApplication { - - public static void main(String[] args) { - SpringApplication.run(HelloWebfluxApplication.class, args); - } - -} diff --git a/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxSecurityConfig.java b/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxSecurityConfig.java deleted file mode 100644 index e082133afdb..00000000000 --- a/samples/boot/hellowebflux/src/main/java/sample/HelloWebfluxSecurityConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * @author Rob Winch - * @since 5.0 - */ -@EnableWebFluxSecurity -public class HelloWebfluxSecurityConfig { - - @Bean - public MapReactiveUserDetailsService userDetailsService() { - UserDetails user = User.withDefaultPasswordEncoder() - .username("user") - .password("user") - .roles("USER") - .build(); - return new MapReactiveUserDetailsService(user); - } -} diff --git a/samples/boot/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java b/samples/boot/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java deleted file mode 100644 index cc7892116f8..00000000000 --- a/samples/boot/hellowebflux/src/test/java/sample/HelloWebfluxApplicationTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureWebTestClient -public class HelloWebfluxApplicationTests { - WebTestClient rest; - - @Autowired - public void setup(ApplicationContext context) { - this.rest = WebTestClient - .bindToApplicationContext(context) - .apply(springSecurity()) - .configureClient() - .build(); - } - - @Test - public void basicWhenNoCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void basicWhenValidCredentialsThenOk() { - this.rest - .get() - .uri("/") - .headers(userCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - public void basicWhenInvalidCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .headers(invalidCredentials()) - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); - } - - @Test - public void mockSupportWhenMutateWithMockUserThenOk() { - this.rest - .mutateWith(mockUser()) - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - @WithMockUser - public void mockSupportWhenWithMockUserThenOk() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - private Consumer<HttpHeaders> userCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "user"); - } - - private Consumer<HttpHeaders> invalidCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "INVALID"); - } -} diff --git a/samples/boot/hellowebfluxfn/spring-security-samples-boot-hellowebfluxfn.gradle b/samples/boot/hellowebfluxfn/spring-security-samples-boot-hellowebfluxfn.gradle deleted file mode 100644 index d65d168ad11..00000000000 --- a/samples/boot/hellowebfluxfn/spring-security-samples-boot-hellowebfluxfn.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-webflux' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java b/samples/boot/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java deleted file mode 100644 index de42538a780..00000000000 --- a/samples/boot/hellowebfluxfn/src/integration-test/java/sample/HelloWebfluxFnApplicationITests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class HelloWebfluxFnApplicationITests { - - WebTestClient rest; - - @Autowired - public void setRest(WebTestClient rest) { - this.rest = rest - .mutateWith((b, h, c) -> b.filter(ExchangeFilterFunctions.basicAuthentication())); - } - - @Test - public void basicWhenNoCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void basicWhenValidCredentialsThenOk() { - this.rest - .get() - .uri("/") - .headers(userCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - public void basicWhenInvalidCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .headers(invalidCredentials()) - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); - } - - private Consumer<HttpHeaders> userCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "user"); - } - - private Consumer<HttpHeaders> invalidCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "INVALID"); - } -} diff --git a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloUserController.java b/samples/boot/hellowebfluxfn/src/main/java/sample/HelloUserController.java deleted file mode 100644 index e0c0810cee1..00000000000 --- a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloUserController.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.security.Principal; -import java.util.Collections; - -import reactor.core.publisher.Mono; - -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.server.ServerRequest; -import org.springframework.web.reactive.function.server.ServerResponse; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Component -public class HelloUserController { - - public Mono<ServerResponse> hello(ServerRequest serverRequest) { - return serverRequest.principal() - .map(Principal::getName) - .flatMap((username) -> - ServerResponse.ok() - .contentType(MediaType.APPLICATION_JSON) - .syncBody(Collections.singletonMap("message", "Hello " + username + "!")) - ); - } -} diff --git a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java b/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java deleted file mode 100644 index c508e9f5547..00000000000 --- a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnApplication.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RouterFunctions.route; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.ServerResponse; - -/** - * @author Rob Winch - * @since 5.0 - */ -@SpringBootApplication -public class HelloWebfluxFnApplication { - - public static void main(String[] args) { - SpringApplication.run(HelloWebfluxFnApplication.class, args); - } - - @Bean - public RouterFunction<ServerResponse> routes(HelloUserController userController) { - return route( - GET("/"), userController::hello); - } -} diff --git a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnSecurityConfig.java b/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnSecurityConfig.java deleted file mode 100644 index bd8c83c5532..00000000000 --- a/samples/boot/hellowebfluxfn/src/main/java/sample/HelloWebfluxFnSecurityConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * @author Rob Winch - * @since 5.0 - */ -@EnableWebFluxSecurity -public class HelloWebfluxFnSecurityConfig { - - @Bean - public MapReactiveUserDetailsService userDetailsService() { - UserDetails user = User.withDefaultPasswordEncoder() - .username("user") - .password("user") - .roles("USER") - .build(); - return new MapReactiveUserDetailsService(user); - } -} diff --git a/samples/boot/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java b/samples/boot/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java deleted file mode 100644 index 826fe95cf35..00000000000 --- a/samples/boot/hellowebfluxfn/src/test/java/sample/HelloWebfluxFnApplicationTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; - -import java.util.function.Consumer; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureWebTestClient -public class HelloWebfluxFnApplicationTests { - - WebTestClient rest; - - @Autowired - public void setup(ApplicationContext context) { - this.rest = WebTestClient - .bindToApplicationContext(context) - .apply(springSecurity()) - .configureClient() - .build(); - } - - @Test - public void basicWhenNoCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isUnauthorized(); - } - - @Test - public void basicWhenValidCredentialsThenOk() { - this.rest - .get() - .uri("/") - .headers(userCredentials()) - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - public void basicWhenInvalidCredentialsThenUnauthorized() { - this.rest - .get() - .uri("/") - .headers(invalidCredentials()) - .exchange() - .expectStatus().isUnauthorized() - .expectBody().isEmpty(); - } - - @Test - public void mockSupportWhenMutateWithMockUserThenOk() { - this.rest - .mutateWith(mockUser()) - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - @Test - @WithMockUser - public void mockSupportWhenWithMockUserThenOk() { - this.rest - .get() - .uri("/") - .exchange() - .expectStatus().isOk() - .expectBody().json("{\"message\":\"Hello user!\"}"); - } - - private Consumer<HttpHeaders> userCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "user"); - } - - private Consumer<HttpHeaders> invalidCredentials() { - return (httpHeaders) -> httpHeaders.setBasicAuth("user", "INVALID"); - } -} diff --git a/samples/boot/helloworld/spring-security-samples-boot-helloworld.gradle b/samples/boot/helloworld/spring-security-samples-boot-helloworld.gradle deleted file mode 100644 index a64426286bf..00000000000 --- a/samples/boot/helloworld/spring-security-samples-boot-helloworld.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldApplicationTests.java b/samples/boot/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldApplicationTests.java deleted file mode 100644 index faeeb015a25..00000000000 --- a/samples/boot/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldApplicationTests.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * - * @author Joe Grandja - */ -@RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class HelloWorldApplicationTests { - - @Autowired - private MockMvc mockMvc; - - @Test - public void accessUnprotected() throws Exception { - // @formatter:off - this.mockMvc.perform(get("/index")) - .andExpect(status().isOk()); - // @formatter:on - } - - @Test - public void accessProtectedRedirectsToLogin() throws Exception { - // @formatter:off - MvcResult mvcResult = this.mockMvc.perform(get("/user/index")) - .andExpect(status().is3xxRedirection()) - .andReturn(); - // @formatter:on - - assertThat(mvcResult.getResponse().getRedirectedUrl()).endsWith("/login"); - } - - @Test - public void loginUser() throws Exception { - // @formatter:off - this.mockMvc.perform(formLogin().user("user").password("password")) - .andExpect(authenticated()); - // @formatter:on - } - - @Test - public void loginInvalidUser() throws Exception { - // @formatter:off - this.mockMvc.perform(formLogin().user("invalid").password("invalid")) - .andExpect(unauthenticated()) - .andExpect(status().is3xxRedirection()); - // @formatter:on - } - - @Test - public void loginUserAccessProtected() throws Exception { - // @formatter:off - MvcResult mvcResult = this.mockMvc.perform(formLogin().user("user").password("password")) - .andExpect(authenticated()).andReturn(); - // @formatter:on - - MockHttpSession httpSession = (MockHttpSession) mvcResult.getRequest().getSession(false); - - // @formatter:off - this.mockMvc.perform(get("/user/index").session(httpSession)) - .andExpect(status().isOk()); - // @formatter:on - } - - @Test - public void loginUserValidateLogout() throws Exception { - // @formatter:off - MvcResult mvcResult = this.mockMvc.perform(formLogin().user("user").password("password")) - .andExpect(authenticated()).andReturn(); - // @formatter:on - - MockHttpSession httpSession = (MockHttpSession) mvcResult.getRequest().getSession(false); - - // @formatter:off - this.mockMvc.perform(post("/logout").with(csrf()).session(httpSession)) - .andExpect(unauthenticated()); - this.mockMvc.perform(get("/user/index").session(httpSession)) - .andExpect(unauthenticated()) - .andExpect(status().is3xxRedirection()); - // @formatter:on - } -} diff --git a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/HelloWorldApplication.java b/samples/boot/helloworld/src/main/java/org/springframework/security/samples/HelloWorldApplication.java deleted file mode 100644 index 170bcadef31..00000000000 --- a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/HelloWorldApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Joe Grandja - */ -@SpringBootApplication -public class HelloWorldApplication { - - public static void main(String[] args) { - SpringApplication.run(HelloWorldApplication.class, args); - } - - -} \ No newline at end of file diff --git a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/boot/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index c3c71cf2fe5..00000000000 --- a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; - -/** - * @author Joe Grandja - */ -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorize) -> authorize - .antMatchers("/css/**", "/index").permitAll() - .antMatchers("/user/**").hasRole("USER") - ) - .formLogin((formLogin) -> formLogin - .loginPage("/login") - .failureUrl("/login-error") - ); - } - // @formatter:on - - @Bean - public UserDetailsService userDetailsService() { - UserDetails userDetails = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build(); - return new InMemoryUserDetailsManager(userDetails); - } -} diff --git a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/web/MainController.java b/samples/boot/helloworld/src/main/java/org/springframework/security/samples/web/MainController.java deleted file mode 100644 index 793a8e97ebf..00000000000 --- a/samples/boot/helloworld/src/main/java/org/springframework/security/samples/web/MainController.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.web; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.RequestMapping; - -/** - * @author Joe Grandja - */ -@Controller -public class MainController { - - @RequestMapping("/") - public String root() { - return "redirect:/index"; - } - - @RequestMapping("/index") - public String index() { - return "index"; - } - - @RequestMapping("/user/index") - public String userIndex() { - return "user/index"; - } - - @RequestMapping("/login") - public String login() { - return "login"; - } - - @RequestMapping("/login-error") - public String loginError(Model model) { - model.addAttribute("loginError", true); - return "login"; - } - -} diff --git a/samples/boot/helloworld/src/main/resources/application.yml b/samples/boot/helloworld/src/main/resources/application.yml deleted file mode 100644 index b59d86df49c..00000000000 --- a/samples/boot/helloworld/src/main/resources/application.yml +++ /dev/null @@ -1,12 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: WARN - org.springframework.web: INFO - org.springframework.security: INFO - -spring: - thymeleaf: - cache: false diff --git a/samples/boot/helloworld/src/main/resources/static/css/main.css b/samples/boot/helloworld/src/main/resources/static/css/main.css deleted file mode 100644 index 5e6687a387c..00000000000 --- a/samples/boot/helloworld/src/main/resources/static/css/main.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - font-family: sans; - font-size: 1em; -} - -p.error { - font-weight: bold; - color: red; -} - -div.logout { - float: right; -} \ No newline at end of file diff --git a/samples/boot/helloworld/src/main/resources/templates/index.html b/samples/boot/helloworld/src/main/resources/templates/index.html deleted file mode 100644 index 05fad120332..00000000000 --- a/samples/boot/helloworld/src/main/resources/templates/index.html +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()"> - Logged in user: <span sec:authentication="name"></span> | - Roles: <span sec:authentication="principal.authorities"></span> - <div> - <form action="#" th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - </div> - </div> - <h1>Hello Spring Security</h1> - <p>This is an unsecured page, but you can access the secured pages after authenticating.</p> - <ul> - <li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li> - </ul> - </body> -</html> diff --git a/samples/boot/helloworld/src/main/resources/templates/login.html b/samples/boot/helloworld/src/main/resources/templates/login.html deleted file mode 100644 index cec2b5b0daf..00000000000 --- a/samples/boot/helloworld/src/main/resources/templates/login.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Login page</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>Login page</h1> - <p>Example user: user / password</p> - <p th:if="${loginError}" class="error">Wrong user or password</p> - <form th:action="@{/login}" method="post"> - <label for="username">Username</label>: - <input type="text" id="username" name="username" autofocus="autofocus" /> <br /> - <label for="password">Password</label>: - <input type="password" id="password" name="password" /> <br /> - <input type="submit" value="Log in" /> - </form> - <p><a href="/index" th:href="@{/index}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/helloworld/src/main/resources/templates/user/index.html b/samples/boot/helloworld/src/main/resources/templates/user/index.html deleted file mode 100644 index 53dd9319a52..00000000000 --- a/samples/boot/helloworld/src/main/resources/templates/user/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:substituteby="index::logout"></div> - <h1>This is a secured page!</h1> - <p><a href="/index" th:href="@{/index}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/insecure/spring-security-samples-boot-insecure.gradle b/samples/boot/insecure/spring-security-samples-boot-insecure.gradle deleted file mode 100644 index 556ebbb0536..00000000000 --- a/samples/boot/insecure/spring-security-samples-boot-insecure.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-web' - - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/insecure/src/integration-test/java/org/springframework/security/samples/InsecureApplicationTests.java b/samples/boot/insecure/src/integration-test/java/org/springframework/security/samples/InsecureApplicationTests.java deleted file mode 100644 index f2c7542e5b0..00000000000 --- a/samples/boot/insecure/src/integration-test/java/org/springframework/security/samples/InsecureApplicationTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * - * @author Joe Grandja - */ -@RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class InsecureApplicationTests { - - @Autowired - private MockMvc mockMvc; - - @Test - public void accessUnprotected() throws Exception { - this.mockMvc.perform(get("/index")).andExpect(status().isOk()); - } - -} diff --git a/samples/boot/insecure/src/main/java/org/springframework/security/samples/InsecureApplication.java b/samples/boot/insecure/src/main/java/org/springframework/security/samples/InsecureApplication.java deleted file mode 100644 index aab24fa9d36..00000000000 --- a/samples/boot/insecure/src/main/java/org/springframework/security/samples/InsecureApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Joe Grandja - */ -@SpringBootApplication -public class InsecureApplication { - - public static void main(String[] args) { - SpringApplication.run(InsecureApplication.class, args); - } - - -} diff --git a/samples/boot/insecure/src/main/java/org/springframework/security/samples/web/MainController.java b/samples/boot/insecure/src/main/java/org/springframework/security/samples/web/MainController.java deleted file mode 100644 index f7de44adcc9..00000000000 --- a/samples/boot/insecure/src/main/java/org/springframework/security/samples/web/MainController.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.web; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * @author Joe Grandja - */ -@Controller -public class MainController { - - @RequestMapping("/") - public String root() { - return "redirect:/index"; - } - - @RequestMapping("/index") - public String index() { - return "index"; - } - - @RequestMapping("/user/index") - public String userIndex() { - return "user/index"; - } - - @RequestMapping(value = "/login") - public String login() { - return "login"; - } - - @RequestMapping(value = "/login", method = RequestMethod.POST) - public String postLogin() { - // TODO Enable form login with Spring Security (trigger error for now) - return "redirect:/login-error"; - } - - @RequestMapping("/login-error") - public String loginError(Model model) { - model.addAttribute("loginError", true); - return "login"; - } - -} diff --git a/samples/boot/insecure/src/main/resources/application.yml b/samples/boot/insecure/src/main/resources/application.yml deleted file mode 100644 index 02540888032..00000000000 --- a/samples/boot/insecure/src/main/resources/application.yml +++ /dev/null @@ -1,11 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: WARN - org.springframework.web: INFO - -spring: - thymeleaf: - cache: false diff --git a/samples/boot/insecure/src/main/resources/static/css/main.css b/samples/boot/insecure/src/main/resources/static/css/main.css deleted file mode 100644 index 5e6687a387c..00000000000 --- a/samples/boot/insecure/src/main/resources/static/css/main.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - font-family: sans; - font-size: 1em; -} - -p.error { - font-weight: bold; - color: red; -} - -div.logout { - float: right; -} \ No newline at end of file diff --git a/samples/boot/insecure/src/main/resources/templates/index.html b/samples/boot/insecure/src/main/resources/templates/index.html deleted file mode 100644 index ee9ccec6183..00000000000 --- a/samples/boot/insecure/src/main/resources/templates/index.html +++ /dev/null @@ -1,15 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>Hello Spring Security</h1> - <p>This is an unsecured page, but you can access the secured pages after authenticating.</p> - <ul> - <li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li> - </ul> - </body> -</html> diff --git a/samples/boot/insecure/src/main/resources/templates/login.html b/samples/boot/insecure/src/main/resources/templates/login.html deleted file mode 100644 index cec2b5b0daf..00000000000 --- a/samples/boot/insecure/src/main/resources/templates/login.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Login page</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>Login page</h1> - <p>Example user: user / password</p> - <p th:if="${loginError}" class="error">Wrong user or password</p> - <form th:action="@{/login}" method="post"> - <label for="username">Username</label>: - <input type="text" id="username" name="username" autofocus="autofocus" /> <br /> - <label for="password">Password</label>: - <input type="password" id="password" name="password" /> <br /> - <input type="submit" value="Log in" /> - </form> - <p><a href="/index" th:href="@{/index}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/insecure/src/main/resources/templates/user/index.html b/samples/boot/insecure/src/main/resources/templates/user/index.html deleted file mode 100644 index 3fd4ccc1a35..00000000000 --- a/samples/boot/insecure/src/main/resources/templates/user/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>TODO Secure this</h1> - <p>We would like to secure this page</p> - <p><a href="/index" th:href="@{/index}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/kotlin-webflux/spring-security-samples-boot-kotlin-webflux.gradle.kts b/samples/boot/kotlin-webflux/spring-security-samples-boot-kotlin-webflux.gradle.kts deleted file mode 100644 index 20eb7b4060a..00000000000 --- a/samples/boot/kotlin-webflux/spring-security-samples-boot-kotlin-webflux.gradle.kts +++ /dev/null @@ -1,41 +0,0 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - id("io.spring.convention.spring-sample-boot") - kotlin("jvm") - kotlin("plugin.spring") version "1.3.71" -} - -repositories { - mavenCentral() -} - -dependencies { - implementation(project(":spring-security-core")) - implementation(project(":spring-security-config")) - implementation(project(":spring-security-web")) - implementation("org.springframework.boot:spring-boot-starter-webflux") - implementation("org.springframework.boot:spring-boot-starter-thymeleaf") - implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5") - implementation("io.projectreactor.kotlin:reactor-kotlin-extensions") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") - - testImplementation(project(":spring-security-test")) - testImplementation("org.springframework.boot:spring-boot-starter-test") { - exclude(group = "org.junit.vintage", module = "junit-vintage-engine") - } - testImplementation("io.projectreactor:reactor-test") -} - -tasks.withType<Test> { - useJUnitPlatform() -} - -tasks.withType<KotlinCompile> { - kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "1.8" - } -} diff --git a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/KotlinWebfluxApplication.kt b/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/KotlinWebfluxApplication.kt deleted file mode 100644 index 572be2a6e3c..00000000000 --- a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/KotlinWebfluxApplication.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -@SpringBootApplication -class KotlinWebfluxApplication - -fun main(args: Array<String>) { - runApplication<KotlinWebfluxApplication>(*args) -} diff --git a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt b/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt deleted file mode 100644 index fcdb5fdcdee..00000000000 --- a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config - -import org.springframework.context.annotation.Bean -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity -import org.springframework.security.config.web.server.ServerHttpSecurity -import org.springframework.security.config.web.server.invoke -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService -import org.springframework.security.core.userdetails.ReactiveUserDetailsService -import org.springframework.security.core.userdetails.User -import org.springframework.security.web.server.SecurityWebFilterChain - -@EnableWebFluxSecurity -class SecurityConfig { - - @Bean - fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - return http { - authorizeExchange { - authorize("/log-in", permitAll) - authorize("/", permitAll) - authorize("/css/**", permitAll) - authorize("/user/**", hasAuthority("ROLE_USER")) - } - formLogin { - loginPage = "/log-in" - } - } - } - - @Bean - fun userDetailsService(): ReactiveUserDetailsService { - val userDetails = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build() - return MapReactiveUserDetailsService(userDetails) - } -} diff --git a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/web/MainController.kt b/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/web/MainController.kt deleted file mode 100644 index 991c0195c06..00000000000 --- a/samples/boot/kotlin-webflux/src/main/kotlin/org/springframework/security/samples/web/MainController.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.web - -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.GetMapping - -@Controller -class MainController { - - @GetMapping("/") - fun index(): String { - return "index" - } - - @GetMapping("/user/index") - fun userIndex(): String { - return "user/index" - } - - @GetMapping("/log-in") - fun login(): String { - return "login" - } -} diff --git a/samples/boot/kotlin-webflux/src/main/resources/application.yml b/samples/boot/kotlin-webflux/src/main/resources/application.yml deleted file mode 100644 index 8c01e005bcd..00000000000 --- a/samples/boot/kotlin-webflux/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -server: - port: 8080 - -spring: - thymeleaf: - cache: false diff --git a/samples/boot/kotlin-webflux/src/main/resources/css/main.css b/samples/boot/kotlin-webflux/src/main/resources/css/main.css deleted file mode 100644 index de0941ecd58..00000000000 --- a/samples/boot/kotlin-webflux/src/main/resources/css/main.css +++ /dev/null @@ -1,8 +0,0 @@ -body { - font-family: sans; - font-size: 1em; -} - -div.logout { - float: right; -} diff --git a/samples/boot/kotlin-webflux/src/main/resources/templates/index.html b/samples/boot/kotlin-webflux/src/main/resources/templates/index.html deleted file mode 100644 index f637854f047..00000000000 --- a/samples/boot/kotlin-webflux/src/main/resources/templates/index.html +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()"> - Logged in user: <span sec:authentication="name"></span> | - Roles: <span sec:authentication="principal.authorities"></span> - <div> - <form action="#" th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - </div> - </div> - <h1>Hello Spring Security</h1> - <p>This is an unsecured page, but you can access the secured pages after authenticating.</p> - <ul> - <li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li> - </ul> - </body> -</html> diff --git a/samples/boot/kotlin-webflux/src/main/resources/templates/login.html b/samples/boot/kotlin-webflux/src/main/resources/templates/login.html deleted file mode 100644 index 2ee92169376..00000000000 --- a/samples/boot/kotlin-webflux/src/main/resources/templates/login.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Login page</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>Login page</h1> - <p>Example user: user / password</p> - <form th:action="@{/log-in}" method="post"> - <label for="username">Username</label>: - <input type="text" id="username" name="username" autofocus="autofocus" /> <br /> - <label for="password">Password</label>: - <input type="password" id="password" name="password" /> <br /> - <input type="submit" value="Log in" /> - </form> - <p><a href="/" th:href="@{/}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/kotlin-webflux/src/main/resources/templates/user/index.html b/samples/boot/kotlin-webflux/src/main/resources/templates/user/index.html deleted file mode 100644 index 393f6d37051..00000000000 --- a/samples/boot/kotlin-webflux/src/main/resources/templates/user/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:substituteby="index::logout"></div> - <h1>This is a secured page!</h1> - <p><a href="/" th:href="@{/}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/kotlin-webflux/src/test/kotlin/org/springframework/security/samples/KotlinWebfluxApplicationTests.kt b/samples/boot/kotlin-webflux/src/test/kotlin/org/springframework/security/samples/KotlinWebfluxApplicationTests.kt deleted file mode 100644 index fc126e58b7f..00000000000 --- a/samples/boot/kotlin-webflux/src/test/kotlin/org/springframework/security/samples/KotlinWebfluxApplicationTests.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples - -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.context.ApplicationContext -import org.springframework.test.web.reactive.server.WebTestClient -import org.springframework.security.test.context.support.WithMockUser - -@SpringBootTest -class KotlinWebfluxApplicationTests { - - lateinit var rest: WebTestClient - - @Autowired - fun setup(context: ApplicationContext) { - rest = WebTestClient - .bindToApplicationContext(context) - .configureClient() - .build() - } - - @Test - fun `index page is not protected`() { - rest - .get() - .uri("/") - .exchange() - .expectStatus().isOk - } - - @Test - fun `protected page when unauthenticated then redirects to login `() { - rest - .get() - .uri("/user/index") - .exchange() - .expectStatus().is3xxRedirection - .expectHeader().valueEquals("Location", "/log-in") - } - - @Test - @WithMockUser - fun `protected page can be accessed when authenticated`() { - rest - .get() - .uri("/user/index") - .exchange() - .expectStatus().isOk - } -} diff --git a/samples/boot/kotlin/spring-security-samples-boot-kotlin.gradle.kts b/samples/boot/kotlin/spring-security-samples-boot-kotlin.gradle.kts deleted file mode 100644 index bd841b8b710..00000000000 --- a/samples/boot/kotlin/spring-security-samples-boot-kotlin.gradle.kts +++ /dev/null @@ -1,31 +0,0 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - id("io.spring.convention.spring-sample-boot") - kotlin("jvm") - kotlin("plugin.spring") version "1.3.71" -} - -repositories { - mavenCentral() -} - -dependencies { - implementation(project(":spring-security-core")) - implementation(project(":spring-security-config")) - implementation(project(":spring-security-web")) - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-thymeleaf") - implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - testImplementation(project(":spring-security-test")) - testImplementation("org.springframework.boot:spring-boot-starter-test") -} - -tasks.withType<KotlinCompile> { - kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "1.8" - } -} diff --git a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/KotlinApplication.kt b/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/KotlinApplication.kt deleted file mode 100644 index 1d7c7b28f2d..00000000000 --- a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/KotlinApplication.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -/** - * @author Eleftheria Stein - */ -@SpringBootApplication -class KotlinApplication - -fun main(args: Array<String>) { - runApplication<KotlinApplication>(*args) -} diff --git a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt b/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt deleted file mode 100644 index 043f29ecae0..00000000000 --- a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/config/SecurityConfig.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config - -import org.springframework.context.annotation.Bean -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter -import org.springframework.security.config.web.servlet.invoke -import org.springframework.security.core.userdetails.User -import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.provisioning.InMemoryUserDetailsManager - -/** - * @author Eleftheria Stein - */ -@EnableWebSecurity -class SecurityConfig : WebSecurityConfigurerAdapter() { - - override fun configure(http: HttpSecurity) { - http { - authorizeRequests { - authorize("/css/**", permitAll) - authorize("/user/**", hasAuthority("ROLE_USER")) - } - formLogin { - loginPage = "/log-in" - } - } - } - - @Bean - public override fun userDetailsService(): UserDetailsService { - val userDetails = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build() - return InMemoryUserDetailsManager(userDetails) - } -} diff --git a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/web/MainController.kt b/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/web/MainController.kt deleted file mode 100644 index e8c8128ad8a..00000000000 --- a/samples/boot/kotlin/src/main/kotlin/org/springframework/security/samples/web/MainController.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.web - -import org.springframework.stereotype.Controller -import org.springframework.web.bind.annotation.GetMapping - -/** - * @author Eleftheria Stein - */ -@Controller -class MainController { - - @GetMapping("/") - fun index(): String { - return "index" - } - - @GetMapping("/user/index") - fun userIndex(): String { - return "user/index" - } - - @GetMapping("/log-in") - fun login(): String { - return "login" - } -} diff --git a/samples/boot/kotlin/src/main/resources/application.yml b/samples/boot/kotlin/src/main/resources/application.yml deleted file mode 100644 index 8c01e005bcd..00000000000 --- a/samples/boot/kotlin/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -server: - port: 8080 - -spring: - thymeleaf: - cache: false diff --git a/samples/boot/kotlin/src/main/resources/static/css/main.css b/samples/boot/kotlin/src/main/resources/static/css/main.css deleted file mode 100644 index de0941ecd58..00000000000 --- a/samples/boot/kotlin/src/main/resources/static/css/main.css +++ /dev/null @@ -1,8 +0,0 @@ -body { - font-family: sans; - font-size: 1em; -} - -div.logout { - float: right; -} diff --git a/samples/boot/kotlin/src/main/resources/templates/index.html b/samples/boot/kotlin/src/main/resources/templates/index.html deleted file mode 100644 index c30f4a8292c..00000000000 --- a/samples/boot/kotlin/src/main/resources/templates/index.html +++ /dev/null @@ -1,40 +0,0 @@ -<!-- - ~ Copyright 2002-2019 the original author or authors. - ~ - ~ 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 - ~ - ~ https://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. - --> - -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()"> - Logged in user: <span sec:authentication="name"></span> | - Roles: <span sec:authentication="principal.authorities"></span> - <div> - <form action="#" th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - </div> - </div> - <h1>Hello Spring Security</h1> - <p>This is an unsecured page, but you can access the secured pages after authenticating.</p> - <ul> - <li>Go to the <a href="/user/index" th:href="@{/user/index}">secured pages</a></li> - </ul> - </body> -</html> diff --git a/samples/boot/kotlin/src/main/resources/templates/login.html b/samples/boot/kotlin/src/main/resources/templates/login.html deleted file mode 100644 index 2ee92169376..00000000000 --- a/samples/boot/kotlin/src/main/resources/templates/login.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Login page</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <h1>Login page</h1> - <p>Example user: user / password</p> - <form th:action="@{/log-in}" method="post"> - <label for="username">Username</label>: - <input type="text" id="username" name="username" autofocus="autofocus" /> <br /> - <label for="password">Password</label>: - <input type="password" id="password" name="password" /> <br /> - <input type="submit" value="Log in" /> - </form> - <p><a href="/" th:href="@{/}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/kotlin/src/main/resources/templates/user/index.html b/samples/boot/kotlin/src/main/resources/templates/user/index.html deleted file mode 100644 index b36caed0272..00000000000 --- a/samples/boot/kotlin/src/main/resources/templates/user/index.html +++ /dev/null @@ -1,29 +0,0 @@ -<!-- - ~ Copyright 2002-2019 the original author or authors. - ~ - ~ 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 - ~ - ~ https://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. - --> - -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> - <head> - <title>Hello Spring Security</title> - <meta charset="utf-8" /> - <link rel="stylesheet" href="/static/css/main.css" th:href="@{/css/main.css}" /> - </head> - <body> - <div th:substituteby="index::logout"></div> - <h1>This is a secured page!</h1> - <p><a href="/" th:href="@{/}">Back to home page</a></p> - </body> -</html> diff --git a/samples/boot/kotlin/src/test/kotlin/org/springframework/security/samples/KotlinApplicationTests.kt b/samples/boot/kotlin/src/test/kotlin/org/springframework/security/samples/KotlinApplicationTests.kt deleted file mode 100644 index 393bb0f2e20..00000000000 --- a/samples/boot/kotlin/src/test/kotlin/org/springframework/security/samples/KotlinApplicationTests.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples - -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.mock.web.MockHttpSession -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin -import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated -import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated -import org.springframework.test.context.junit4.SpringRunner -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.get -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status - -@RunWith(SpringRunner::class) -@SpringBootTest -@AutoConfigureMockMvc -class KotlinApplicationTests { - - @Autowired - private lateinit var mockMvc: MockMvc - - @Test - fun `index page is not protected`() { - this.mockMvc.get("/") - .andExpect { - status { isOk() } - } - } - - @Test - fun `protected page redirects to login`() { - val mvcResult = this.mockMvc.get("/user/index") - .andExpect { status { is3xxRedirection() } } - .andReturn() - - assertThat(mvcResult.response.redirectedUrl).endsWith("/log-in") - } - - @Test - fun `valid user permitted to log in`() { - this.mockMvc.perform(formLogin("/log-in").user("user").password("password")) - .andExpect(authenticated()) - } - - @Test - fun `invalid user not permitted to log in`() { - this.mockMvc.perform(formLogin("/log-in").user("invalid").password("invalid")) - .andExpect(unauthenticated()) - .andExpect(status().is3xxRedirection) - } - - @Test - fun `logged in user can access protected page`() { - val mvcResult = this.mockMvc.perform(formLogin("/log-in").user("user").password("password")) - .andExpect(authenticated()).andReturn() - - val httpSession = mvcResult.request.getSession(false) as MockHttpSession - - this.mockMvc.get("/user/index") { - session = httpSession - }.andExpect { - status { isOk() } - } - } -} diff --git a/samples/boot/oauth2authorizationserver/README.adoc b/samples/boot/oauth2authorizationserver/README.adoc deleted file mode 100644 index e056a21b175..00000000000 --- a/samples/boot/oauth2authorizationserver/README.adoc +++ /dev/null @@ -1,39 +0,0 @@ -= OAuth 2.0 Authorization Server Sample - -This sample demonstrates an Authorization Server that supports a simple, static JWK Set. - -It's useful for working with the other samples in the library that want to point to an Authorization Server. - -== 1. Running the server - -To run the server, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2AuthorizationServerApplication` from there. - -Once it is up, this request asks for a token with the "message:read" scope: - -```bash -curl reader:secret@localhost:8081/oauth/token -d grant_type=password -d username=subject -d password=password -``` - -Which will respond with something like: - -```json -{ - "access_token":"eyJhbGciOiJSUzI1NiIsI...Fhq4RIVyA4ZAkC7T1aZbKAQ", - "token_type":"bearer", - "expires_in":599999999, - "scope":"message:read", - "jti":"8a425df7-f4c9-4ca4-be12-0136c3015da0" -} -``` - -You can also do the same with the `writer` client: - -```bash -curl writer:secret@localhost:8081/oauth/token -d grant_type=password -d username=subject -d password=password -``` diff --git a/samples/boot/oauth2authorizationserver/spring-security-samples-boot-oauth2authorizationserver.gradle b/samples/boot/oauth2authorizationserver/spring-security-samples-boot-oauth2authorizationserver.gradle deleted file mode 100644 index 3b2df4e605f..00000000000 --- a/samples/boot/oauth2authorizationserver/spring-security-samples-boot-oauth2authorizationserver.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.springframework.boot:spring-boot-starter-security' - compile "org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:${springBootVersion}" - - compile 'javax.xml.bind:jaxb-api' - compile 'com.sun.xml.bind:jaxb-core' - compile 'com.sun.xml.bind:jaxb-impl' - compile 'com.nimbusds:nimbus-jose-jwt' - - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2authorizationserver/src/main/java/sample/AuthorizationServerConfiguration.java b/samples/boot/oauth2authorizationserver/src/main/java/sample/AuthorizationServerConfiguration.java deleted file mode 100644 index 4b20b0c88c5..00000000000 --- a/samples/boot/oauth2authorizationserver/src/main/java/sample/AuthorizationServerConfiguration.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.math.BigInteger; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.RSAPrivateKeySpec; -import java.security.spec.RSAPublicKeySpec; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.RSAKey; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint; -import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; -import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -/** - * An instance of Legacy Authorization Server (spring-security-oauth2) that uses a single, - * not-rotating key and exposes a JWK endpoint. - * - * See - * <a - * target="_blank" - * href="https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/reference/htmlsingle/"> - * Spring Security OAuth Autoconfig's documentation</a> for additional detail - * - * @author Josh Cummings - * @since 5.1 - */ -@EnableAuthorizationServer -@Configuration -public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { - - AuthenticationManager authenticationManager; - KeyPair keyPair; - boolean jwtEnabled; - - public AuthorizationServerConfiguration( - AuthenticationConfiguration authenticationConfiguration, - KeyPair keyPair, - @Value("${security.oauth2.authorizationserver.jwt.enabled:true}") boolean jwtEnabled) throws Exception { - - this.authenticationManager = authenticationConfiguration.getAuthenticationManager(); - this.keyPair = keyPair; - this.jwtEnabled = jwtEnabled; - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) - throws Exception { - // @formatter:off - clients.inMemory() - .withClient("reader") - .authorizedGrantTypes("password") - .secret("{noop}secret") - .scopes("message:read") - .accessTokenValiditySeconds(600_000_000) - .and() - .withClient("writer") - .authorizedGrantTypes("password") - .secret("{noop}secret") - .scopes("message:write") - .accessTokenValiditySeconds(600_000_000) - .and() - .withClient("noscopes") - .authorizedGrantTypes("password") - .secret("{noop}secret") - .scopes("none") - .accessTokenValiditySeconds(600_000_000); - // @formatter:on - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - // @formatter:off - endpoints - .authenticationManager(this.authenticationManager) - .tokenStore(tokenStore()); - - if (this.jwtEnabled) { - endpoints - .accessTokenConverter(accessTokenConverter()); - } - // @formatter:on - } - - @Bean - public TokenStore tokenStore() { - if (this.jwtEnabled) { - return new JwtTokenStore(accessTokenConverter()); - } else { - return new InMemoryTokenStore(); - } - } - - @Bean - public JwtAccessTokenConverter accessTokenConverter() { - JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); - converter.setKeyPair(this.keyPair); - - DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); - accessTokenConverter.setUserTokenConverter(new SubjectAttributeUserTokenConverter()); - converter.setAccessTokenConverter(accessTokenConverter); - - return converter; - } -} - -/** - * For configuring the end users recognized by this Authorization Server - */ -@Configuration -class UserConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .mvcMatchers("/.well-known/jwks.json").permitAll() - .anyRequest().authenticated() - .and() - .httpBasic() - .and() - .csrf().ignoringRequestMatchers((request) -> "/introspect".equals(request.getRequestURI())); - } - - @Bean - @Override - public UserDetailsService userDetailsService() { - return new InMemoryUserDetailsManager( - User.withDefaultPasswordEncoder() - .username("subject") - .password("password") - .roles("USER") - .build()); - } -} - -/** - * Legacy Authorization Server (spring-security-oauth2) does not support any - * Token Introspection endpoint. - * - * This class adds ad-hoc support in order to better support the other samples in the repo. - */ -@FrameworkEndpoint -class IntrospectEndpoint { - TokenStore tokenStore; - - IntrospectEndpoint(TokenStore tokenStore) { - this.tokenStore = tokenStore; - } - - @PostMapping("/introspect") - @ResponseBody - public Map<String, Object> introspect(@RequestParam("token") String token) { - OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(token); - Map<String, Object> attributes = new HashMap<>(); - if (accessToken == null || accessToken.isExpired()) { - attributes.put("active", false); - return attributes; - } - - OAuth2Authentication authentication = this.tokenStore.readAuthentication(token); - - attributes.put("active", true); - attributes.put("exp", accessToken.getExpiration().getTime()); - attributes.put("scope", accessToken.getScope().stream().collect(Collectors.joining(" "))); - attributes.put("sub", authentication.getName()); - - return attributes; - } -} - -/** - * Legacy Authorization Server (spring-security-oauth2) does not support any - * <a href target="_blank" href="https://tools.ietf.org/html/rfc7517#section-5">JWK Set</a> endpoint. - * - * This class adds ad-hoc support in order to better support the other samples in the repo. - */ -@FrameworkEndpoint -class JwkSetEndpoint { - KeyPair keyPair; - - JwkSetEndpoint(KeyPair keyPair) { - this.keyPair = keyPair; - } - - @GetMapping("/.well-known/jwks.json") - @ResponseBody - public Map<String, Object> getKey() { - RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic(); - RSAKey key = new RSAKey.Builder(publicKey).build(); - return new JWKSet(key).toJSONObject(); - } -} - -/** - * An Authorization Server will more typically have a key rotation strategy, and the keys will not - * be hard-coded into the application code. - * - * For simplicity, though, this sample doesn't demonstrate key rotation. - */ -@Configuration -class KeyConfig { - @Bean - KeyPair keyPair() { - try { - String privateExponent = "3851612021791312596791631935569878540203393691253311342052463788814433805390794604753109719790052408607029530149004451377846406736413270923596916756321977922303381344613407820854322190592787335193581632323728135479679928871596911841005827348430783250026013354350760878678723915119966019947072651782000702927096735228356171563532131162414366310012554312756036441054404004920678199077822575051043273088621405687950081861819700809912238863867947415641838115425624808671834312114785499017269379478439158796130804789241476050832773822038351367878951389438751088021113551495469440016698505614123035099067172660197922333993"; - String modulus = "18044398961479537755088511127417480155072543594514852056908450877656126120801808993616738273349107491806340290040410660515399239279742407357192875363433659810851147557504389760192273458065587503508596714389889971758652047927503525007076910925306186421971180013159326306810174367375596043267660331677530921991343349336096643043840224352451615452251387611820750171352353189973315443889352557807329336576421211370350554195530374360110583327093711721857129170040527236951522127488980970085401773781530555922385755722534685479501240842392531455355164896023070459024737908929308707435474197069199421373363801477026083786683"; - String exponent = "65537"; - - RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent)); - RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent)); - KeyFactory factory = KeyFactory.getInstance("RSA"); - return new KeyPair(factory.generatePublic(publicSpec), factory.generatePrivate(privateSpec)); - } catch ( Exception e ) { - throw new IllegalArgumentException(e); - } - } -} - -/** - * Legacy Authorization Server does not support a custom name for the user parameter, so we'll need - * to extend the default. By default, it uses the attribute {@code user_name}, though it would be - * better to adhere to the {@code sub} property defined in the - * <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JWT Specification</a>. - */ -class SubjectAttributeUserTokenConverter extends DefaultUserAuthenticationConverter { - @Override - public Map<String, ?> convertUserAuthentication(Authentication authentication) { - Map<String, Object> response = new LinkedHashMap<>(); - response.put("sub", authentication.getName()); - if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) { - response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities())); - } - return response; - } -} diff --git a/samples/boot/oauth2authorizationserver/src/main/java/sample/OAuth2AuthorizationServerApplication.java b/samples/boot/oauth2authorizationserver/src/main/java/sample/OAuth2AuthorizationServerApplication.java deleted file mode 100644 index 13602ec74e5..00000000000 --- a/samples/boot/oauth2authorizationserver/src/main/java/sample/OAuth2AuthorizationServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2AuthorizationServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2AuthorizationServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2authorizationserver/src/main/resources/application.yml b/samples/boot/oauth2authorizationserver/src/main/resources/application.yml deleted file mode 100644 index b0b10a294f5..00000000000 --- a/samples/boot/oauth2authorizationserver/src/main/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -server.port: 8081 - -# security.oauth2.authorizationserver.jwt.enabled: false diff --git a/samples/boot/oauth2authorizationserver/src/test/java/sample/OAuth2AuthorizationServerApplicationTests.java b/samples/boot/oauth2authorizationserver/src/test/java/sample/OAuth2AuthorizationServerApplicationTests.java deleted file mode 100644 index 05377bf3f86..00000000000 --- a/samples/boot/oauth2authorizationserver/src/test/java/sample/OAuth2AuthorizationServerApplicationTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Tests for {@link OAuth2AuthorizationServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class OAuth2AuthorizationServerApplicationTests { - - @Autowired - MockMvc mvc; - - @Test - public void requestTokenWhenUsingPasswordGrantTypeThenOk() - throws Exception { - - this.mvc.perform(post("/oauth/token") - .param("grant_type", "password") - .param("username", "subject") - .param("password", "password") - .header("Authorization", "Basic cmVhZGVyOnNlY3JldA==")) - .andExpect(status().isOk()); - } - - @Test - public void requestJwkSetWhenUsingDefaultsThenOk() - throws Exception { - - this.mvc.perform(get("/.well-known/jwks.json")) - .andExpect(status().isOk()); - } - -} diff --git a/samples/boot/oauth2login-webflux/README.adoc b/samples/boot/oauth2login-webflux/README.adoc deleted file mode 100644 index a96fec395be..00000000000 --- a/samples/boot/oauth2login-webflux/README.adoc +++ /dev/null @@ -1,324 +0,0 @@ -NOTE: Spring Security Reactive OAuth only supports authentication using a user info endpoint. -Support for JWT validation will be added in https://github.com/spring-projects/spring-security/issues/5330[gh-5330]. - -= OAuth 2.0 Login Sample - -This guide provides instructions on setting up the sample application with OAuth 2.0 Login using an OAuth 2.0 Provider or OpenID Connect 1.0 Provider. -The sample application uses Spring Boot 2.0.0.M6 and the `spring-security-oauth2-client` module which is new in Spring Security 5.0. - -The following sections provide detailed steps for setting up OAuth 2.0 Login for these Providers: - -* <<google-login, Google>> -* <<github-login, GitHub>> -* <<facebook-login, Facebook>> -* <<okta-login, Okta>> - -[[google-login]] -== Login with Google - -This section shows how to configure the sample application using Google as the Authentication Provider and covers the following topics: - -* <<google-initial-setup,Initial setup>> -* <<google-redirect-uri,Setting the redirect URI>> -* <<google-application-config,Configure application.yml>> -* <<google-boot-application,Boot up the application>> - -[[google-initial-setup]] -=== Initial setup - -To use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials. - -NOTE: https://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the - https://openid.net/connect/[OpenID Connect 1.0] specification and is https://openid.net/certification/[OpenID Certified]. - -Follow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the section, "Setting up OAuth 2.0". - -After completing the "Obtain OAuth 2.0 credentials" instructions, you should have a new OAuth Client with credentials consisting of a Client ID and a Client Secret. - -[[google-redirect-uri]] -=== Setting the redirect URI - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google -and have granted access to the OAuth Client _(created in the previous step)_ on the Consent page. - -In the "Set a redirect URI" sub-section, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[google-application-config]] -=== Configure application.yml - -Now that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - google: <2> - client-id: google-client-id - client-secret: google-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as google. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[google-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Google. - -Click on the Google link, and you are then redirected to Google for authentication. - -After authenticating with your Google account credentials, the next page presented to you is the Consent screen. -The Consent screen asks you to either allow or deny access to the OAuth Client you created earlier. -Click *Allow* to authorize the OAuth Client to access your email address and basic profile information. - -At this point, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. - -[[github-login]] -== Login with GitHub - -This section shows how to configure the sample application using GitHub as the Authentication Provider and covers the following topics: - -* <<github-register-application,Register OAuth application>> -* <<github-application-config,Configure application.yml>> -* <<github-boot-application,Boot up the application>> - -[[github-register-application]] -=== Register OAuth application - -To use GitHub's OAuth 2.0 authentication system for login, you must https://github.com/settings/applications/new[Register a new OAuth application]. - -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/github`. - -The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub -and have granted access to the OAuth application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[github-application-config]] -=== Configure application.yml - -Now that you have a new OAuth application with GitHub, you need to configure the application to use the OAuth application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - github: <2> - client-id: github-client-id - client-secret: github-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as github. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[github-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for GitHub. - -Click on the GitHub link, and you are then redirected to GitHub for authentication. - -After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your personal user data information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[TIP] -For detailed information returned from the UserInfo Endpoint, see the API documentation -for https://developer.github.com/v3/users/#get-the-authenticated-user["Get the authenticated user"]. - -[[facebook-login]] -== Login with Facebook - -This section shows how to configure the sample application using Facebook as the Authentication Provider and covers the following topics: - -* <<facebook-register-application,Add a New App>> -* <<facebook-application-config,Configure application.yml>> -* <<facebook-boot-application,Boot up the application>> - -[[facebook-register-application]] -=== Add a New App - -To use Facebook's OAuth 2.0 authentication system for login, you must first https://developers.facebook.com/apps[Add a New App]. - -Select "Create a New App" and then the "Create a New App ID" page is presented. Enter the Display Name, Contact Email, Category and then click "Create App ID". - -NOTE: The selection for the _Category_ field is not relevant but it's a required field - select "Local". - -The next page presented is "Product Setup". Click the "Get Started" button for the *Facebook Login* product. -In the left sidebar, under _Products -> Facebook Login_, select _Settings_. - -For the field *Valid OAuth redirect URIs*, enter `http://localhost:8080/login/oauth2/code/facebook` then click _Save Changes_. - -The OAuth redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Facebook -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[facebook-application-config]] -=== Configure application.yml - -Now that you have created a new application with Facebook, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - facebook: <2> - client-id: facebook-client-id - client-secret: facebook-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as facebook. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[facebook-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Facebook. - -Click on the Facebook link, and you are then redirected to Facebook for authentication. - -After authenticating with your Facebook credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your _public profile_ and _email address_ information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[[okta-login]] -== Login with Okta - -This section shows how to configure the sample application using Okta as the Authentication Provider and covers the following topics: - -* <<okta-register-application,Add Application>> -* <<okta-assign-application-people,Assign Application to People>> -* <<okta-application-config,Configure application.yml>> -* <<okta-boot-application,Boot up the application>> - -[[okta-register-application]] -=== Add Application - -To use Okta's OAuth 2.0 authentication system for login, you must first https://www.okta.com/developer/signup[create a developer account]. - -Sign in to your account sub-domain and navigate to _Applications -> Applications_ and then select the "Add Application" button. -From the "Add Application" page, select the "Create New App" button and enter the following: - -* *Platform:* Web -* *Sign on method:* OpenID Connect - -Select the _Create_ button. -On the "General Settings" page, enter the Application Name (for example, "Spring Security Okta Login") and then select the _Next_ button. -On the "Configure OpenID Connect" page, enter `http://localhost:8080/login/oauth2/code/okta` for the field *Redirect URIs* and then select _Finish_. - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Okta -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[okta-assign-application-people]] -=== Assign Application to People - -From the "General" tab of the application, select the "Assignments" tab and then select the _Assign_ button. -Select _Assign to People_ and assign your account to the application. Then select the _Save and Go Back_ button. - -[[okta-application-config]] -=== Configure application.yml - -Now that you have created a new application with Okta, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - okta: <2> - client-id: okta-client-id - client-secret: okta-client-secret - provider: <3> - okta: - authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize - token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token - user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo - user-name-attribute: sub - jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as okta. -<3> `spring.security.oauth2.client.provider` is the base property prefix for OAuth Provider properties. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. -As well, replace `https://your-subdomain.oktapreview.com` in `authorization-uri`, `token-uri`, `user-info-uri` and `jwk-set-uri` with the sub-domain assigned to your account during the registration process. - -[[okta-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Okta. - -Click on the Okta link, and you are then redirected to Okta for authentication. - -After authenticating with your Okta account credentials, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. diff --git a/samples/boot/oauth2login-webflux/spring-security-samples-boot-oauth2login-webflux.gradle b/samples/boot/oauth2login-webflux/spring-security-samples-boot-oauth2login-webflux.gradle deleted file mode 100644 index 49099dbd7a8..00000000000 --- a/samples/boot/oauth2login-webflux/spring-security-samples-boot-oauth2login-webflux.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-jose') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-webflux' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - testCompile project(':spring-security-test') - testCompile 'net.sourceforge.htmlunit:htmlunit' - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2login-webflux/src/integration-test/java/sample/OAuth2LoginApplicationTests.java b/samples/boot/oauth2login-webflux/src/integration-test/java/sample/OAuth2LoginApplicationTests.java deleted file mode 100644 index c3e2b63af6d..00000000000 --- a/samples/boot/oauth2login-webflux/src/integration-test/java/sample/OAuth2LoginApplicationTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.hamcrest.core.StringContains.containsString; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Login; - -/** - * Tests for {@link ReactiveOAuth2LoginApplication} - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureWebTestClient -public class OAuth2LoginApplicationTests { - - @Autowired - WebTestClient test; - - @Autowired - ReactiveClientRegistrationRepository clientRegistrationRepository; - - @Test - public void requestWhenMockOidcLoginThenIndex() { - this.clientRegistrationRepository.findByRegistrationId("github") - .map((clientRegistration) -> - this.test.mutateWith(mockOAuth2Login().clientRegistration(clientRegistration)) - .get().uri("/") - .exchange() - .expectBody(String.class).value(containsString("GitHub")) - ).block(); - } -} diff --git a/samples/boot/oauth2login-webflux/src/main/java/sample/ReactiveOAuth2LoginApplication.java b/samples/boot/oauth2login-webflux/src/main/java/sample/ReactiveOAuth2LoginApplication.java deleted file mode 100644 index f6fd94e8490..00000000000 --- a/samples/boot/oauth2login-webflux/src/main/java/sample/ReactiveOAuth2LoginApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - */ -@SpringBootApplication -public class ReactiveOAuth2LoginApplication { - - public static void main(String[] args) { - SpringApplication.run(ReactiveOAuth2LoginApplication.class, args); - } - -} diff --git a/samples/boot/oauth2login-webflux/src/main/java/sample/web/OAuth2LoginController.java b/samples/boot/oauth2login-webflux/src/main/java/sample/web/OAuth2LoginController.java deleted file mode 100644 index 2e03a6adcd4..00000000000 --- a/samples/boot/oauth2login-webflux/src/main/java/sample/web/OAuth2LoginController.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Rob Winch - */ -@Controller -public class OAuth2LoginController { - - @GetMapping("/") - public String index(Model model, - @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, - @AuthenticationPrincipal OAuth2User oauth2User) { - model.addAttribute("userName", oauth2User.getName()); - model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName()); - model.addAttribute("userAttributes", oauth2User.getAttributes()); - return "index"; - } -} diff --git a/samples/boot/oauth2login-webflux/src/main/resources/application.yml b/samples/boot/oauth2login-webflux/src/main/resources/application.yml deleted file mode 100644 index 73b08fbd839..00000000000 --- a/samples/boot/oauth2login-webflux/src/main/resources/application.yml +++ /dev/null @@ -1,35 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - google: - client-id: your-app-client-id - client-secret: your-app-client-secret - github: - client-id: your-app-client-id - client-secret: your-app-client-secret - facebook: - client-id: your-app-client-id - client-secret: your-app-client-secret - okta: - client-id: your-app-client-id - client-secret: your-app-client-secret - provider: - okta: - authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize - token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token - user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo - jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys diff --git a/samples/boot/oauth2login-webflux/src/main/resources/templates/index.html b/samples/boot/oauth2login-webflux/src/main/resources/templates/index.html deleted file mode 100644 index ce8cdcde232..00000000000 --- a/samples/boot/oauth2login-webflux/src/main/resources/templates/index.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<!-- - ~ /* - ~ * Copyright 2002-2018 the original author or authors. - ~ * - ~ * 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 - ~ * - ~ * https://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. - ~ */ - ~ - --> - -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> -<head> - <title>Spring Security - OAuth 2.0 Login</title> - <meta charset="utf-8" /> -</head> -<body> -<div style="float: right" th:fragment="logout"> - <div style="float:left"> - <span style="font-weight:bold">User: </span><span th:text="${userName}"></span> - </div> - <div style="float:none"> </div> - <div style="float:right"> - <a th:href="@{/logout}">Log Out</a> - </div> -</div> -<h1>OAuth 2.0 Login with Spring Security</h1> -<div> - You are successfully logged in <span style="font-weight:bold" th:text="${userName}"></span> - via the OAuth 2.0 Client <span style="font-weight:bold" th:text="${clientName}"></span> -</div> -<div> </div> -<div> - <span style="font-weight:bold">User Attributes:</span> - <ul> - <li th:each="userAttribute : ${userAttributes}"> - <span style="font-weight:bold" th:text="${userAttribute.key}"></span>: <span th:text="${userAttribute.value}"></span> - </li> - </ul> -</div> -</body> -</html> diff --git a/samples/boot/oauth2login-webflux/src/test/java/sample/OAuth2LoginControllerTests.java b/samples/boot/oauth2login-webflux/src/test/java/sample/OAuth2LoginControllerTests.java deleted file mode 100644 index 11ae5f8ba6f..00000000000 --- a/samples/boot/oauth2login-webflux/src/test/java/sample/OAuth2LoginControllerTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import sample.web.OAuth2LoginController; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.reactive.result.method.annotation.OAuth2AuthorizedClientArgumentResolver; -import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; -import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver; -import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.result.view.ViewResolver; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Login; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; - -/** - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@WebFluxTest(OAuth2LoginController.class) -public class OAuth2LoginControllerTests { - - @Autowired - OAuth2LoginController controller; - - @Autowired - ViewResolver viewResolver; - - @Mock - ReactiveClientRegistrationRepository clientRegistrationRepository; - - @Mock - ServerOAuth2AuthorizedClientRepository authorizedClientRepository; - - WebTestClient rest; - - @Before - public void setup() { - this.rest = WebTestClient - .bindToController(this.controller) - .apply(springSecurity()) - .webFilter(new SecurityContextServerWebExchangeWebFilter()) - .argumentResolvers((c) -> { - c.addCustomResolver(new AuthenticationPrincipalArgumentResolver(new ReactiveAdapterRegistry())); - c.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver - (this.clientRegistrationRepository, this.authorizedClientRepository)); - }) - .viewResolvers((c) -> c.viewResolver(this.viewResolver)) - .build(); - } - - @Test - public void indexGreetsAuthenticatedUser() { - this.rest.mutateWith(mockOAuth2Login()) - .get().uri("/").exchange() - .expectBody(String.class).value(containsString("user")); - } -} diff --git a/samples/boot/oauth2login/README.adoc b/samples/boot/oauth2login/README.adoc deleted file mode 100644 index e949a7f84e1..00000000000 --- a/samples/boot/oauth2login/README.adoc +++ /dev/null @@ -1,321 +0,0 @@ -= OAuth 2.0 Login Sample - -This guide provides instructions on setting up the sample application with OAuth 2.0 Login using an OAuth 2.0 Provider or OpenID Connect 1.0 Provider. -The sample application uses Spring Boot 2.0.0.M6 and the `spring-security-oauth2-client` module which is new in Spring Security 5.0. - -The following sections provide detailed steps for setting up OAuth 2.0 Login for these Providers: - -* <<google-login, Google>> -* <<github-login, GitHub>> -* <<facebook-login, Facebook>> -* <<okta-login, Okta>> - -[[google-login]] -== Login with Google - -This section shows how to configure the sample application using Google as the Authentication Provider and covers the following topics: - -* <<google-initial-setup,Initial setup>> -* <<google-redirect-uri,Setting the redirect URI>> -* <<google-application-config,Configure application.yml>> -* <<google-boot-application,Boot up the application>> - -[[google-initial-setup]] -=== Initial setup - -To use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials. - -NOTE: https://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the - https://openid.net/connect/[OpenID Connect 1.0] specification and is https://openid.net/certification/[OpenID Certified]. - -Follow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the section, "Setting up OAuth 2.0". - -After completing the "Obtain OAuth 2.0 credentials" instructions, you should have a new OAuth Client with credentials consisting of a Client ID and a Client Secret. - -[[google-redirect-uri]] -=== Setting the redirect URI - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google -and have granted access to the OAuth Client _(created in the previous step)_ on the Consent page. - -In the "Set a redirect URI" sub-section, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[google-application-config]] -=== Configure application.yml - -Now that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - google: <2> - client-id: google-client-id - client-secret: google-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as google. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[google-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Google. - -Click on the Google link, and you are then redirected to Google for authentication. - -After authenticating with your Google account credentials, the next page presented to you is the Consent screen. -The Consent screen asks you to either allow or deny access to the OAuth Client you created earlier. -Click *Allow* to authorize the OAuth Client to access your email address and basic profile information. - -At this point, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. - -[[github-login]] -== Login with GitHub - -This section shows how to configure the sample application using GitHub as the Authentication Provider and covers the following topics: - -* <<github-register-application,Register OAuth application>> -* <<github-application-config,Configure application.yml>> -* <<github-boot-application,Boot up the application>> - -[[github-register-application]] -=== Register OAuth application - -To use GitHub's OAuth 2.0 authentication system for login, you must https://github.com/settings/applications/new[Register a new OAuth application]. - -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/github`. - -The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub -and have granted access to the OAuth application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[github-application-config]] -=== Configure application.yml - -Now that you have a new OAuth application with GitHub, you need to configure the application to use the OAuth application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - github: <2> - client-id: github-client-id - client-secret: github-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as github. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[github-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for GitHub. - -Click on the GitHub link, and you are then redirected to GitHub for authentication. - -After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your personal user data information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[TIP] -For detailed information returned from the UserInfo Endpoint, see the API documentation -for https://developer.github.com/v3/users/#get-the-authenticated-user["Get the authenticated user"]. - -[[facebook-login]] -== Login with Facebook - -This section shows how to configure the sample application using Facebook as the Authentication Provider and covers the following topics: - -* <<facebook-register-application,Add a New App>> -* <<facebook-application-config,Configure application.yml>> -* <<facebook-boot-application,Boot up the application>> - -[[facebook-register-application]] -=== Add a New App - -To use Facebook's OAuth 2.0 authentication system for login, you must first https://developers.facebook.com/apps[Add a New App]. - -Select "Create a New App" and then the "Create a New App ID" page is presented. Enter the Display Name, Contact Email, Category and then click "Create App ID". - -NOTE: The selection for the _Category_ field is not relevant but it's a required field - select "Local". - -The next page presented is "Product Setup". Click the "Get Started" button for the *Facebook Login* product. -In the left sidebar, under _Products -> Facebook Login_, select _Settings_. - -For the field *Valid OAuth redirect URIs*, enter `http://localhost:8080/login/oauth2/code/facebook` then click _Save Changes_. - -The OAuth redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Facebook -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[facebook-application-config]] -=== Configure application.yml - -Now that you have created a new application with Facebook, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - facebook: <2> - client-id: facebook-client-id - client-secret: facebook-client-secret ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as facebook. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[facebook-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Facebook. - -Click on the Facebook link, and you are then redirected to Facebook for authentication. - -After authenticating with your Facebook credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your _public profile_ and _email address_ information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[[okta-login]] -== Login with Okta - -This section shows how to configure the sample application using Okta as the Authentication Provider and covers the following topics: - -* <<okta-register-application,Add Application>> -* <<okta-assign-application-people,Assign Application to People>> -* <<okta-application-config,Configure application.yml>> -* <<okta-boot-application,Boot up the application>> - -[[okta-register-application]] -=== Add Application - -To use Okta's OAuth 2.0 authentication system for login, you must first https://www.okta.com/developer/signup[create a developer account]. - -Sign in to your account sub-domain and navigate to _Applications -> Applications_ and then select the "Add Application" button. -From the "Add Application" page, select the "Create New App" button and enter the following: - -* *Platform:* Web -* *Sign on method:* OpenID Connect - -Select the _Create_ button. -On the "General Settings" page, enter the Application Name (for example, "Spring Security Okta Login") and then select the _Next_ button. -On the "Configure OpenID Connect" page, enter `http://localhost:8080/login/oauth2/code/okta` for the field *Redirect URIs* and then select _Finish_. - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Okta -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[okta-assign-application-people]] -=== Assign Application to People - -From the "General" tab of the application, select the "Assignments" tab and then select the _Assign_ button. -Select _Assign to People_ and assign your account to the application. Then select the _Save and Go Back_ button. - -[[okta-application-config]] -=== Configure application.yml - -Now that you have created a new application with Okta, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - okta: <2> - client-id: okta-client-id - client-secret: okta-client-secret - provider: <3> - okta: - authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize - token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token - user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo - user-name-attribute: sub - jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, such as okta. -<3> `spring.security.oauth2.client.provider` is the base property prefix for OAuth Provider properties. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. -As well, replace `https://your-subdomain.oktapreview.com` in `authorization-uri`, `token-uri`, `user-info-uri` and `jwk-set-uri` with the sub-domain assigned to your account during the registration process. - -[[okta-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Okta. - -Click on the Okta link, and you are then redirected to Okta for authentication. - -After authenticating with your Okta account credentials, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. diff --git a/samples/boot/oauth2login/spring-security-samples-boot-oauth2login.gradle b/samples/boot/oauth2login/spring-security-samples-boot-oauth2login.gradle deleted file mode 100644 index ef3a4e27b56..00000000000 --- a/samples/boot/oauth2login/spring-security-samples-boot-oauth2login.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-jose') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - testCompile project(':spring-security-test') - testCompile 'net.sourceforge.htmlunit:htmlunit' - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2login/src/integration-test/java/sample/OAuth2LoginApplicationTests.java b/samples/boot/oauth2login/src/integration-test/java/sample/OAuth2LoginApplicationTests.java deleted file mode 100644 index 77867dbbc84..00000000000 --- a/samples/boot/oauth2login/src/integration-test/java/sample/OAuth2LoginApplicationTests.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.net.URI; -import java.net.URL; -import java.net.URLDecoder; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebResponse; -import com.gargoylesoftware.htmlunit.html.DomNodeList; -import com.gargoylesoftware.htmlunit.html.HtmlAnchor; -import com.gargoylesoftware.htmlunit.html.HtmlElement; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; -import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; -import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.security.oauth2.core.user.DefaultOAuth2User; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; - -/** - * Integration tests for the OAuth 2.0 client filters {@link OAuth2AuthorizationRequestRedirectFilter} - * and {@link OAuth2LoginAuthenticationFilter}. These filters work together to realize - * OAuth 2.0 Login leveraging the Authorization Code Grant flow. - * - * @author Joe Grandja - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class OAuth2LoginApplicationTests { - private static final String AUTHORIZATION_BASE_URI = "/oauth2/authorization"; - private static final String AUTHORIZE_BASE_URL = "http://localhost:8080/login/oauth2/code"; - - @Autowired - private WebClient webClient; - - @Autowired - private MockMvc mvc; - - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - @Before - public void setup() { - this.webClient.getCookieManager().clearCookies(); - } - - @Test - public void requestIndexPageWhenNotAuthenticatedThenDisplayLoginPage() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - this.assertLoginPage(page); - } - - @Test - public void requestOtherPageWhenNotAuthenticatedThenDisplayLoginPage() throws Exception { - HtmlPage page = this.webClient.getPage("/other-page"); - this.assertLoginPage(page); - } - - @Test - public void requestAuthorizeGitHubClientWhenLinkClickedThenStatusRedirectForAuthorization() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - - HtmlAnchor clientAnchorElement = this.getClientAnchorElement(page, clientRegistration); - assertThat(clientAnchorElement).isNotNull(); - - WebResponse response = this.followLinkDisableRedirects(clientAnchorElement); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.MOVED_PERMANENTLY.value()); - - String authorizeRedirectUri = response.getResponseHeaderValue("Location"); - assertThat(authorizeRedirectUri).isNotNull(); - - UriComponents uriComponents = UriComponentsBuilder.fromUri(URI.create(authorizeRedirectUri)).build(); - - String requestUri = uriComponents.getScheme() + "://" + uriComponents.getHost() + uriComponents.getPath(); - assertThat(requestUri).isEqualTo(clientRegistration.getProviderDetails().getAuthorizationUri()); - - Map<String, String> params = uriComponents.getQueryParams().toSingleValueMap(); - - assertThat(params.get(OAuth2ParameterNames.RESPONSE_TYPE)).isEqualTo(OAuth2AuthorizationResponseType.CODE.getValue()); - assertThat(params.get(OAuth2ParameterNames.CLIENT_ID)).isEqualTo(clientRegistration.getClientId()); - String redirectUri = AUTHORIZE_BASE_URL + "/" + clientRegistration.getRegistrationId(); - assertThat(URLDecoder.decode(params.get(OAuth2ParameterNames.REDIRECT_URI), "UTF-8")).isEqualTo(redirectUri); - assertThat(URLDecoder.decode(params.get(OAuth2ParameterNames.SCOPE), "UTF-8")) - .isEqualTo(clientRegistration.getScopes().stream().collect(Collectors.joining(" "))); - assertThat(params.get(OAuth2ParameterNames.STATE)).isNotNull(); - } - - @Test - public void requestAuthorizeClientWhenInvalidClientThenStatusInternalServerError() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - - HtmlAnchor clientAnchorElement = this.getClientAnchorElement(page, clientRegistration); - assertThat(clientAnchorElement).isNotNull(); - clientAnchorElement.setAttribute("href", clientAnchorElement.getHrefAttribute() + "-invalid"); - - WebResponse response = null; - try { - clientAnchorElement.click(); - } catch (FailingHttpStatusCodeException ex) { - response = ex.getResponse(); - } - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR.value()); - } - - @Test - public void requestAuthorizationCodeGrantWhenValidAuthorizationResponseThenDisplayIndexPage() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - - HtmlAnchor clientAnchorElement = this.getClientAnchorElement(page, clientRegistration); - assertThat(clientAnchorElement).isNotNull(); - - WebResponse response = this.followLinkDisableRedirects(clientAnchorElement); - - UriComponents authorizeRequestUriComponents = UriComponentsBuilder.fromUri( - URI.create(response.getResponseHeaderValue("Location"))).build(); - - Map<String, String> params = authorizeRequestUriComponents.getQueryParams().toSingleValueMap(); - String code = "auth-code"; - String state = URLDecoder.decode(params.get(OAuth2ParameterNames.STATE), "UTF-8"); - String redirectUri = URLDecoder.decode(params.get(OAuth2ParameterNames.REDIRECT_URI), "UTF-8"); - - String authorizationResponseUri = - UriComponentsBuilder.fromHttpUrl(redirectUri) - .queryParam(OAuth2ParameterNames.CODE, code) - .queryParam(OAuth2ParameterNames.STATE, state) - .build().encode().toUriString(); - - page = this.webClient.getPage(new URL(authorizationResponseUri)); - this.assertIndexPage(page); - } - - @Test - public void requestAuthorizationCodeGrantWhenNoMatchingAuthorizationRequestThenDisplayLoginPageWithError() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - URL loginPageUrl = page.getBaseURL(); - URL loginErrorPageUrl = new URL(loginPageUrl.toString() + "?error"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - - String code = "auth-code"; - String state = "state"; - String redirectUri = AUTHORIZE_BASE_URL + "/" + clientRegistration.getRegistrationId(); - - String authorizationResponseUri = - UriComponentsBuilder.fromHttpUrl(redirectUri) - .queryParam(OAuth2ParameterNames.CODE, code) - .queryParam(OAuth2ParameterNames.STATE, state) - .build().encode().toUriString(); - - // Clear session cookie will ensure the 'session-saved' - // Authorization Request (from previous request) is not found - this.webClient.getCookieManager().clearCookies(); - - page = this.webClient.getPage(new URL(authorizationResponseUri)); - assertThat(page.getBaseURL()).isEqualTo(loginErrorPageUrl); - - HtmlElement errorElement = page.getBody().getFirstByXPath("div"); - assertThat(errorElement).isNotNull(); - assertThat(errorElement.asText()).contains("authorization_request_not_found"); - } - - @Test - public void requestAuthorizationCodeGrantWhenInvalidStateParamThenDisplayLoginPageWithError() throws Exception { - HtmlPage page = this.webClient.getPage("/"); - URL loginPageUrl = page.getBaseURL(); - URL loginErrorPageUrl = new URL(loginPageUrl.toString() + "?error"); - - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - - HtmlAnchor clientAnchorElement = this.getClientAnchorElement(page, clientRegistration); - assertThat(clientAnchorElement).isNotNull(); - this.followLinkDisableRedirects(clientAnchorElement); - - String code = "auth-code"; - String state = "invalid-state"; - String redirectUri = AUTHORIZE_BASE_URL + "/" + clientRegistration.getRegistrationId(); - - String authorizationResponseUri = - UriComponentsBuilder.fromHttpUrl(redirectUri) - .queryParam(OAuth2ParameterNames.CODE, code) - .queryParam(OAuth2ParameterNames.STATE, state) - .build().encode().toUriString(); - - page = this.webClient.getPage(new URL(authorizationResponseUri)); - assertThat(page.getBaseURL()).isEqualTo(loginErrorPageUrl); - - HtmlElement errorElement = page.getBody().getFirstByXPath("div"); - assertThat(errorElement).isNotNull(); - assertThat(errorElement.asText()).contains("authorization_request_not_found"); - } - - @Test - public void requestWhenMockOAuth2LoginThenIndex() throws Exception { - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - this.mvc.perform(get("/").with(oauth2Login().clientRegistration(clientRegistration))) - .andExpect(model().attribute("userName", "user")) - .andExpect(model().attribute("clientName", "GitHub")) - .andExpect(model().attribute("userAttributes", Collections.singletonMap("sub", "user"))); - } - - private void assertLoginPage(HtmlPage page) { - assertThat(page.getTitleText()).isEqualTo("Please sign in"); - - int expectedClients = 4; - - List<HtmlAnchor> clientAnchorElements = page.getAnchors(); - assertThat(clientAnchorElements.size()).isEqualTo(expectedClients); - - ClientRegistration googleClientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - ClientRegistration githubClientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - ClientRegistration facebookClientRegistration = this.clientRegistrationRepository.findByRegistrationId("facebook"); - ClientRegistration oktaClientRegistration = this.clientRegistrationRepository.findByRegistrationId("okta"); - - String baseAuthorizeUri = AUTHORIZATION_BASE_URI + "/"; - String googleClientAuthorizeUri = baseAuthorizeUri + googleClientRegistration.getRegistrationId(); - String githubClientAuthorizeUri = baseAuthorizeUri + githubClientRegistration.getRegistrationId(); - String facebookClientAuthorizeUri = baseAuthorizeUri + facebookClientRegistration.getRegistrationId(); - String oktaClientAuthorizeUri = baseAuthorizeUri + oktaClientRegistration.getRegistrationId(); - - for (int i=0; i<expectedClients; i++) { - assertThat(clientAnchorElements.get(i).getAttribute("href")).isIn( - googleClientAuthorizeUri, githubClientAuthorizeUri, - facebookClientAuthorizeUri, oktaClientAuthorizeUri); - assertThat(clientAnchorElements.get(i).asText()).isIn( - googleClientRegistration.getClientName(), - githubClientRegistration.getClientName(), - facebookClientRegistration.getClientName(), - oktaClientRegistration.getClientName()); - } - } - - private void assertIndexPage(HtmlPage page) { - assertThat(page.getTitleText()).isEqualTo("Spring Security - OAuth 2.0 Login"); - - DomNodeList<HtmlElement> divElements = page.getBody().getElementsByTagName("div"); - assertThat(divElements.get(1).asText()).contains("User: joeg@springsecurity.io"); - assertThat(divElements.get(4).asText()).contains("You are successfully logged in joeg@springsecurity.io"); - } - - private HtmlAnchor getClientAnchorElement(HtmlPage page, ClientRegistration clientRegistration) { - Optional<HtmlAnchor> clientAnchorElement = page.getAnchors().stream() - .filter((e) -> e.asText().equals(clientRegistration.getClientName())).findFirst(); - - return (clientAnchorElement.orElse(null)); - } - - private WebResponse followLinkDisableRedirects(HtmlAnchor anchorElement) throws Exception { - WebResponse response = null; - try { - // Disable the automatic redirection (which will trigger - // an exception) so that we can capture the response - this.webClient.getOptions().setRedirectEnabled(false); - anchorElement.click(); - } catch (FailingHttpStatusCodeException ex) { - response = ex.getResponse(); - this.webClient.getOptions().setRedirectEnabled(true); - } - return response; - } - - @EnableWebSecurity - @TestConfiguration - public static class SecurityTestConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .anyRequest().authenticated() - ) - .oauth2Login((oauth2Login) -> - oauth2Login - .tokenEndpoint((tokenEndpoint) -> - tokenEndpoint - .accessTokenResponseClient(this.mockAccessTokenResponseClient()) - ) - .userInfoEndpoint((userInfoEndpoint) -> - userInfoEndpoint - .userService(this.mockUserService()) - ) - ); - } - // @formatter:on - - private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> mockAccessTokenResponseClient() { - OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234") - .tokenType(OAuth2AccessToken.TokenType.BEARER) - .expiresIn(60 * 1000) - .build(); - - OAuth2AccessTokenResponseClient tokenResponseClient = mock(OAuth2AccessTokenResponseClient.class); - when(tokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - return tokenResponseClient; - } - - private OAuth2UserService<OAuth2UserRequest, OAuth2User> mockUserService() { - Map<String, Object> attributes = new HashMap<>(); - attributes.put("id", "joeg"); - attributes.put("first-name", "Joe"); - attributes.put("last-name", "Grandja"); - attributes.put("email", "joeg@springsecurity.io"); - - GrantedAuthority authority = new OAuth2UserAuthority(attributes); - Set<GrantedAuthority> authorities = new HashSet<>(); - authorities.add(authority); - - DefaultOAuth2User user = new DefaultOAuth2User(authorities, attributes, "email"); - - OAuth2UserService userService = mock(OAuth2UserService.class); - when(userService.loadUser(any())).thenReturn(user); - return userService; - } - } -} diff --git a/samples/boot/oauth2login/src/main/java/sample/OAuth2LoginApplication.java b/samples/boot/oauth2login/src/main/java/sample/OAuth2LoginApplication.java deleted file mode 100644 index dc69b3f9ec3..00000000000 --- a/samples/boot/oauth2login/src/main/java/sample/OAuth2LoginApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Joe Grandja - */ -@SpringBootApplication -public class OAuth2LoginApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2LoginApplication.class, args); - } -} diff --git a/samples/boot/oauth2login/src/main/java/sample/web/OAuth2LoginController.java b/samples/boot/oauth2login/src/main/java/sample/web/OAuth2LoginController.java deleted file mode 100644 index fa46a60ad79..00000000000 --- a/samples/boot/oauth2login/src/main/java/sample/web/OAuth2LoginController.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Joe Grandja - * @author Rob Winch - */ -@Controller -public class OAuth2LoginController { - - @GetMapping("/") - public String index(Model model, - @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, - @AuthenticationPrincipal OAuth2User oauth2User) { - model.addAttribute("userName", oauth2User.getName()); - model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName()); - model.addAttribute("userAttributes", oauth2User.getAttributes()); - return "index"; - } -} diff --git a/samples/boot/oauth2login/src/main/resources/application.yml b/samples/boot/oauth2login/src/main/resources/application.yml deleted file mode 100644 index 73b08fbd839..00000000000 --- a/samples/boot/oauth2login/src/main/resources/application.yml +++ /dev/null @@ -1,35 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - google: - client-id: your-app-client-id - client-secret: your-app-client-secret - github: - client-id: your-app-client-id - client-secret: your-app-client-secret - facebook: - client-id: your-app-client-id - client-secret: your-app-client-secret - okta: - client-id: your-app-client-id - client-secret: your-app-client-secret - provider: - okta: - authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize - token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token - user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo - jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys diff --git a/samples/boot/oauth2login/src/main/resources/templates/index.html b/samples/boot/oauth2login/src/main/resources/templates/index.html deleted file mode 100644 index 629d8ac8ee5..00000000000 --- a/samples/boot/oauth2login/src/main/resources/templates/index.html +++ /dev/null @@ -1,34 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> -<head> - <title>Spring Security - OAuth 2.0 Login</title> - <meta charset="utf-8" /> -</head> -<body> -<div style="float: right" th:fragment="logout" sec:authorize="isAuthenticated()"> - <div style="float:left"> - <span style="font-weight:bold">User: </span><span sec:authentication="name"></span> - </div> - <div style="float:none"> </div> - <div style="float:right"> - <form action="#" th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - </div> -</div> -<h1>OAuth 2.0 Login with Spring Security</h1> -<div> - You are successfully logged in <span style="font-weight:bold" th:text="${userName}"></span> - via the OAuth 2.0 Client <span style="font-weight:bold" th:text="${clientName}"></span> -</div> -<div> </div> -<div> - <span style="font-weight:bold">User Attributes:</span> - <ul> - <li th:each="userAttribute : ${userAttributes}"> - <span style="font-weight:bold" th:text="${userAttribute.key}"></span>: <span th:text="${userAttribute.value}"></span> - </li> - </ul> -</div> -</body> -</html> diff --git a/samples/boot/oauth2login/src/test/java/sample/web/OAuth2LoginControllerTests.java b/samples/boot/oauth2login/src/test/java/sample/web/OAuth2LoginControllerTests.java deleted file mode 100644 index 6a3af42ff7b..00000000000 --- a/samples/boot/oauth2login/src/test/java/sample/web/OAuth2LoginControllerTests.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import java.util.Collections; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; - -/** - * Tests for {@link OAuth2LoginController} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@WebMvcTest(OAuth2LoginController.class) -public class OAuth2LoginControllerTests { - - @Autowired - MockMvc mvc; - - @Test - public void rootWhenAuthenticatedReturnsUserAndClient() throws Exception { - this.mvc.perform(get("/").with(oauth2Login())) - .andExpect(model().attribute("userName", "user")) - .andExpect(model().attribute("clientName", "test")) - .andExpect(model().attribute("userAttributes", Collections.singletonMap("sub", "user"))); - } - - @Test - public void rootWhenOverridingClientRegistrationReturnsAccordingly() throws Exception { - ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("test") - .authorizationGrantType(AuthorizationGrantType.PASSWORD) - .clientId("my-client-id") - .clientName("my-client-name") - .tokenUri("https://token-uri.example.org") - .build(); - - this.mvc.perform(get("/").with(oauth2Login() - .clientRegistration(clientRegistration) - .attributes((a) -> a.put("sub", "spring-security")))) - .andExpect(model().attribute("userName", "spring-security")) - .andExpect(model().attribute("clientName", "my-client-name")) - .andExpect(model().attribute("userAttributes", Collections.singletonMap("sub", "spring-security"))); - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/README.adoc b/samples/boot/oauth2resourceserver-jwe/README.adoc deleted file mode 100644 index 84c64f6f587..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/README.adoc +++ /dev/null @@ -1,119 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a mock Authorization Server, though it can be modified to integrate -with your favorite Authorization Server. This resource server is configured to accept JWE-encrypted tokens. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplicationTests` from there. - -=== What is it doing? - -By default, the tests are pointing at a mock Authorization Server instance. - -The tests are configured with a set of hard-coded tokens originally obtained from the mock Authorization Server, -and each makes a query to the Resource Server with their corresponding token. - -The Resource Server decrypts the token and subsquently verifies it with the Authorization Server and authorizes the request, returning the phrase - -```bash -Hello, subject! -``` - -where "subject" is the value of the `sub` field in the JWT returned by the Authorization Server. - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplication` from there. - -Once it is up, you can use the following token: - -```bash -export TOKEN=eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.IyeWtsTonaiWJdoT13B0M7gpqVxAirVGlfqFI4TOmTRcVHICs_ESezS7fa0ODS9XYdwklTtG7hH39yeeMzr2Zo1Ghh-m36fdoqQrV1Do04rUvuTjqbgyNffeZEGB6rquJ-cyAVjp_Oljy10-Bbnw7CeVGwNBSVo9UCB5j49OlNWhLxFpYARFmOlYpXj-s4Q4JiqV6EvjDAYeohAR4QQmND3qoxR-s2I6SLcIho0sSSpUlhrRiqu2uvWefHDcZJdW2WYWnxLHxhzNu3CfnLiqhhaA_YA_iWXR9FYnPDCf_4q3FgSXcgttXzomFKAx5DwnE_dXvsCvpWxslZMU1UIiLA.MHOrrza2GQ9_5PIv.zU4tfhxT6apWBC3stBwQmGlCQBltWVQe4dFIykybWWBFqxo1bf2BZ37twzoEIFXG9jSYEMH4mvBXPWSvn66t-_jnqLnKTJst2plBjhagGCAoLNWXVKeYNp67o-lKOD_JJQFqsRw4oE05VSgRr14MZeaUBFcU3A_kKxMXOu899DKfXBGJvj75H7lDyd8RUXTb-OSWWfUiJc6Y5AUy1zCZCN9yfDsCXt9heTsZANy92Oou9sMFaXkYzyums5OtkBtLFzyuNMEoNioRehTV-FTuL8tDRB1mNhHObwsBfFbR6M1jJK37pHUXGtko-yZ6NGwxyLtwGh4uU2jzE614rQzuzR8aHaHxOkUs1pBTZ8AcRt41snByOe-KU0adthHxedobFiQQBoQ05DgSU7DO6hsK0uVBDF3eG2KjH4L2lZy-WugloLHhdguUoO9F0zUx15-XZO4EVzmhy8xfH2tSXz98eKzz9Dv0DdGnrBL9cK2MM88N1zoq5u4NdlnE12HvuesB7GKdMwZx1-gTw_pzP81TzcctJWl6ETK09Uc.jk0O8qc4Fvip856stDz05Q -``` - -And then make this request: - -```bash -curl -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject! -``` - -where `subject` is the value of the `sub` field in the JWT returned by the Authorization Server. - -Or this: - -```bash -export TOKEN=eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.CRBAEgvQhpB6pPQhpkTAKpsDai1FDcvkDSRig1R3OI-g18JdTe-qDhzWwP-hV3aCwFwHxQ_g8Z8OIZvhyKpQaPwBb72UeLqqhzSIkm0gEsmmjYg1vEGOrDH5_Fqlm0LnAnXTmsbOIWYIj11ZuenI2lEmMCkVwqth0RlzakdcHRXiuDTEln_trhZpE2j80X-9rS2gZy9Raa9VLir3-F3wC0GKPEL6e3x1OygC03ix9uyXS3vpTsU9zlgoYADZyaLeOF1mCG4mQhvXs7IPmPbwNsElJwKh0xSQCHvNOQShprlvd3cHiUFKYw9fXphY1O-AUYcRzHk4DjoBdkGNQMy_Kw.KtC_z674rYBtDgkN.e8QU50Iq1JHkn-1USSxpjEkbrukb4cobvlQRK40iXGAKVIuOod4bSq5fDpIAPHugqIf-_zGsvr-2OCOdzhtBikL46wU7UdZppxPWtk-X6kl33zH_XObRMaGfe-hLxt3RPxRVn7I1Hp6tGW1Rkxyf_ESq4XlcbbrkhDoIz_G_LKXJhvQ-xahW2e0AUc7RZSucns4XUeq9xX_dd7Ht-o1TmQI9WFoFc1l7oh9GtQ6GZMsghnZ1VrbIS2L7jSYiSsD2JqSv1LLtOGj_FBA0ufhqM3LloGiwflEwAryMD10oNb73WonKEycEj1rBsTIKW7SHkI-VkrQA4-8N-aLWgHwDnzyPZmyNyKpqUMvhjIE_0w6oqU4HpN7J5nfBEIAtpPZ_pDkwAdxCQ7JV3zfiUnF7ZQ3q1PnSId315si02ZN9-wRSrMHcHnboQN1Hs4xCAfGyClVyLpCzfa_fAehjt6v1DjgjbzwSjr_LdNmWTvXYBhNO8Jq9Vb7axksrdwksD3pYNMY8cRZxP-LO0V5Sv1_kT_Hf2yLo2iTwB8n8szzGrJ4QQLb5Znu7Sv-M2x52cnIDMiorP3LNpFk.G85FuMSm-8bGumFAStiFQA - -curl -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message -``` - -== 2. Testing against other Authorization Servers - -_In order to use this sample, your Authorization Server must encrypt using the public key available in the sample. -Also it must support JWTs that either use the "scope" or "scp" attribute._ - -To change the sample to point at your Authorization Server, simply find this property in the `application.yml`: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json -``` - -And change the property to your Authorization Server's JWK set endpoint: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: https://localhost:9031/pf/JWKS -``` - -If your Authorization Server does not support RSA_OAEP_256 or AESGCM, then you can change these values in `OAuth2ResourceServerSecurityConfiguration`: - -```java - -``` - -And then you can run the app the same as before: - -```bash -./gradlew bootRun -``` - -Make sure to obtain valid tokens from your Authorization Server in order to play with the sample Resource Server. -To use the `/` endpoint, any valid token from your Authorization Server will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver-jwe/spring-security-samples-boot-oauth2resourceserver-jwe.gradle b/samples/boot/oauth2resourceserver-jwe/spring-security-samples-boot-oauth2resourceserver-jwe.gradle deleted file mode 100644 index 2135bb0af66..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/spring-security-samples-boot-oauth2resourceserver-jwe.gradle +++ /dev/null @@ -1,13 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'com.squareup.okhttp3:mockwebserver' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver-jwe/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java deleted file mode 100644 index cd25346ce8c..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.RequestPostProcessor; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for {@link OAuth2ResourceServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -public class OAuth2ResourceServerApplicationITests { - - String noScopesToken = "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.IyeWtsTonaiWJdoT13B0M7gpqVxAirVGlfqFI4TOmTRcVHICs_ESezS7fa0ODS9XYdwklTtG7hH39yeeMzr2Zo1Ghh-m36fdoqQrV1Do04rUvuTjqbgyNffeZEGB6rquJ-cyAVjp_Oljy10-Bbnw7CeVGwNBSVo9UCB5j49OlNWhLxFpYARFmOlYpXj-s4Q4JiqV6EvjDAYeohAR4QQmND3qoxR-s2I6SLcIho0sSSpUlhrRiqu2uvWefHDcZJdW2WYWnxLHxhzNu3CfnLiqhhaA_YA_iWXR9FYnPDCf_4q3FgSXcgttXzomFKAx5DwnE_dXvsCvpWxslZMU1UIiLA.MHOrrza2GQ9_5PIv.zU4tfhxT6apWBC3stBwQmGlCQBltWVQe4dFIykybWWBFqxo1bf2BZ37twzoEIFXG9jSYEMH4mvBXPWSvn66t-_jnqLnKTJst2plBjhagGCAoLNWXVKeYNp67o-lKOD_JJQFqsRw4oE05VSgRr14MZeaUBFcU3A_kKxMXOu899DKfXBGJvj75H7lDyd8RUXTb-OSWWfUiJc6Y5AUy1zCZCN9yfDsCXt9heTsZANy92Oou9sMFaXkYzyums5OtkBtLFzyuNMEoNioRehTV-FTuL8tDRB1mNhHObwsBfFbR6M1jJK37pHUXGtko-yZ6NGwxyLtwGh4uU2jzE614rQzuzR8aHaHxOkUs1pBTZ8AcRt41snByOe-KU0adthHxedobFiQQBoQ05DgSU7DO6hsK0uVBDF3eG2KjH4L2lZy-WugloLHhdguUoO9F0zUx15-XZO4EVzmhy8xfH2tSXz98eKzz9Dv0DdGnrBL9cK2MM88N1zoq5u4NdlnE12HvuesB7GKdMwZx1-gTw_pzP81TzcctJWl6ETK09Uc.jk0O8qc4Fvip856stDz05Q"; - String messageReadToken = "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.CRBAEgvQhpB6pPQhpkTAKpsDai1FDcvkDSRig1R3OI-g18JdTe-qDhzWwP-hV3aCwFwHxQ_g8Z8OIZvhyKpQaPwBb72UeLqqhzSIkm0gEsmmjYg1vEGOrDH5_Fqlm0LnAnXTmsbOIWYIj11ZuenI2lEmMCkVwqth0RlzakdcHRXiuDTEln_trhZpE2j80X-9rS2gZy9Raa9VLir3-F3wC0GKPEL6e3x1OygC03ix9uyXS3vpTsU9zlgoYADZyaLeOF1mCG4mQhvXs7IPmPbwNsElJwKh0xSQCHvNOQShprlvd3cHiUFKYw9fXphY1O-AUYcRzHk4DjoBdkGNQMy_Kw.KtC_z674rYBtDgkN.e8QU50Iq1JHkn-1USSxpjEkbrukb4cobvlQRK40iXGAKVIuOod4bSq5fDpIAPHugqIf-_zGsvr-2OCOdzhtBikL46wU7UdZppxPWtk-X6kl33zH_XObRMaGfe-hLxt3RPxRVn7I1Hp6tGW1Rkxyf_ESq4XlcbbrkhDoIz_G_LKXJhvQ-xahW2e0AUc7RZSucns4XUeq9xX_dd7Ht-o1TmQI9WFoFc1l7oh9GtQ6GZMsghnZ1VrbIS2L7jSYiSsD2JqSv1LLtOGj_FBA0ufhqM3LloGiwflEwAryMD10oNb73WonKEycEj1rBsTIKW7SHkI-VkrQA4-8N-aLWgHwDnzyPZmyNyKpqUMvhjIE_0w6oqU4HpN7J5nfBEIAtpPZ_pDkwAdxCQ7JV3zfiUnF7ZQ3q1PnSId315si02ZN9-wRSrMHcHnboQN1Hs4xCAfGyClVyLpCzfa_fAehjt6v1DjgjbzwSjr_LdNmWTvXYBhNO8Jq9Vb7axksrdwksD3pYNMY8cRZxP-LO0V5Sv1_kT_Hf2yLo2iTwB8n8szzGrJ4QQLb5Znu7Sv-M2x52cnIDMiorP3LNpFk.G85FuMSm-8bGumFAStiFQA"; - - @Autowired - MockMvc mvc; - - @Test - public void performWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/").with(bearerToken(this.noScopesToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject!"))); - } - - @Test - public void performWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.messageReadToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message"))); - } - - @Test - public void performWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.noScopesToken))) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - private static class BearerTokenRequestPostProcessor implements RequestPostProcessor { - private String token; - - BearerTokenRequestPostProcessor(String token) { - this.token = token; - } - - @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - request.addHeader("Authorization", "Bearer " + this.token); - return request; - } - } - - private static BearerTokenRequestPostProcessor bearerToken(String token) { - return new BearerTokenRequestPostProcessor(token); - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java deleted file mode 100644 index 62f91d1a1fc..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * @author Rob Winch - */ -public class MockWebServerEnvironmentPostProcessor - implements EnvironmentPostProcessor, DisposableBean { - - private final MockWebServerPropertySource propertySource = new MockWebServerPropertySource(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - environment.getPropertySources().addFirst(this.propertySource); - } - - @Override - public void destroy() throws Exception { - this.propertySource.destroy(); - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java deleted file mode 100644 index 5e9df1d836f..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import java.io.IOException; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.core.env.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -/** - * @author Rob Winch - */ -public class MockWebServerPropertySource extends PropertySource<MockWebServer> implements - DisposableBean { - - private static final MockResponse JWKS_RESPONSE = response( - "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"i7H90yfquGVhXxekdzXkMaxlIg67Q_ofd7iuFHtgeUx-Iye2QjukuULhl774oITYnZIZsh2UHxRYG8nFypcYZfHJMQes_OYFTkTvRroKll5p3wxSkhpARbkEPFMyMJ5WIm3MeNO2ermMhDWVVeI2xQH-tW6w-C6b5d_F6lrIwCnpZwSv6PQ3kef-rcObp_PZANIo232bvpwyC6uW1W2kpjAvYJhQ8NrkG2oO0ynqEJW2UyoCWRdsT2BLZcFMAcxG3Iw9b9__IbvNoUBwr596JYfzrX0atiKyk4Yg8dJ1wBjHFN2fkHTlzn6HDwTJkj4VNDQvKu4P2zhKn1gmWuxhuQ\"}]}", - 200 - ); - - private static final MockResponse NOT_FOUND_RESPONSE = response( - "{ \"message\" : \"This mock authorization server responds to just one request: GET /.well-known/jwks.json.\" }", - 404 - ); - - /** - * Name of the random {@link PropertySource}. - */ - public static final String MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME = "mockwebserver"; - - private static final String NAME = "mockwebserver.url"; - - private static final Log logger = LogFactory.getLog(MockWebServerPropertySource.class); - - private boolean started; - - public MockWebServerPropertySource() { - super(MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME, new MockWebServer()); - } - - @Override - public Object getProperty(String name) { - if (!name.equals(NAME)) { - return null; - } - if (logger.isTraceEnabled()) { - logger.trace("Looking up the url for '" + name + "'"); - } - String url = getUrl(); - return url; - } - - @Override - public void destroy() throws Exception { - getSource().shutdown(); - } - - /** - * Get's the URL (i.e. "http://localhost:123456") - * @return - */ - private String getUrl() { - MockWebServer mockWebServer = getSource(); - if (!this.started) { - intializeMockWebServer(mockWebServer); - } - String url = mockWebServer.url("").url().toExternalForm(); - return url.substring(0, url.length() - 1); - } - - private void intializeMockWebServer(MockWebServer mockWebServer) { - Dispatcher dispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - if ("/.well-known/jwks.json".equals(request.getPath())) { - return JWKS_RESPONSE; - } - - return NOT_FOUND_RESPONSE; - } - }; - - mockWebServer.setDispatcher(dispatcher); - try { - mockWebServer.start(); - this.started = true; - } catch (IOException e) { - throw new RuntimeException("Could not start " + mockWebServer, e); - } - } - - private static MockResponse response(String body, int status) { - return new MockResponse() - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setResponseCode(status) - .setBody(body); - } - -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/package-info.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/package-info.java deleted file mode 100644 index 02260378222..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/org/springframework/boot/env/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -/** - * This provides integration of a {@link okhttp3.mockwebserver.MockWebServer} and the - * {@link org.springframework.core.env.Environment} - * @author Rob Winch - */ -package org.springframework.boot.env; diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerApplication.java deleted file mode 100644 index a0841c00f32..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index f0bcdbe64f5..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal Jwt jwt) { - return String.format("Hello, %s!", jwt.getSubject()); - } - - @GetMapping("/message") - public String message() { - return "secret message"; - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java b/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java deleted file mode 100644 index dfe4135d4bc..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.net.URL; -import java.security.interfaces.RSAPrivateCrtKey; -import java.security.interfaces.RSAPrivateKey; - -import com.nimbusds.jose.EncryptionMethod; -import com.nimbusds.jose.JWEAlgorithm; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.KeyUse; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.source.ImmutableJWKSet; -import com.nimbusds.jose.jwk.source.JWKSource; -import com.nimbusds.jose.jwk.source.RemoteJWKSet; -import com.nimbusds.jose.proc.JWEDecryptionKeySelector; -import com.nimbusds.jose.proc.JWEKeySelector; -import com.nimbusds.jose.proc.JWSKeySelector; -import com.nimbusds.jose.proc.JWSVerificationKeySelector; -import com.nimbusds.jose.proc.SecurityContext; -import com.nimbusds.jose.util.Base64URL; -import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; -import com.nimbusds.jwt.proc.DefaultJWTProcessor; -import com.nimbusds.jwt.proc.JWTProcessor; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Josh Cummings - */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { - - private final JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256; - private final JWEAlgorithm jweAlgorithm = JWEAlgorithm.RSA_OAEP_256; - private final EncryptionMethod encryptionMethod = EncryptionMethod.A256GCM; - - @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") - URL jwkSetUri; - - @Value("${sample.jwe-key-value}") - RSAPrivateKey key; - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/message/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2ResourceServer) -> - oauth2ResourceServer - .jwt(withDefaults()) - ); - // @formatter:on - } - - @Bean - JwtDecoder jwtDecoder() { - return new NimbusJwtDecoder(jwtProcessor()); - } - - private JWTProcessor<SecurityContext> jwtProcessor() { - JWKSource<SecurityContext> jwsJwkSource = new RemoteJWKSet<>(this.jwkSetUri); - JWSKeySelector<SecurityContext> jwsKeySelector = - new JWSVerificationKeySelector<>(this.jwsAlgorithm, jwsJwkSource); - - JWKSource<SecurityContext> jweJwkSource = new ImmutableJWKSet<>(new JWKSet(rsaKey())); - JWEKeySelector<SecurityContext> jweKeySelector = - new JWEDecryptionKeySelector<>(this.jweAlgorithm, this.encryptionMethod, jweJwkSource); - - ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>(); - jwtProcessor.setJWSKeySelector(jwsKeySelector); - jwtProcessor.setJWEKeySelector(jweKeySelector); - - return jwtProcessor; - } - - private RSAKey rsaKey() { - RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey) this.key; - Base64URL n = Base64URL.encode(crtKey.getModulus()); - Base64URL e = Base64URL.encode(crtKey.getPublicExponent()); - return new RSAKey.Builder(n, e) - .privateKey(this.key) - .keyUse(KeyUse.ENCRYPTION) - .build(); - } -} diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/resources/META-INF/spring.factories b/samples/boot/oauth2resourceserver-jwe/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37b447c9702..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.boot.env.MockWebServerEnvironmentPostProcessor diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/resources/application.yml b/samples/boot/oauth2resourceserver-jwe/src/main/resources/application.yml deleted file mode 100644 index 8573fda8f66..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/resources/application.yml +++ /dev/null @@ -1,9 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json - -sample: - jwe-key-value: classpath:simple.priv diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.priv b/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.priv deleted file mode 100644 index 53510079ace..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.priv +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA -iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM -g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK -LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF -oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc -3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn -+jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE -E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek -lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG -mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7 -62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0 -bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA -+Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH -Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA -8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd -I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY -QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d -rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk -HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA -Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN -HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a -FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF -snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H -c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM -TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR -47jndeyIaMTNETEmOnms+as17g== ------END PRIVATE KEY----- \ No newline at end of file diff --git a/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.pub b/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.pub deleted file mode 100644 index 0b2ee7b336c..00000000000 --- a/samples/boot/oauth2resourceserver-jwe/src/main/resources/simple.pub +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd -7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv -c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6 -iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2 -kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o -RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj -KwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/samples/boot/oauth2resourceserver-multitenancy/README.adoc b/samples/boot/oauth2resourceserver-multitenancy/README.adoc deleted file mode 100644 index 97674479bde..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/README.adoc +++ /dev/null @@ -1,155 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a mock Authorization Server, though it can be modified to integrate -with your favorite Authorization Server. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplicationTests` from there. - -=== What is it doing? - -By default, the tests are pointing at a mock Authorization Server instance. - -The tests are configured with a set of hard-coded tokens originally obtained from the mock Authorization Server, -and each makes a query to the Resource Server with their corresponding token. - -The Resource Server subsequently verifies with the Authorization Server and authorizes the request, returning either the -phrase - -```bash -Hello, subject for tenant one! -``` - -where "subject" is the value of the `sub` field in the JWT sent in the `Authorization` header, - -or the phrase -```bash -Hello, subject for tenant two! -``` -where "subject" is the value of the `sub` field in the Introspection response from the Authorization Server. - - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplication` from there. - -=== Authorizing with tenantOne (JWT) - -Once it is up, you can use the following token: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjo0NjgzODA1MTI4fQ.ULEPdHG-MK5GlrTQMhgqcyug2brTIZaJIrahUeq9zaiwUSdW83fJ7W1IDd2Z3n4a25JY2uhEcoV95lMfccHR6y_2DLrNvfta22SumY9PEDF2pido54LXG6edIGgarnUbJdR4rpRe_5oRGVa8gDx8FnuZsNv6StSZHAzw5OsuevSTJ1UbJm4UfX3wiahFOQ2OI6G-r5TB2rQNdiPHuNyzG5yznUqRIZ7-GCoMqHMaC-1epKxiX8gYXRROuUYTtcMNa86wh7OVDmvwVmFioRcR58UWBRoO1XQexTtOQq_t8KYsrPZhb9gkyW8x2bAQF-d0J0EJY8JslaH6n4RBaZISww -``` - -And then make this request: - -```bash -curl -H "tenant: one" -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject for tenant one! -``` - -where `subject` is the value of the `sub` field in the JWT sent in the `Authorization` header. - -Or this: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0Iiwic2NvcGUiOiJtZXNzYWdlOnJlYWQiLCJleHAiOjQ2ODM4MDUxNDF9.h-j6FKRFdnTdmAueTZCdep45e6DPwqM68ZQ8doIJ1exi9YxAlbWzOwId6Bd0L5YmCmp63gGQgsBUBLzwnZQ8kLUgUOBEC3UzSWGRqMskCY9_k9pX0iomX6IfF3N0PaYs0WPC4hO1s8wfZQ-6hKQ4KigFi13G9LMLdH58PRMK0pKEvs3gCbHJuEPw-K5ORlpdnleUTQIwINafU57cmK3KocTeknPAM_L716sCuSYGvDl6xUTXO7oPdrXhS_EhxLP6KxrpI1uD4Ea_5OWTh7S0Wx5LLDfU6wBG1DowN20d374zepOIEkR-Jnmr_QlR44vmRqS5ncrF-1R0EGcPX49U6A - -curl -H "tenant: one" -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message for tenant one -``` - -=== Authorizing with tenantTwo (Opaque token) - -Once it is up, you can use the following token: - -```bash -export TOKEN=00ed5855-1869-47a0-b0c9-0f3ce520aee7 -``` - -And then make this request: - -```bash -curl -H "tenant: two" -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject for tenant two! -``` - -where `subject` is the value of the `sub` field in the Introspection response from the Authorization Server. - -Or this: - -```bash -export TOKEN=b43d1500-c405-4dc9-b9c9-6cfd966c34c9 - -curl -H "tenant: two" -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message for tenant two -``` - -== 2. Testing against other Authorization Servers - -_In order to use this sample, your Authorization Server must support JWTs that either use the "scope" or "scp" attribute._ - -To change the sample to point at your Authorization Server, simply find these properties in the `application.yml`: - -```yaml -tenantOne.jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json -tenantTwo.introspection-uri: ${mockwebserver.url}/introspect -tenantTwo.introspection-client-id: client -tenantTwo.introspection-client-secret: secret -``` - -And change the properties to your Authorization Server's JWK set endpoint and -introspection endpoint, including its client id and secret - -```yaml -tenantOne.jwk-set-uri: https://dev-123456.oktapreview.com/oauth2/default/v1/keys -tenantTwo.introspection-uri: https://dev-123456.oktapreview.com/oauth2/default/v1/introspect -tenantTwo.introspection-client-id: client -tenantTwo.introspection-client-secret: secret -``` - -And then you can run the app the same as before: - -```bash -./gradlew bootRun -``` - -Make sure to obtain valid tokens from your Authorization Server in order to play with the sample Resource Server. -To use the `/` endpoint, any valid token from your Authorization Server will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver-multitenancy/spring-security-samples-boot-oauth2resourceserver-multitenancy.gradle b/samples/boot/oauth2resourceserver-multitenancy/spring-security-samples-boot-oauth2resourceserver-multitenancy.gradle deleted file mode 100644 index 9074842b18a..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/spring-security-samples-boot-oauth2resourceserver-multitenancy.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'com.nimbusds:oauth2-oidc-sdk' - compile 'com.squareup.okhttp3:mockwebserver' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver-multitenancy/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java deleted file mode 100644 index be6d3f559ca..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for {@link OAuth2ResourceServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class OAuth2ResourceServerApplicationITests { - - String tenantOneNoScopesToken = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjo0NjgzODA1MTI4fQ.ULEPdHG-MK5GlrTQMhgqcyug2brTIZaJIrahUeq9zaiwUSdW83fJ7W1IDd2Z3n4a25JY2uhEcoV95lMfccHR6y_2DLrNvfta22SumY9PEDF2pido54LXG6edIGgarnUbJdR4rpRe_5oRGVa8gDx8FnuZsNv6StSZHAzw5OsuevSTJ1UbJm4UfX3wiahFOQ2OI6G-r5TB2rQNdiPHuNyzG5yznUqRIZ7-GCoMqHMaC-1epKxiX8gYXRROuUYTtcMNa86wh7OVDmvwVmFioRcR58UWBRoO1XQexTtOQq_t8KYsrPZhb9gkyW8x2bAQF-d0J0EJY8JslaH6n4RBaZISww"; - String tenantOneMessageReadToken = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0Iiwic2NvcGUiOiJtZXNzYWdlOnJlYWQiLCJleHAiOjQ2ODM4MDUxNDF9.h-j6FKRFdnTdmAueTZCdep45e6DPwqM68ZQ8doIJ1exi9YxAlbWzOwId6Bd0L5YmCmp63gGQgsBUBLzwnZQ8kLUgUOBEC3UzSWGRqMskCY9_k9pX0iomX6IfF3N0PaYs0WPC4hO1s8wfZQ-6hKQ4KigFi13G9LMLdH58PRMK0pKEvs3gCbHJuEPw-K5ORlpdnleUTQIwINafU57cmK3KocTeknPAM_L716sCuSYGvDl6xUTXO7oPdrXhS_EhxLP6KxrpI1uD4Ea_5OWTh7S0Wx5LLDfU6wBG1DowN20d374zepOIEkR-Jnmr_QlR44vmRqS5ncrF-1R0EGcPX49U6A"; - String tenantTwoNoScopesToken = "00ed5855-1869-47a0-b0c9-0f3ce520aee7"; - String tenantTwoMessageReadToken = "b43d1500-c405-4dc9-b9c9-6cfd966c34c9"; - - @Autowired - MockMvc mvc; - - @Test - public void tenantOnePerformWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/") - .header("tenant", "one") - .header("Authorization", "Bearer " + this.tenantOneNoScopesToken)) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject for tenant one!"))); - } - - @Test - public void tenantOnePerformWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message") - .header("tenant", "one") - .header("Authorization", "Bearer " + this.tenantOneMessageReadToken)) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message for tenant one"))); - } - - @Test - public void tenantOnePerformWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message") - .header("tenant", "one") - .header("Authorization", "Bearer " + this.tenantOneNoScopesToken)) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - @Test - public void tenantTwoPerformWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/") - .header("tenant", "two") - .header("Authorization", "Bearer " + this.tenantTwoNoScopesToken)) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject for tenant two!"))); - } - - @Test - public void tenantTwoPerformWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message") - .header("tenant", "two") - .header("Authorization", "Bearer " + this.tenantTwoMessageReadToken)) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message for tenant two"))); - } - - @Test - public void tenantTwoPerformWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message") - .header("tenant", "two") - .header("Authorization", "Bearer " + this.tenantTwoNoScopesToken)) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - @Test(expected = IllegalArgumentException.class) - public void invalidTenantPerformWhenValidBearerTokenThenThrowsException() - throws Exception { - - this.mvc.perform(get("/") - .header("tenant", "three") - .header("Authorization", "Bearer " + this.tenantOneNoScopesToken)); - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java deleted file mode 100644 index f6f664891be..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * @author Rob Winch - */ -public class MockWebServerEnvironmentPostProcessor - implements EnvironmentPostProcessor, DisposableBean { - - private final MockWebServerPropertySource propertySource = new MockWebServerPropertySource(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - environment.getPropertySources().addFirst(this.propertySource); - } - - @Override - public void destroy() throws Exception { - this.propertySource.destroy(); - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java deleted file mode 100644 index 9e3f58e628d..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import java.io.IOException; -import java.util.Base64; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import okio.Buffer; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.core.env.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -/** - * @author Rob Winch - */ -public class MockWebServerPropertySource extends PropertySource<MockWebServer> implements - DisposableBean { - - // introspection endpoint - - private static final MockResponse NO_SCOPES_RESPONSE = response( - "{\n" + - " \"active\": true,\n" + - " \"sub\": \"subject\"\n" + - " }", - 200 - ); - - private static final MockResponse MESSASGE_READ_SCOPE_RESPONSE = response( - "{\n" + - " \"active\": true,\n" + - " \"scope\" : \"message:read\"," + - " \"sub\": \"subject\"\n" + - " }", - 200 - ); - - private static final MockResponse INACTIVE_RESPONSE = response( - "{\n" + - " \"active\": false,\n" + - " }", - 200 - ); - - private static final MockResponse BAD_REQUEST_RESPONSE = response( - "{ \"message\" : \"This mock authorization server requires a username and password of " + - "client/secret and a POST body of token=${token}\" }", - 400 - ); - - private static final MockResponse NOT_FOUND_RESPONSE = response( - "{ \"message\" : \"This mock authorization server responds to just two requests: POST /introspect" + - " and GET /.well-known/jwks.json.\" }", - 404 - ); - - // jwks endpoint - - private static final MockResponse JWKS_RESPONSE = response( - "{\"keys\":[{\"p\":\"2p-ViY7DE9ZrdWQb544m0Jp7Cv03YCSljqfim9pD4ALhObX0OrAznOiowTjwBky9JGffMwDBVSfJSD9TSU7aH2sbbfi0bZLMdekKAuimudXwUqPDxrrg0BCyvCYgLmKjbVT3zcdylWSog93CNTxGDPzauu-oc0XPNKCXnaDpNvE\",\"kty\":\"RSA\",\"q\":\"sP_QYavrpBvSJ86uoKVGj2AGl78CSsAtpf1ybSY5TwUlorXSdqapRbY69Y271b0aMLzlleUn9ZTBO1dlKV2_dw_lPADHVia8z3pxL-8sUhIXLsgj4acchMk4c9YX-sFh07xENnyZ-_TXm3llPLuL67HUfBC2eKe800TmCYVWc9U\",\"d\":\"bn1nFxCQT4KLTHqo8mo9HvHD0cRNRNdWcKNnnEQkCF6tKbt-ILRyQGP8O40axLd7CoNVG9c9p_-g4-2kwCtLJNv_STLtwfpCY7VN5o6-ZIpfTjiW6duoPrLWq64Hm_4LOBQTiZfUPcLhsuJRHbWqakj-kV_YbUyC2Ocf_dd8IAQcSrAU2SCcDebhDCWwRUFvaa9V5eq0851S9goaA-AJz-JXyePH6ZFr8JxmWkWxYZ5kdcMD-sm9ZbxE0CaEk32l4fE4hR-L8x2dDtjWA-ahKCZ091z-gV3HWtR2JOjvxoNRjxUo3UxaGiFJHWNIl0EYUJZu1Cb-5wIlEI7wPx5mwQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"qS0OK48M2CIAA6_4Wdw4EbCaAfcTLf5Oy9t5BOF_PFUKqoSpZ6JsT5H0a_4zkjt-oI969v78OTlvBKbmEyKO-KeytzHBAA5CsLmVcz0THrMSg6oXZqu66MPnvWoZN9FEN5TklPOvBFm8Bg1QZ3k-YMVaM--DLvhaYR95_mqaz50\",\"dp\":\"Too2NozLGD1XrXyhabZvy1E0EuaVFj0UHQPDLSpkZ_2g3BK6Art6T0xmE8RYtmqrKIEIdlI3IliAvyvAx_1D7zWTTRaj-xlZyqJFrnXWL7zj8UxT8PkB-r2E-ILZ3NAi1gxIWezlBTZ8M6NfObDFmbTc_3tJkN_raISo8z_ziIE\",\"dq\":\"U0yhSkY5yOsa9YcMoigGVBWSJLpNHtbg5NypjHrPv8OhWbkOSq7WvSstBkFk5AtyFvvfZLMLIkWWxxGzV0t6f1MoxBtttLrYYyCxwihiiGFhLbAdSuZ1wnxcqA9bC7UVECvrQmVTpsMs8UupfHKbQBpZ8OWAqrnuYNNtG4_4Bt0\",\"n\":\"lygtuZj0lJjqOqIWocF8Bb583QDdq-aaFg8PesOp2-EDda6GqCpL-_NZVOflNGX7XIgjsWHcPsQHsV9gWuOzSJ0iEuWvtQ6eGBP5M6m7pccLNZfwUse8Cb4Ngx3XiTlyuqM7pv0LPyppZusfEHVEdeelou7Dy9k0OQ_nJTI3b2E1WBoHC58CJ453lo4gcBm1efURN3LIVc1V9NQY_ESBKVdwqYyoJPEanURLVGRd6cQKn6YrCbbIRHjqAyqOE-z3KmgDJnPriljfR5XhSGyM9eqD9Xpy6zu_MAeMJJfSArp857zLPk-Wf5VP9STAcjyfdBIybMKnwBYr2qHMT675hQ\"}]}", - 200 - ); - - /** - * Name of the random {@link PropertySource}. - */ - public static final String MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME = "mockwebserver"; - - private static final String NAME = "mockwebserver.url"; - - private static final Log logger = LogFactory.getLog(MockWebServerPropertySource.class); - - private boolean started; - - public MockWebServerPropertySource() { - super(MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME, new MockWebServer()); - } - - @Override - public Object getProperty(String name) { - if (!name.equals(NAME)) { - return null; - } - if (logger.isTraceEnabled()) { - logger.trace("Looking up the url for '" + name + "'"); - } - String url = getUrl(); - return url; - } - - @Override - public void destroy() throws Exception { - getSource().shutdown(); - } - - /** - * Get's the URL (i.e. "http://localhost:123456") - * @return - */ - private String getUrl() { - MockWebServer mockWebServer = getSource(); - if (!this.started) { - intializeMockWebServer(mockWebServer); - } - String url = mockWebServer.url("").url().toExternalForm(); - return url.substring(0, url.length() - 1); - } - - private void intializeMockWebServer(MockWebServer mockWebServer) { - Dispatcher dispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - return doDispatch(request); - } - }; - - mockWebServer.setDispatcher(dispatcher); - try { - mockWebServer.start(); - this.started = true; - } catch (IOException e) { - throw new RuntimeException("Could not start " + mockWebServer, e); - } - } - - private MockResponse doDispatch(RecordedRequest request) { - if ("/.well-known/jwks.json".equals(request.getPath())) { - return JWKS_RESPONSE; - } - - if ("/introspect".equals(request.getPath())) { - return Optional.ofNullable(request.getHeader(HttpHeaders.AUTHORIZATION)) - .filter((authorization) -> isAuthorized(authorization, "client", "secret")) - .map((authorization) -> parseBody(request.getBody())) - .map((parameters) -> parameters.get("token")) - .map((token) -> { - if ("00ed5855-1869-47a0-b0c9-0f3ce520aee7".equals(token)) { - return NO_SCOPES_RESPONSE; - } else if ("b43d1500-c405-4dc9-b9c9-6cfd966c34c9".equals(token)) { - return MESSASGE_READ_SCOPE_RESPONSE; - } else { - return INACTIVE_RESPONSE; - } - }) - .orElse(BAD_REQUEST_RESPONSE); - } - - return NOT_FOUND_RESPONSE; - } - - private boolean isAuthorized(String authorization, String username, String password) { - String[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(":"); - return username.equals(values[0]) && password.equals(values[1]); - } - - private Map<String, Object> parseBody(Buffer body) { - return Stream.of(body.readUtf8().split("&")) - .map((parameter) -> parameter.split("=")) - .collect(Collectors.toMap((parts) -> parts[0], (parts) -> parts[1])); - } - - private static MockResponse response(String body, int status) { - return new MockResponse() - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setResponseCode(status) - .setBody(body); - } - -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/package-info.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/package-info.java deleted file mode 100644 index 4db05821da1..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/org/springframework/boot/env/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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. - */ - -/** - * This provides integration of a {@link okhttp3.mockwebserver.MockWebServer} and the - * {@link org.springframework.core.env.Environment} - * @author Rob Winch - */ -package org.springframework.boot.env; diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerApplication.java deleted file mode 100644 index d5c70cc70da..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index 1dce6e718a5..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal token, @RequestHeader("tenant") String tenant) { - String subject = token.getAttribute("sub"); - return String.format("Hello, %s for tenant %s!", subject, tenant); - } - - @GetMapping("/message") - public String message(@RequestHeader("tenant") String tenant) { - return String.format("secret message for tenant %s", tenant); - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java deleted file mode 100644 index 75bf3865b92..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationManagerResolver; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * @author Josh Cummings - */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Autowired - AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver; - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authz) -> authz - .antMatchers("/message/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .authenticationManagerResolver(this.authenticationManagerResolver) - ); - // @formatter:on - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/TenantAuthenticationManagerResolver.java b/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/TenantAuthenticationManagerResolver.java deleted file mode 100644 index 939bfc0b8b5..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/java/sample/TenantAuthenticationManagerResolver.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationManagerResolver; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; -import org.springframework.security.oauth2.server.resource.authentication.JwtBearerTokenAuthenticationConverter; -import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider; -import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; -import org.springframework.stereotype.Component; - -@Component -public class TenantAuthenticationManagerResolver - implements AuthenticationManagerResolver<HttpServletRequest> { - - private AuthenticationManager jwt; - private AuthenticationManager opaqueToken; - - public TenantAuthenticationManagerResolver( - JwtDecoder jwtDecoder, OpaqueTokenIntrospector opaqueTokenIntrospector) { - - JwtAuthenticationProvider jwtAuthenticationProvider = new JwtAuthenticationProvider(jwtDecoder); - jwtAuthenticationProvider.setJwtAuthenticationConverter(new JwtBearerTokenAuthenticationConverter()); - this.jwt = new ProviderManager(jwtAuthenticationProvider); - this.opaqueToken = new ProviderManager(new OpaqueTokenAuthenticationProvider(opaqueTokenIntrospector)); - } - - @Override - public AuthenticationManager resolve(HttpServletRequest request) { - String tenant = request.getHeader("tenant"); - if ("one".equals(tenant)) { - return this.jwt; - } - if ("two".equals(tenant)) { - return this.opaqueToken; - } - throw new IllegalArgumentException("unknown tenant"); - } -} diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/META-INF/spring.factories b/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37b447c9702..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.boot.env.MockWebServerEnvironmentPostProcessor diff --git a/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/application.yml b/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/application.yml deleted file mode 100644 index 6447ad0d940..00000000000 --- a/samples/boot/oauth2resourceserver-multitenancy/src/main/resources/application.yml +++ /dev/null @@ -1,10 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json - opaquetoken: - introspection-uri: ${mockwebserver.url}/introspect - client-id: client - client-secret: secret diff --git a/samples/boot/oauth2resourceserver-opaque/README.adoc b/samples/boot/oauth2resourceserver-opaque/README.adoc deleted file mode 100644 index fc6add9a1f3..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/README.adoc +++ /dev/null @@ -1,114 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a mock Authorization Server, though it can be modified to integrate -with your favorite Authorization Server. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Opaque Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplicationTests` from there. - -=== What is it doing? - -By default, the tests are pointing at a mock Authorization Server instance. - -The tests are configured with a set of hard-coded tokens originally obtained from the mock Authorization Server, -and each makes a query to the Resource Server with their corresponding token. - -The Resource Server subsquently verifies with the Authorization Server and authorizes the request, returning the phrase - -```bash -Hello, subject! -``` - -where "subject" is the value of the `sub` field in the JWT returned by the Authorization Server. - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplication` from there. - -Once it is up, you can use the following token: - -```bash -export TOKEN=00ed5855-1869-47a0-b0c9-0f3ce520aee7 -``` - -And then make this request: - -```bash -curl -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject! -``` - -where `subject` is the value of the `sub` field in the JWT returned by the Authorization Server. - -Or this: - -```bash -export TOKEN=b43d1500-c405-4dc9-b9c9-6cfd966c34c9 - -curl -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message -``` - -== 2. Testing against other Authorization Servers - -_In order to use this sample, your Authorization Server must support Opaque Tokens and the Introspection Endpoint. - -To change the sample to point at your Authorization Server, simply find this property in the `application.yml`: - -```yaml -spring: - security: - oauth2: - resourceserver: - opaque: - introspection-uri: ${mockwebserver.url}/introspect - introspection-client-id: client - introspection-client-secret: secret -``` - -And change the property to your Authorization Server's Introspection endpoint, including its client id and secret: - -```yaml -spring: - security: - oauth2: - resourceserver: - opaque: - introspection-uri: ${mockwebserver.url}/introspect -``` - -And then you can run the app the same as before: - -```bash -./gradlew bootRun -``` - -Make sure to obtain valid tokens from your Authorization Server in order to play with the sample Resource Server. -To use the `/` endpoint, any valid token from your Authorization Server will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver-opaque/spring-security-samples-boot-oauth2resourceserver-opaque.gradle b/samples/boot/oauth2resourceserver-opaque/spring-security-samples-boot-oauth2resourceserver-opaque.gradle deleted file mode 100644 index 9074842b18a..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/spring-security-samples-boot-oauth2resourceserver-opaque.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'com.nimbusds:oauth2-oidc-sdk' - compile 'com.squareup.okhttp3:mockwebserver' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver-opaque/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java deleted file mode 100644 index 10bd3fc7ec1..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.RequestPostProcessor; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for {@link OAuth2ResourceServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -public class OAuth2ResourceServerApplicationITests { - - String noScopesToken = "00ed5855-1869-47a0-b0c9-0f3ce520aee7"; - String messageReadToken = "b43d1500-c405-4dc9-b9c9-6cfd966c34c9"; - - @Autowired - MockMvc mvc; - - @Test - public void performWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/").with(bearerToken(this.noScopesToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject!"))); - } - - @Test - public void performWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.messageReadToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message"))); - } - - @Test - public void performWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.noScopesToken))) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - private static class BearerTokenRequestPostProcessor implements RequestPostProcessor { - private String token; - - BearerTokenRequestPostProcessor(String token) { - this.token = token; - } - - @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - request.addHeader("Authorization", "Bearer " + this.token); - return request; - } - } - - private static BearerTokenRequestPostProcessor bearerToken(String token) { - return new BearerTokenRequestPostProcessor(token); - } -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java deleted file mode 100644 index f6f664891be..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * @author Rob Winch - */ -public class MockWebServerEnvironmentPostProcessor - implements EnvironmentPostProcessor, DisposableBean { - - private final MockWebServerPropertySource propertySource = new MockWebServerPropertySource(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - environment.getPropertySources().addFirst(this.propertySource); - } - - @Override - public void destroy() throws Exception { - this.propertySource.destroy(); - } -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java deleted file mode 100644 index 0bc6b575586..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import java.io.IOException; -import java.util.Base64; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import okio.Buffer; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.core.env.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -/** - * @author Rob Winch - */ -public class MockWebServerPropertySource extends PropertySource<MockWebServer> implements - DisposableBean { - - private static final MockResponse NO_SCOPES_RESPONSE = response( - "{\n" + - " \"active\": true,\n" + - " \"sub\": \"subject\"\n" + - " }", - 200 - ); - - private static final MockResponse MESSASGE_READ_SCOPE_RESPONSE = response( - "{\n" + - " \"active\": true,\n" + - " \"scope\" : \"message:read\"," + - " \"sub\": \"subject\"\n" + - " }", - 200 - ); - - private static final MockResponse INACTIVE_RESPONSE = response( - "{\n" + - " \"active\": false,\n" + - " }", - 200 - ); - - private static final MockResponse BAD_REQUEST_RESPONSE = response( - "{ \"message\" : \"This mock authorization server requires a username and password of " + - "client/secret and a POST body of token=${token}\" }", - 400 - ); - - private static final MockResponse NOT_FOUND_RESPONSE = response( - "{ \"message\" : \"This mock authorization server responds to just one request: POST /introspect.\" }", - 404 - ); - - /** - * Name of the random {@link PropertySource}. - */ - public static final String MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME = "mockwebserver"; - - private static final String NAME = "mockwebserver.url"; - - private static final Log logger = LogFactory.getLog(MockWebServerPropertySource.class); - - private boolean started; - - public MockWebServerPropertySource() { - super(MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME, new MockWebServer()); - } - - @Override - public Object getProperty(String name) { - if (!name.equals(NAME)) { - return null; - } - if (logger.isTraceEnabled()) { - logger.trace("Looking up the url for '" + name + "'"); - } - String url = getUrl(); - return url; - } - - @Override - public void destroy() throws Exception { - getSource().shutdown(); - } - - /** - * Get's the URL (e.g. "http://localhost:123456") - * @return - */ - private String getUrl() { - MockWebServer mockWebServer = getSource(); - if (!this.started) { - initializeMockWebServer(mockWebServer); - } - String url = mockWebServer.url("").url().toExternalForm(); - return url.substring(0, url.length() - 1); - } - - private void initializeMockWebServer(MockWebServer mockWebServer) { - Dispatcher dispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - return doDispatch(request); - } - }; - - mockWebServer.setDispatcher(dispatcher); - try { - mockWebServer.start(); - this.started = true; - } catch (IOException e) { - throw new RuntimeException("Could not start " + mockWebServer, e); - } - } - - private MockResponse doDispatch(RecordedRequest request) { - if ("/introspect".equals(request.getPath())) { - return Optional.ofNullable(request.getHeader(HttpHeaders.AUTHORIZATION)) - .filter((authorization) -> isAuthorized(authorization, "client", "secret")) - .map((authorization) -> parseBody(request.getBody())) - .map((parameters) -> parameters.get("token")) - .map((token) -> { - if ("00ed5855-1869-47a0-b0c9-0f3ce520aee7".equals(token)) { - return NO_SCOPES_RESPONSE; - } else if ("b43d1500-c405-4dc9-b9c9-6cfd966c34c9".equals(token)) { - return MESSASGE_READ_SCOPE_RESPONSE; - } else { - return INACTIVE_RESPONSE; - } - }) - .orElse(BAD_REQUEST_RESPONSE); - } - - return NOT_FOUND_RESPONSE; - } - - private boolean isAuthorized(String authorization, String username, String password) { - String[] values = new String(Base64.getDecoder().decode(authorization.substring(6))).split(":"); - return username.equals(values[0]) && password.equals(values[1]); - } - - private Map<String, Object> parseBody(Buffer body) { - return Stream.of(body.readUtf8().split("&")) - .map((parameter) -> parameter.split("=")) - .collect(Collectors.toMap((parts) -> parts[0], (parts) -> parts[1])); - } - - private static MockResponse response(String body, int status) { - return new MockResponse() - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setResponseCode(status) - .setBody(body); - } - -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/package-info.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/package-info.java deleted file mode 100644 index 22b7245ebba..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/org/springframework/boot/env/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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. - */ - -/** - * This provides integration of a {@link okhttp3.mockwebserver.MockWebServer} and the - * {@link org.springframework.core.env.Environment} - * - * @author Rob Winch - */ -package org.springframework.boot.env; diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerApplication.java deleted file mode 100644 index d5c70cc70da..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index 857d29be66f..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal(expression="subject") String subject) { - return String.format("Hello, %s!", subject); - } - - @GetMapping("/message") - public String message() { - return "secret message"; - } - - @PostMapping("/message") - public String createMessage(@RequestBody String message) { - return String.format("Message was created. Content: %s", message); - } -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java b/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java deleted file mode 100644 index 6fc3444c731..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -/** - * @author Josh Cummings - */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Value("${spring.security.oauth2.resourceserver.opaque.introspection-uri}") String introspectionUri; - @Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-id}") String clientId; - @Value("${spring.security.oauth2.resourceserver.opaque.introspection-client-secret}") String clientSecret; - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") - .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2ResourceServer) -> - oauth2ResourceServer - .opaqueToken((opaqueToken) -> - opaqueToken - .introspectionUri(this.introspectionUri) - .introspectionClientCredentials(this.clientId, this.clientSecret) - ) - ); - // @formatter:on - } -} diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/resources/META-INF/spring.factories b/samples/boot/oauth2resourceserver-opaque/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37b447c9702..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.boot.env.MockWebServerEnvironmentPostProcessor diff --git a/samples/boot/oauth2resourceserver-opaque/src/main/resources/application.yml b/samples/boot/oauth2resourceserver-opaque/src/main/resources/application.yml deleted file mode 100644 index a7dcfead944..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/main/resources/application.yml +++ /dev/null @@ -1,8 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - opaque: - introspection-uri: ${mockwebserver.url}/introspect - introspection-client-id: client - introspection-client-secret: secret diff --git a/samples/boot/oauth2resourceserver-opaque/src/test/java/sample/OAuth2ResourceServerControllerTests.java b/samples/boot/oauth2resourceserver-opaque/src/test/java/sample/OAuth2ResourceServerControllerTests.java deleted file mode 100644 index ee3318f2469..00000000000 --- a/samples/boot/oauth2resourceserver-opaque/src/test/java/sample/OAuth2ResourceServerControllerTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.opaqueToken; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * @author Josh Cummings - * @since 5.3 - */ -@RunWith(SpringRunner.class) -@WebMvcTest(OAuth2ResourceServerController.class) -public class OAuth2ResourceServerControllerTests { - - @Autowired - MockMvc mvc; - - @Test - public void indexGreetsAuthenticatedUser() throws Exception { - this.mvc.perform(get("/").with(opaqueToken().attributes((a) -> a.put("sub", "ch4mpy")))) - .andExpect(content().string(is("Hello, ch4mpy!"))); - } - - @Test - public void messageCanBeReadWithScopeMessageReadAuthority() throws Exception { - this.mvc.perform(get("/message").with(opaqueToken().attributes((a) -> a.put("scope", "message:read")))) - .andExpect(content().string(is("secret message"))); - - this.mvc.perform(get("/message") - .with(jwt().authorities(new SimpleGrantedAuthority(("SCOPE_message:read"))))) - .andExpect(content().string(is("secret message"))); - } - - @Test - public void messageCanNotBeReadWithoutScopeMessageReadAuthority() throws Exception { - this.mvc.perform(get("/message").with(opaqueToken())) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanNotBeCreatedWithoutAnyScope() throws Exception { - this.mvc.perform(post("/message") - .content("Hello message") - .with(opaqueToken())) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanNotBeCreatedWithScopeMessageReadAuthority() throws Exception { - this.mvc.perform(post("/message") - .content("Hello message") - .with(opaqueToken().authorities(new SimpleGrantedAuthority("SCOPE_message:read")))) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanBeCreatedWithScopeMessageWriteAuthority() throws Exception { - this.mvc.perform(post("/message") - .content("Hello message") - .with(opaqueToken().authorities(new SimpleGrantedAuthority("SCOPE_message:write")))) - .andExpect(status().isOk()) - .andExpect(content().string(is("Message was created. Content: Hello message"))); - } -} diff --git a/samples/boot/oauth2resourceserver-static/README.adoc b/samples/boot/oauth2resourceserver-static/README.adoc deleted file mode 100644 index ecc530d7242..00000000000 --- a/samples/boot/oauth2resourceserver-static/README.adoc +++ /dev/null @@ -1,82 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a pre-configured key. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplicationITests` from there. - -=== What is it doing? - -By default, the application is configured with an RSA public key that is available in the sample. - -The tests are configured with a set of hard-coded tokens that are signed with the corresponding RSA private key. -Each test makes a query to the Resource Server with their corresponding token. - -The Resource Server subsequently verifies the token against the public key and authorizes the request, returning the phrase - -```bash -Hello, subject! -``` - -where "subject" is the value of the `sub` field in the token. - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplication` from there. - -Once it is up, you can use the following token: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaWF0IjoxNTE2MjM5MDIyfQ.eB2c9xtg5wcCZxZ-o-sH4Mx1JGkqAZwH4_WS0UcDbj_nen0NPBj6CqOEPhr_LZDagb4mM6HoAPJywWWG8b_Ylnn5r2gWDzib2mb0kxIuAjnvVBrpzusw4ItTVvP_srv2DrwcisKYiKqU5X_3ka7MSVvKtswdLY3RXeCJ_S2W9go -``` - -And then make this request: - -```bash -curl -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject! -``` - -where `subject` is the value of the `sub` field in the token. - -Or this: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaWF0IjoxNTE2MjM5MDIyLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCJ9.bsRCpUEaiWnzX4OqNxTBqwUD4vxxtPp-CHKTw7XcrglrvZ2lvYXaiZZbCp-hcPhuzMEzEAFuH6s4GZZOWVIX-wT47GdTz9cfA-Z4QPjS2RxePKphFXgBI3jHEpQo94Qya2fJdV4LvgBmA1uM_RTnYY1UbmeYuHKnXrZoGyV8QQQ - -curl -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message -``` - -== 3. Testing with Other Tokens - -You can create your own tokens. Simply edit the public key in `OAuth2ResourceServerSecurityConfiguration` to match the private key you use. - -To use the `/` endpoint, any valid token will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver-static/spring-security-samples-boot-oauth2resourceserver-static.gradle b/samples/boot/oauth2resourceserver-static/spring-security-samples-boot-oauth2resourceserver-static.gradle deleted file mode 100644 index faba0d5f463..00000000000 --- a/samples/boot/oauth2resourceserver-static/spring-security-samples-boot-oauth2resourceserver-static.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-web' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2resourceserver-static/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver-static/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java deleted file mode 100644 index 851d3bd242a..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.RequestPostProcessor; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for {@link OAuth2ResourceServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -public class OAuth2ResourceServerApplicationITests { - - String noScopesToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaWF0IjoxNTE2MjM5MDIyfQ.eB2c9xtg5wcCZxZ-o-sH4Mx1JGkqAZwH4_WS0UcDbj_nen0NPBj6CqOEPhr_LZDagb4mM6HoAPJywWWG8b_Ylnn5r2gWDzib2mb0kxIuAjnvVBrpzusw4ItTVvP_srv2DrwcisKYiKqU5X_3ka7MSVvKtswdLY3RXeCJ_S2W9go"; - String messageReadToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiaWF0IjoxNTE2MjM5MDIyLCJzY29wZSI6Im1lc3NhZ2U6cmVhZCJ9.bsRCpUEaiWnzX4OqNxTBqwUD4vxxtPp-CHKTw7XcrglrvZ2lvYXaiZZbCp-hcPhuzMEzEAFuH6s4GZZOWVIX-wT47GdTz9cfA-Z4QPjS2RxePKphFXgBI3jHEpQo94Qya2fJdV4LvgBmA1uM_RTnYY1UbmeYuHKnXrZoGyV8QQQ"; - - @Autowired - MockMvc mvc; - - @Test - public void performWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/").with(bearerToken(this.noScopesToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject!"))); - } - - @Test - public void performWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.messageReadToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message"))); - } - - @Test - public void performWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.noScopesToken))) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - private static class BearerTokenRequestPostProcessor implements RequestPostProcessor { - private String token; - - BearerTokenRequestPostProcessor(String token) { - this.token = token; - } - - @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - request.addHeader("Authorization", "Bearer " + this.token); - return request; - } - } - - private static BearerTokenRequestPostProcessor bearerToken(String token) { - return new BearerTokenRequestPostProcessor(token); - } -} diff --git a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerApplication.java deleted file mode 100644 index a0841c00f32..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index f0bcdbe64f5..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal Jwt jwt) { - return String.format("Hello, %s!", jwt.getSubject()); - } - - @GetMapping("/message") - public String message() { - return "secret message"; - } -} diff --git a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java b/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java deleted file mode 100644 index a57bed2d51b..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.security.interfaces.RSAPublicKey; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; - -/** - * @author Josh Cummings - */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Value("${spring.security.oauth2.resourceserver.jwt.key-value}") - RSAPublicKey key; - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/message/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2ResourceServer) -> - oauth2ResourceServer - .jwt((jwt) -> - jwt.decoder(jwtDecoder()) - ) - ); - // @formatter:on - } - - @Bean - JwtDecoder jwtDecoder() { - return NimbusJwtDecoder.withPublicKey(this.key).build(); - } -} diff --git a/samples/boot/oauth2resourceserver-static/src/main/resources/application.yml b/samples/boot/oauth2resourceserver-static/src/main/resources/application.yml deleted file mode 100644 index 123d342ead4..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - jwt: - key-value: classpath:simple.pub diff --git a/samples/boot/oauth2resourceserver-static/src/main/resources/simple.pub b/samples/boot/oauth2resourceserver-static/src/main/resources/simple.pub deleted file mode 100644 index a25c08779e4..00000000000 --- a/samples/boot/oauth2resourceserver-static/src/main/resources/simple.pub +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd -UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs -HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D -o2kQ+X5xK9cipRgEKwIDAQAB ------END PUBLIC KEY----- - diff --git a/samples/boot/oauth2resourceserver-webflux/README.adoc b/samples/boot/oauth2resourceserver-webflux/README.adoc deleted file mode 100644 index 75c3fd7b419..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/README.adoc +++ /dev/null @@ -1,112 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a mock Authorization Server, though it can be modified to integrate -with your favorite Authorization Server. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `ServerOAuth2ResourceServerApplicationTests` from there. - -=== What is it doing? - -By default, the tests are pointing at a mock Authorization Server instance. - -The tests are configured with a set of hard-coded tokens originally obtained from the mock Authorization Server, -and each makes a query to the Resource Server with their corresponding token. - -The Resource Server subsquently verifies with the Authorization Server and authorizes the request, returning the phrase - -```bash -Hello, subject! -``` - -where "subject" is the value of the `sub` field in the JWT returned by the Authorization Server. - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `ServerOAuth2ResourceServerApplication` from there. - -Once it is up, you can use the following token: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjo0NjgzODA1MTI4fQ.ULEPdHG-MK5GlrTQMhgqcyug2brTIZaJIrahUeq9zaiwUSdW83fJ7W1IDd2Z3n4a25JY2uhEcoV95lMfccHR6y_2DLrNvfta22SumY9PEDF2pido54LXG6edIGgarnUbJdR4rpRe_5oRGVa8gDx8FnuZsNv6StSZHAzw5OsuevSTJ1UbJm4UfX3wiahFOQ2OI6G-r5TB2rQNdiPHuNyzG5yznUqRIZ7-GCoMqHMaC-1epKxiX8gYXRROuUYTtcMNa86wh7OVDmvwVmFioRcR58UWBRoO1XQexTtOQq_t8KYsrPZhb9gkyW8x2bAQF-d0J0EJY8JslaH6n4RBaZISww -``` - -And then make this request: - -```bash -curl -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject! -``` - -where `subject` is the value of the `sub` field in the JWT returned by the Authorization Server. - -Or this: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0Iiwic2NvcGUiOiJtZXNzYWdlOnJlYWQiLCJleHAiOjQ2ODM4MDUxNDF9.h-j6FKRFdnTdmAueTZCdep45e6DPwqM68ZQ8doIJ1exi9YxAlbWzOwId6Bd0L5YmCmp63gGQgsBUBLzwnZQ8kLUgUOBEC3UzSWGRqMskCY9_k9pX0iomX6IfF3N0PaYs0WPC4hO1s8wfZQ-6hKQ4KigFi13G9LMLdH58PRMK0pKEvs3gCbHJuEPw-K5ORlpdnleUTQIwINafU57cmK3KocTeknPAM_L716sCuSYGvDl6xUTXO7oPdrXhS_EhxLP6KxrpI1uD4Ea_5OWTh7S0Wx5LLDfU6wBG1DowN20d374zepOIEkR-Jnmr_QlR44vmRqS5ncrF-1R0EGcPX49U6A - -curl -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message -``` - -== 2. Testing against other Authorization Servers - -_In order to use this sample, your Authorization Server must support JWTs that either use the "scope" or "scp" attribute._ - -To change the sample to point at your Authorization Server, simply find this property in the `application.yml`: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json -``` - -And change the property to your Authorization Server's JWK set endpoint: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: https://dev-123456.oktapreview.com/oauth2/default/v1/keys -``` - -And then you can run the app the same as before: - -```bash -./gradlew bootRun -``` - -Make sure to obtain valid tokens from your Authorization Server in order to play with the sample Resource Server. -To use the `/` endpoint, any valid token from your Authorization Server will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver-webflux/spring-security-samples-boot-oauth2resourceserver-webflux.gradle b/samples/boot/oauth2resourceserver-webflux/spring-security-samples-boot-oauth2resourceserver-webflux.gradle deleted file mode 100644 index 0d92aef8de6..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/spring-security-samples-boot-oauth2resourceserver-webflux.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-webflux' - compile 'com.squareup.okhttp3:mockwebserver' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' - - testCompile 'com.squareup.okhttp3:mockwebserver' -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/integration-test/java/sample/ServerOAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver-webflux/src/integration-test/java/sample/ServerOAuth2ResourceServerApplicationITests.java deleted file mode 100644 index 9cbdcf2b36b..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/integration-test/java/sample/ServerOAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -import java.util.function.Consumer; - -import static org.hamcrest.Matchers.containsString; - -/** - * @author Rob Winch - * @since 5.1 - */ -@SpringBootTest -@AutoConfigureWebTestClient -@RunWith(SpringJUnit4ClassRunner.class) -public class ServerOAuth2ResourceServerApplicationITests { - - Consumer<HttpHeaders> noScopesToken = (http) -> http.setBearerAuth("eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjo0NjgzODA1MTI4fQ.ULEPdHG-MK5GlrTQMhgqcyug2brTIZaJIrahUeq9zaiwUSdW83fJ7W1IDd2Z3n4a25JY2uhEcoV95lMfccHR6y_2DLrNvfta22SumY9PEDF2pido54LXG6edIGgarnUbJdR4rpRe_5oRGVa8gDx8FnuZsNv6StSZHAzw5OsuevSTJ1UbJm4UfX3wiahFOQ2OI6G-r5TB2rQNdiPHuNyzG5yznUqRIZ7-GCoMqHMaC-1epKxiX8gYXRROuUYTtcMNa86wh7OVDmvwVmFioRcR58UWBRoO1XQexTtOQq_t8KYsrPZhb9gkyW8x2bAQF-d0J0EJY8JslaH6n4RBaZISww"); - Consumer<HttpHeaders> messageReadToken = (http) -> http.setBearerAuth("eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0Iiwic2NvcGUiOiJtZXNzYWdlOnJlYWQiLCJleHAiOjQ2ODM4MDUxNDF9.h-j6FKRFdnTdmAueTZCdep45e6DPwqM68ZQ8doIJ1exi9YxAlbWzOwId6Bd0L5YmCmp63gGQgsBUBLzwnZQ8kLUgUOBEC3UzSWGRqMskCY9_k9pX0iomX6IfF3N0PaYs0WPC4hO1s8wfZQ-6hKQ4KigFi13G9LMLdH58PRMK0pKEvs3gCbHJuEPw-K5ORlpdnleUTQIwINafU57cmK3KocTeknPAM_L716sCuSYGvDl6xUTXO7oPdrXhS_EhxLP6KxrpI1uD4Ea_5OWTh7S0Wx5LLDfU6wBG1DowN20d374zepOIEkR-Jnmr_QlR44vmRqS5ncrF-1R0EGcPX49U6A"); - - @Autowired - private WebTestClient rest; - - - @Test - public void getWhenValidBearerTokenThenAllows() { - - this.rest.get().uri("/") - .headers(this.noScopesToken) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Hello, subject!"); - } - - @Test - public void getWhenValidBearerTokenThenScopedRequestsAlsoWork() { - - this.rest.get().uri("/message") - .headers(this.messageReadToken) - .exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("secret message"); - } - - @Test - public void getWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() { - - this.rest.get().uri("/message") - .headers(this.noScopesToken) - .exchange() - .expectStatus().isForbidden() - .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, containsString("Bearer error=\"insufficient_scope\"")); - } -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java deleted file mode 100644 index 62f91d1a1fc..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * @author Rob Winch - */ -public class MockWebServerEnvironmentPostProcessor - implements EnvironmentPostProcessor, DisposableBean { - - private final MockWebServerPropertySource propertySource = new MockWebServerPropertySource(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - environment.getPropertySources().addFirst(this.propertySource); - } - - @Override - public void destroy() throws Exception { - this.propertySource.destroy(); - } -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java deleted file mode 100644 index 54dc2ca1430..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.core.env.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -import java.io.IOException; - -/** - * @author Rob Winch - */ -public class MockWebServerPropertySource extends PropertySource<MockWebServer> implements - DisposableBean { - - private static final MockResponse JWKS_RESPONSE = response( - "{\"keys\":[{\"p\":\"2p-ViY7DE9ZrdWQb544m0Jp7Cv03YCSljqfim9pD4ALhObX0OrAznOiowTjwBky9JGffMwDBVSfJSD9TSU7aH2sbbfi0bZLMdekKAuimudXwUqPDxrrg0BCyvCYgLmKjbVT3zcdylWSog93CNTxGDPzauu-oc0XPNKCXnaDpNvE\",\"kty\":\"RSA\",\"q\":\"sP_QYavrpBvSJ86uoKVGj2AGl78CSsAtpf1ybSY5TwUlorXSdqapRbY69Y271b0aMLzlleUn9ZTBO1dlKV2_dw_lPADHVia8z3pxL-8sUhIXLsgj4acchMk4c9YX-sFh07xENnyZ-_TXm3llPLuL67HUfBC2eKe800TmCYVWc9U\",\"d\":\"bn1nFxCQT4KLTHqo8mo9HvHD0cRNRNdWcKNnnEQkCF6tKbt-ILRyQGP8O40axLd7CoNVG9c9p_-g4-2kwCtLJNv_STLtwfpCY7VN5o6-ZIpfTjiW6duoPrLWq64Hm_4LOBQTiZfUPcLhsuJRHbWqakj-kV_YbUyC2Ocf_dd8IAQcSrAU2SCcDebhDCWwRUFvaa9V5eq0851S9goaA-AJz-JXyePH6ZFr8JxmWkWxYZ5kdcMD-sm9ZbxE0CaEk32l4fE4hR-L8x2dDtjWA-ahKCZ091z-gV3HWtR2JOjvxoNRjxUo3UxaGiFJHWNIl0EYUJZu1Cb-5wIlEI7wPx5mwQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"qS0OK48M2CIAA6_4Wdw4EbCaAfcTLf5Oy9t5BOF_PFUKqoSpZ6JsT5H0a_4zkjt-oI969v78OTlvBKbmEyKO-KeytzHBAA5CsLmVcz0THrMSg6oXZqu66MPnvWoZN9FEN5TklPOvBFm8Bg1QZ3k-YMVaM--DLvhaYR95_mqaz50\",\"dp\":\"Too2NozLGD1XrXyhabZvy1E0EuaVFj0UHQPDLSpkZ_2g3BK6Art6T0xmE8RYtmqrKIEIdlI3IliAvyvAx_1D7zWTTRaj-xlZyqJFrnXWL7zj8UxT8PkB-r2E-ILZ3NAi1gxIWezlBTZ8M6NfObDFmbTc_3tJkN_raISo8z_ziIE\",\"dq\":\"U0yhSkY5yOsa9YcMoigGVBWSJLpNHtbg5NypjHrPv8OhWbkOSq7WvSstBkFk5AtyFvvfZLMLIkWWxxGzV0t6f1MoxBtttLrYYyCxwihiiGFhLbAdSuZ1wnxcqA9bC7UVECvrQmVTpsMs8UupfHKbQBpZ8OWAqrnuYNNtG4_4Bt0\",\"n\":\"lygtuZj0lJjqOqIWocF8Bb583QDdq-aaFg8PesOp2-EDda6GqCpL-_NZVOflNGX7XIgjsWHcPsQHsV9gWuOzSJ0iEuWvtQ6eGBP5M6m7pccLNZfwUse8Cb4Ngx3XiTlyuqM7pv0LPyppZusfEHVEdeelou7Dy9k0OQ_nJTI3b2E1WBoHC58CJ453lo4gcBm1efURN3LIVc1V9NQY_ESBKVdwqYyoJPEanURLVGRd6cQKn6YrCbbIRHjqAyqOE-z3KmgDJnPriljfR5XhSGyM9eqD9Xpy6zu_MAeMJJfSArp857zLPk-Wf5VP9STAcjyfdBIybMKnwBYr2qHMT675hQ\"}]}", - 200 - ); - - private static final MockResponse NOT_FOUND_RESPONSE = response( - "{ \"message\" : \"This mock authorization server responds to just one request: GET /.well-known/jwks.json.\" }", - 404 - ); - - /** - * Name of the random {@link PropertySource}. - */ - public static final String MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME = "mockwebserver"; - - private static final String NAME = "mockwebserver.url"; - - private static final Log logger = LogFactory.getLog(MockWebServerPropertySource.class); - - private boolean started; - - public MockWebServerPropertySource() { - super(MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME, new MockWebServer()); - } - - @Override - public Object getProperty(String name) { - if (!name.equals(NAME)) { - return null; - } - if (logger.isTraceEnabled()) { - logger.trace("Looking up the url for '" + name + "'"); - } - String url = getUrl(); - return url; - } - - @Override - public void destroy() throws Exception { - getSource().shutdown(); - } - - /** - * Get's the URL (i.e. "http://localhost:123456") - * @return - */ - private String getUrl() { - MockWebServer mockWebServer = getSource(); - if (!this.started) { - intializeMockWebServer(mockWebServer); - } - String url = mockWebServer.url("").url().toExternalForm(); - return url.substring(0, url.length() - 1); - } - - private void intializeMockWebServer(MockWebServer mockWebServer) { - Dispatcher dispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - if ("/.well-known/jwks.json".equals(request.getPath())) { - return JWKS_RESPONSE; - } - - return NOT_FOUND_RESPONSE; - } - }; - - mockWebServer.setDispatcher(dispatcher); - try { - mockWebServer.start(); - this.started = true; - } catch (IOException e) { - throw new RuntimeException("Could not start " + mockWebServer, e); - } - } - - private static MockResponse response(String body, int status) { - return new MockResponse() - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setResponseCode(status) - .setBody(body); - } - -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/package-info.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/package-info.java deleted file mode 100644 index 02260378222..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/org/springframework/boot/env/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -/** - * This provides integration of a {@link okhttp3.mockwebserver.MockWebServer} and the - * {@link org.springframework.core.env.Environment} - * @author Rob Winch - */ -package org.springframework.boot.env; diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index ea34b99e202..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal Jwt jwt) { - return String.format("Hello, %s!", jwt.getSubject()); - } - - @GetMapping("/message") - public String message() { - return "secret message"; - } - - @PostMapping("/message") - public String createMessage(@RequestBody String message) { - return String.format("Message was created. Content: %s", message); - } -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java deleted file mode 100644 index d4f6429b51a..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Rob Winch - * @since 5.1 - */ -@EnableWebFluxSecurity -public class SecurityConfig { - - @Bean - SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { - http - .authorizeExchange((exchanges) -> - exchanges - .pathMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") - .pathMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") - .anyExchange().authenticated() - ) - .oauth2ResourceServer((oauth2ResourceServer) -> - oauth2ResourceServer - .jwt(withDefaults()) - ); - return http.build(); - } -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/ServerOAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/ServerOAuth2ResourceServerApplication.java deleted file mode 100644 index c3e3575e26d..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/ServerOAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - */ -@SpringBootApplication -public class ServerOAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(ServerOAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/resources/META-INF/spring.factories b/samples/boot/oauth2resourceserver-webflux/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37b447c9702..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.boot.env.MockWebServerEnvironmentPostProcessor diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/resources/application.yml b/samples/boot/oauth2resourceserver-webflux/src/main/resources/application.yml deleted file mode 100644 index 2a6d127d3fe..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json diff --git a/samples/boot/oauth2resourceserver-webflux/src/test/java/sample/OAuth2ResourceServerControllerTests.java b/samples/boot/oauth2resourceserver-webflux/src/test/java/sample/OAuth2ResourceServerControllerTests.java deleted file mode 100644 index 8fc72574f19..00000000000 --- a/samples/boot/oauth2resourceserver-webflux/src/test/java/sample/OAuth2ResourceServerControllerTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockJwt; - -/** - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@WebFluxTest(OAuth2ResourceServerController.class) -@Import(SecurityConfig.class) -public class OAuth2ResourceServerControllerTests { - - @Autowired - WebTestClient rest; - - @MockBean - ReactiveJwtDecoder jwtDecoder; - - @Test - public void indexGreetsAuthenticatedUser() { - this.rest.mutateWith(mockJwt().jwt((jwt) -> jwt.subject("test-subject"))) - .get().uri("/").exchange() - .expectBody(String.class).isEqualTo("Hello, test-subject!"); - } - - @Test - public void messageCanBeReadWithScopeMessageReadAuthority() { - this.rest.mutateWith(mockJwt().jwt((jwt) -> jwt.claim("scope", "message:read"))) - .get().uri("/message").exchange() - .expectBody(String.class).isEqualTo("secret message"); - - this.rest.mutateWith(mockJwt().authorities(new SimpleGrantedAuthority("SCOPE_message:read"))) - .get().uri("/message").exchange() - .expectBody(String.class).isEqualTo("secret message"); - } - - @Test - public void messageCanNotBeReadWithoutScopeMessageReadAuthority() { - this.rest.mutateWith(mockJwt()) - .get().uri("/message").exchange() - .expectStatus().isForbidden(); - } - - @Test - public void messageCanNotBeCreatedWithoutAnyScope() { - Jwt jwt = jwt().claim("scope", "").build(); - when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt)); - this.rest.post().uri("/message") - .headers((headers) -> headers.setBearerAuth(jwt.getTokenValue())) - .syncBody("Hello message").exchange() - .expectStatus().isForbidden(); - } - - @Test - public void messageCanNotBeCreatedWithScopeMessageReadAuthority() { - Jwt jwt = jwt().claim("scope", "message:read").build(); - when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt)); - this.rest.post().uri("/message") - .headers((headers) -> headers.setBearerAuth(jwt.getTokenValue())) - .syncBody("Hello message").exchange() - .expectStatus().isForbidden(); - } - - @Test - public void messageCanBeCreatedWithScopeMessageWriteAuthority() { - Jwt jwt = jwt().claim("scope", "message:write").build(); - when(this.jwtDecoder.decode(anyString())).thenReturn(Mono.just(jwt)); - this.rest.post().uri("/message") - .headers((headers) -> headers.setBearerAuth(jwt.getTokenValue())) - .syncBody("Hello message").exchange() - .expectStatus().isOk() - .expectBody(String.class).isEqualTo("Message was created. Content: Hello message"); - } - - private Jwt.Builder jwt() { - return Jwt.withTokenValue("token").header("alg", "none"); - } -} diff --git a/samples/boot/oauth2resourceserver/README.adoc b/samples/boot/oauth2resourceserver/README.adoc deleted file mode 100644 index a82747004f8..00000000000 --- a/samples/boot/oauth2resourceserver/README.adoc +++ /dev/null @@ -1,126 +0,0 @@ -= OAuth 2.0 Resource Server Sample - -This sample demonstrates integrating Resource Server with a mock Authorization Server, though it can be modified to integrate -with your favorite Authorization Server. - -With it, you can run the integration tests or run the application as a stand-alone service to explore how you can -secure your own service with OAuth 2.0 Bearer Tokens using Spring Security. - -== 1. Running the tests - -To run the tests, do: - -```bash -./gradlew integrationTest -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplicationTests` from there. - -=== What is it doing? - -By default, the tests are pointing at a mock Authorization Server instance. - -The tests are configured with a set of hard-coded tokens originally obtained from the mock Authorization Server, -and each makes a query to the Resource Server with their corresponding token. - -The Resource Server subsquently verifies with the Authorization Server and authorizes the request, returning the phrase - -```bash -Hello, subject! -``` - -where "subject" is the value of the `sub` field in the JWT returned by the Authorization Server. - -== 2. Running the app - -To run as a stand-alone application, do: - -```bash -./gradlew bootRun -``` - -Or import the project into your IDE and run `OAuth2ResourceServerApplication` from there. - -Once it is up, you can use the following token: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1ODgwLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMDFkOThlZWEtNjc0MC00OGRlLTk4ODAtYzM5ZjgyMGZiNzVlIiwiY2xpZW50X2lkIjoibm9zY29wZXMiLCJzY29wZSI6WyJub25lIl19.VOzgGLOUuQ_R2Ur1Ke41VaobddhKgUZgto7Y3AGxst7SuxLQ4LgWwdSSDRx-jRvypjsCgYPbjAYLhn9nCbfwtCitkymUKUNKdebvVAI0y8YvliWTL5S-GiJD9dN8SSsXUla9A4xB_9Mt5JAlRpQotQSCLojVSKQmjhMpQWmYAlKVjnlImoRwQFPI4w3Ijn4G4EMTKWUYRfrD0-WNT9ZYWBeza6QgV6sraP7ToRB3eQLy2p04cU40X-RHLeYCsMBfxsMMh89CJff-9tn7VDKi1hAGc_Lp9yS9ZaItJuFJTjf8S_vsjVB1nBhvdS_6IED_m_fOU52KiGSO2qL6shxHvg -``` - -And then make this request: - -```bash -curl -H "Authorization: Bearer $TOKEN" localhost:8080 -``` - -Which will respond with the phrase: - -```bash -Hello, subject! -``` - -where `subject` is the value of the `sub` field in the JWT returned by the Authorization Server. - -Or this to make a GET request to /messages: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1NjQ4LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiY2I1ZGMwNDYtMDkyMi00ZGJmLWE5MzAtOGI2M2FhZTYzZjk2IiwiY2xpZW50X2lkIjoicmVhZGVyIiwic2NvcGUiOlsibWVzc2FnZTpyZWFkIl19.Pre2ksnMiOGYWQtuIgHB0i3uTnNzD0SMFM34iyQJHK5RLlSjge08s9qHdx6uv5cZ4gZm_cB1D6f4-fLx76bCblK6mVcabbR74w_eCdSBXNXuqG-HNrOYYmmx5iJtdwx5fXPmF8TyVzsq_LvRm_LN4lWNYquT4y36Tox6ZD3feYxXvHQ3XyZn9mVKnlzv-GCwkBohCR3yPow5uVmr04qh_al52VIwKMrvJBr44igr4fTZmzwRAZmQw5rZeyep0b4nsCjadNcndHtMtYKNVuG5zbDLsB7GGvilcI9TDDnUXtwthB_3iq32DAd9x8wJmJ5K8gmX6GjZFtYzKk_zEboXoQ - -curl -H "Authorization: Bearer $TOKEN" localhost:8080/message -``` - -Will respond with: - -```bash -secret message -``` - -In order to make a POST request to /message, you can use the following request: - -```bash -export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQzOTA0LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiZGI4ZjgwMzQtM2VlNy00NjBjLTk3NTEtMDJiMDA1OWI5NzA4IiwiY2xpZW50X2lkIjoid3JpdGVyIiwic2NvcGUiOlsibWVzc2FnZTp3cml0ZSJdfQ.USvpx_ntKXtchLmc93auJq0qSav6vLm4B7ItPzhrDH2xmogBP35eKeklwXK5GCb7ck1aKJV5SpguBlTCz0bZC1zAWKB6gyFIqedALPAran5QR-8WpGfl0wFqds7d8Jw3xmpUUBduRLab9hkeAhgoVgxevc8d6ITM7kRnHo5wT3VzvBU8DquedVXm5fbBnRPgG4_jOWJKbqYpqaR2z2TnZRWh3CqL82Orh1Ww1dJYF_fae1dTVV4tvN5iSndYcGxMoBaiw3kRRi6EyNxnXnt1pFtZqc1f6D9x4AHiri8_vpBp2vwG5OfQD5-rrleP_XlIB3rNQT7tu3fiqu4vUzQaEg - -curl -H "Authorization: Bearer $TOKEN" -d "my message" localhost:8080/message -``` - -Will respond this: - -```bash -Message was created. Content: my message -``` - -== 2. Testing against other Authorization Servers - -_In order to use this sample, your Authorization Server must support JWTs that either use the "scope" or "scp" attribute._ - -To change the sample to point at your Authorization Server, simply find this property in the `application.yml`: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json -``` - -And change the property to your Authorization Server's JWK set endpoint: - -```yaml -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: https://dev-123456.oktapreview.com/oauth2/default/v1/keys -``` - -And then you can run the app the same as before: - -```bash -./gradlew bootRun -``` - -Make sure to obtain valid tokens from your Authorization Server in order to play with the sample Resource Server. -To use the `/` endpoint, any valid token from your Authorization Server will do. -To use the `/message` endpoint, the token should have the `message:read` scope. diff --git a/samples/boot/oauth2resourceserver/spring-security-samples-boot-oauth2resourceserver.gradle b/samples/boot/oauth2resourceserver/spring-security-samples-boot-oauth2resourceserver.gradle deleted file mode 100644 index 2135bb0af66..00000000000 --- a/samples/boot/oauth2resourceserver/spring-security-samples-boot-oauth2resourceserver.gradle +++ /dev/null @@ -1,13 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-jose') - compile project(':spring-security-oauth2-resource-server') - - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'com.squareup.okhttp3:mockwebserver' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/oauth2resourceserver/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java b/samples/boot/oauth2resourceserver/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java deleted file mode 100644 index bb7de586653..00000000000 --- a/samples/boot/oauth2resourceserver/src/integration-test/java/sample/OAuth2ResourceServerApplicationITests.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.RequestPostProcessor; - -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for {@link OAuth2ResourceServerApplication} - * - * @author Josh Cummings - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -public class OAuth2ResourceServerApplicationITests { - - String noScopesToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1ODgwLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMDFkOThlZWEtNjc0MC00OGRlLTk4ODAtYzM5ZjgyMGZiNzVlIiwiY2xpZW50X2lkIjoibm9zY29wZXMiLCJzY29wZSI6WyJub25lIl19.VOzgGLOUuQ_R2Ur1Ke41VaobddhKgUZgto7Y3AGxst7SuxLQ4LgWwdSSDRx-jRvypjsCgYPbjAYLhn9nCbfwtCitkymUKUNKdebvVAI0y8YvliWTL5S-GiJD9dN8SSsXUla9A4xB_9Mt5JAlRpQotQSCLojVSKQmjhMpQWmYAlKVjnlImoRwQFPI4w3Ijn4G4EMTKWUYRfrD0-WNT9ZYWBeza6QgV6sraP7ToRB3eQLy2p04cU40X-RHLeYCsMBfxsMMh89CJff-9tn7VDKi1hAGc_Lp9yS9ZaItJuFJTjf8S_vsjVB1nBhvdS_6IED_m_fOU52KiGSO2qL6shxHvg"; - String messageReadToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1NjQ4LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiY2I1ZGMwNDYtMDkyMi00ZGJmLWE5MzAtOGI2M2FhZTYzZjk2IiwiY2xpZW50X2lkIjoicmVhZGVyIiwic2NvcGUiOlsibWVzc2FnZTpyZWFkIl19.Pre2ksnMiOGYWQtuIgHB0i3uTnNzD0SMFM34iyQJHK5RLlSjge08s9qHdx6uv5cZ4gZm_cB1D6f4-fLx76bCblK6mVcabbR74w_eCdSBXNXuqG-HNrOYYmmx5iJtdwx5fXPmF8TyVzsq_LvRm_LN4lWNYquT4y36Tox6ZD3feYxXvHQ3XyZn9mVKnlzv-GCwkBohCR3yPow5uVmr04qh_al52VIwKMrvJBr44igr4fTZmzwRAZmQw5rZeyep0b4nsCjadNcndHtMtYKNVuG5zbDLsB7GGvilcI9TDDnUXtwthB_3iq32DAd9x8wJmJ5K8gmX6GjZFtYzKk_zEboXoQ"; - String messageWriteToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQzOTA0LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiZGI4ZjgwMzQtM2VlNy00NjBjLTk3NTEtMDJiMDA1OWI5NzA4IiwiY2xpZW50X2lkIjoid3JpdGVyIiwic2NvcGUiOlsibWVzc2FnZTp3cml0ZSJdfQ.USvpx_ntKXtchLmc93auJq0qSav6vLm4B7ItPzhrDH2xmogBP35eKeklwXK5GCb7ck1aKJV5SpguBlTCz0bZC1zAWKB6gyFIqedALPAran5QR-8WpGfl0wFqds7d8Jw3xmpUUBduRLab9hkeAhgoVgxevc8d6ITM7kRnHo5wT3VzvBU8DquedVXm5fbBnRPgG4_jOWJKbqYpqaR2z2TnZRWh3CqL82Orh1Ww1dJYF_fae1dTVV4tvN5iSndYcGxMoBaiw3kRRi6EyNxnXnt1pFtZqc1f6D9x4AHiri8_vpBp2vwG5OfQD5-rrleP_XlIB3rNQT7tu3fiqu4vUzQaEg"; - - @Autowired - MockMvc mvc; - - @Test - public void performWhenValidBearerTokenThenAllows() - throws Exception { - - this.mvc.perform(get("/").with(bearerToken(this.noScopesToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Hello, subject!"))); - } - - @Test - public void performWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.messageReadToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("secret message"))); - } - - @Test - public void performWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(get("/message").with(bearerToken(this.noScopesToken))) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - @Test - public void performPostWhenValidBearerTokenThenScopedRequestsAlsoWork() - throws Exception { - - this.mvc.perform(post("/message").content("example message") - .with(bearerToken(this.messageWriteToken))) - .andExpect(status().isOk()) - .andExpect(content().string(containsString("Message was created"))); - } - - @Test - public void performPostWhenInsufficientlyScopedBearerTokenThenDeniesScopedMethodAccess() - throws Exception { - - this.mvc.perform(post("/message").content("Example message") - .with(bearerToken(this.messageReadToken))) - .andExpect(status().isForbidden()) - .andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, - containsString("Bearer error=\"insufficient_scope\""))); - } - - private static class BearerTokenRequestPostProcessor implements RequestPostProcessor { - private String token; - - BearerTokenRequestPostProcessor(String token) { - this.token = token; - } - - @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - request.addHeader("Authorization", "Bearer " + this.token); - return request; - } - } - - private static BearerTokenRequestPostProcessor bearerToken(String token) { - return new BearerTokenRequestPostProcessor(token); - } -} diff --git a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java b/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java deleted file mode 100644 index 62f91d1a1fc..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerEnvironmentPostProcessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.boot.SpringApplication; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * @author Rob Winch - */ -public class MockWebServerEnvironmentPostProcessor - implements EnvironmentPostProcessor, DisposableBean { - - private final MockWebServerPropertySource propertySource = new MockWebServerPropertySource(); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - environment.getPropertySources().addFirst(this.propertySource); - } - - @Override - public void destroy() throws Exception { - this.propertySource.destroy(); - } -} diff --git a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java b/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java deleted file mode 100644 index b90ad29b91c..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/MockWebServerPropertySource.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.boot.env; - -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.core.env.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -import java.io.IOException; - -/** - * @author Rob Winch - */ -public class MockWebServerPropertySource extends PropertySource<MockWebServer> implements - DisposableBean { - - private static final MockResponse JWKS_RESPONSE = response( - "{ \"keys\": [ { \"kty\": \"RSA\", \"e\": \"AQAB\", \"n\": \"jvBtqsGCOmnYzwe_-HvgOqlKk6HPiLEzS6uCCcnVkFXrhnkPMZ-uQXTR0u-7ZklF0XC7-AMW8FQDOJS1T7IyJpCyeU4lS8RIf_Z8RX51gPGnQWkRvNw61RfiSuSA45LR5NrFTAAGoXUca_lZnbqnl0td-6hBDVeHYkkpAsSck1NPhlcsn-Pvc2Vleui_Iy1U2mzZCM1Vx6Dy7x9IeP_rTNtDhULDMFbB_JYs-Dg6Zd5Ounb3mP57tBGhLYN7zJkN1AAaBYkElsc4GUsGsUWKqgteQSXZorpf6HdSJsQMZBDd7xG8zDDJ28hGjJSgWBndRGSzQEYU09Xbtzk-8khPuw\" } ] }", - 200 - ); - - private static final MockResponse NOT_FOUND_RESPONSE = response( - "{ \"message\" : \"This mock authorization server responds to just one request: GET /.well-known/jwks.json.\" }", - 404 - ); - - /** - * Name of the random {@link PropertySource}. - */ - public static final String MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME = "mockwebserver"; - - private static final String NAME = "mockwebserver.url"; - - private static final Log logger = LogFactory.getLog(MockWebServerPropertySource.class); - - private boolean started; - - public MockWebServerPropertySource() { - super(MOCK_WEB_SERVER_PROPERTY_SOURCE_NAME, new MockWebServer()); - } - - @Override - public Object getProperty(String name) { - if (!name.equals(NAME)) { - return null; - } - if (logger.isTraceEnabled()) { - logger.trace("Looking up the url for '" + name + "'"); - } - String url = getUrl(); - return url; - } - - @Override - public void destroy() throws Exception { - getSource().shutdown(); - } - - /** - * Get's the URL (i.e. "http://localhost:123456") - * @return - */ - private String getUrl() { - MockWebServer mockWebServer = getSource(); - if (!this.started) { - intializeMockWebServer(mockWebServer); - } - String url = mockWebServer.url("").url().toExternalForm(); - return url.substring(0, url.length() - 1); - } - - private void intializeMockWebServer(MockWebServer mockWebServer) { - Dispatcher dispatcher = new Dispatcher() { - @Override - public MockResponse dispatch(RecordedRequest request) { - if ("/.well-known/jwks.json".equals(request.getPath())) { - return JWKS_RESPONSE; - } - - return NOT_FOUND_RESPONSE; - } - }; - - mockWebServer.setDispatcher(dispatcher); - try { - mockWebServer.start(); - this.started = true; - } catch (IOException e) { - throw new RuntimeException("Could not start " + mockWebServer, e); - } - } - - private static MockResponse response(String body, int status) { - return new MockResponse() - .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .setResponseCode(status) - .setBody(body); - } - -} diff --git a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/package-info.java b/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/package-info.java deleted file mode 100644 index 02260378222..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/org/springframework/boot/env/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -/** - * This provides integration of a {@link okhttp3.mockwebserver.MockWebServer} and the - * {@link org.springframework.core.env.Environment} - * @author Rob Winch - */ -package org.springframework.boot.env; diff --git a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerApplication.java b/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerApplication.java deleted file mode 100644 index a0841c00f32..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Josh Cummings - */ -@SpringBootApplication -public class OAuth2ResourceServerApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2ResourceServerApplication.class, args); - } -} diff --git a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerController.java b/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerController.java deleted file mode 100644 index c761078dd8e..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerController.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author Josh Cummings - */ -@RestController -public class OAuth2ResourceServerController { - - @GetMapping("/") - public String index(@AuthenticationPrincipal Jwt jwt) { - return String.format("Hello, %s!", jwt.getSubject()); - } - - @GetMapping("/message") - public String message() { - return "secret message"; - } - - @PostMapping("/message") - public String createMessage(@RequestBody String message) { - return String.format("Message was created. Content: %s", message); - } -} diff --git a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java b/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java deleted file mode 100644 index d7e157cfc65..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/java/sample/OAuth2ResourceServerSecurityConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; - -/** - * @author Josh Cummings - */ -@EnableWebSecurity -public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") String jwkSetUri; - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read") - .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write") - .anyRequest().authenticated() - ) - .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); - // @formatter:on - } - - @Bean - JwtDecoder jwtDecoder() { - return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build(); - } -} diff --git a/samples/boot/oauth2resourceserver/src/main/resources/META-INF/spring.factories b/samples/boot/oauth2resourceserver/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 37b447c9702..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=org.springframework.boot.env.MockWebServerEnvironmentPostProcessor diff --git a/samples/boot/oauth2resourceserver/src/main/resources/application.yml b/samples/boot/oauth2resourceserver/src/main/resources/application.yml deleted file mode 100644 index 2a6d127d3fe..00000000000 --- a/samples/boot/oauth2resourceserver/src/main/resources/application.yml +++ /dev/null @@ -1,6 +0,0 @@ -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: ${mockwebserver.url}/.well-known/jwks.json diff --git a/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java b/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java deleted file mode 100644 index 565212b0738..00000000000 --- a/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * - * @author Jérôme Wacongne <ch4mp@c4-soft.com> - * @author Josh Cummings - * @since 5.2.0 - * - */ -@RunWith(SpringRunner.class) -@WebMvcTest(OAuth2ResourceServerController.class) -public class OAuth2ResourceServerControllerTests { - - @Autowired - MockMvc mockMvc; - - @Test - public void indexGreetsAuthenticatedUser() throws Exception { - mockMvc.perform(get("/").with(jwt().jwt((jwt) -> jwt.subject("ch4mpy")))) - .andExpect(content().string(is("Hello, ch4mpy!"))); - } - - @Test - public void messageCanBeReadWithScopeMessageReadAuthority() throws Exception { - mockMvc.perform(get("/message").with(jwt().jwt((jwt) -> jwt.claim("scope", "message:read")))) - .andExpect(content().string(is("secret message"))); - - mockMvc.perform(get("/message") - .with(jwt().authorities(new SimpleGrantedAuthority(("SCOPE_message:read"))))) - .andExpect(content().string(is("secret message"))); - } - - @Test - public void messageCanNotBeReadWithoutScopeMessageReadAuthority() throws Exception { - mockMvc.perform(get("/message").with(jwt())) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanNotBeCreatedWithoutAnyScope() throws Exception { - mockMvc.perform(post("/message") - .content("Hello message") - .with(jwt())) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanNotBeCreatedWithScopeMessageReadAuthority() throws Exception { - mockMvc.perform(post("/message") - .content("Hello message") - .with(jwt().jwt((jwt) -> jwt.claim("scope", "message:read")))) - .andExpect(status().isForbidden()); - } - - @Test - public void messageCanBeCreatedWithScopeMessageWriteAuthority() - throws Exception { - mockMvc.perform(post("/message") - .content("Hello message") - .with(jwt().jwt((jwt) -> jwt.claim("scope", "message:write")))) - .andExpect(status().isOk()) - .andExpect(content().string(is("Message was created. Content: Hello message"))); - } -} diff --git a/samples/boot/oauth2webclient-webflux/README.adoc b/samples/boot/oauth2webclient-webflux/README.adoc deleted file mode 100644 index ea984aa684a..00000000000 --- a/samples/boot/oauth2webclient-webflux/README.adoc +++ /dev/null @@ -1,63 +0,0 @@ -= OAuth 2.0 WebClient (WebFlux) Sample - -== GitHub Repositories - -This guide provides instructions on setting up the sample application, which leverages WebClient OAuth2 integration to display a list of public GitHub repositories that are accessible to the authenticated user. - -This includes repositories owned by the authenticated user, repositories where the authenticated user is a collaborator, and repositories that the authenticated user has access to through an organization membership. - -The following sections provide detailed steps for setting up the sample and covers the following topics: - -* <<github-register-application,Register OAuth application>> -* <<github-application-config,Configure application.yml>> -* <<github-boot-application,Boot up the application>> - -[[github-register-application]] -=== Register OAuth application - -To use GitHub's OAuth 2.0 authorization system, you must https://github.com/settings/applications/new[Register a new OAuth application]. - -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/client-id`. - -The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub and have granted access to the OAuth application on the _Authorize application_ page. - -[[github-application-config]] -=== Configure application.yml - -Now that you have a new OAuth application with GitHub, you need to configure the sample to use the OAuth application for the _authorization code grant flow_. -To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - client-id: <2> - client-id: replace-with-client-id - client-secret: replace-with-client-secret - provider: github - scope: read:user,public_repo ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, which is github. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[github-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ form login page. -Log in using *'user'* (username) and *'password'* (password) or click the link to authenticate with GitHub and then you'll be redirected to GitHub for authentication. - -After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access and display your public repository information. diff --git a/samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle b/samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle deleted file mode 100644 index 286f80a5818..00000000000 --- a/samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle +++ /dev/null @@ -1,15 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-jose') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-webflux' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - compile 'io.projectreactor.netty:reactor-netty' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'com.squareup.okhttp3:mockwebserver' -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java deleted file mode 100644 index cdd6416f937..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Joe Grandja - */ -@SpringBootApplication -public class OAuth2WebClientWebFluxApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2WebClientWebFluxApplication.class, args); - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java deleted file mode 100644 index 33fb8b78676..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Rob Winch - */ -@EnableWebFluxSecurity -public class SecurityConfig { - - @Bean - SecurityWebFilterChain configure(ServerHttpSecurity http) { - http - .authorizeExchange((exchanges) -> - exchanges - .pathMatchers("/", "/public/**").permitAll() - .anyExchange().authenticated() - ) - .oauth2Login(withDefaults()) - .formLogin(withDefaults()) - .oauth2Client(withDefaults()); - return http.build(); - } - - @Bean - MapReactiveUserDetailsService userDetailsService() { - UserDetails userDetails = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build(); - return new MapReactiveUserDetailsService(userDetails); - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java deleted file mode 100644 index 6016aa2ef59..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; -import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * @author Rob Winch - * @since 5.1 - */ -@Configuration -public class WebClientConfig { - - @Value("${resource-uri}") String uri; - - @Bean - WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) { - ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = - new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); - oauth.setDefaultOAuth2AuthorizedClient(true); - return WebClient.builder() - .baseUrl(this.uri) - .filter(oauth) - .build(); - } - - @Bean - ReactiveOAuth2AuthorizedClientManager authorizedClientManager( - ReactiveClientRegistrationRepository clientRegistrationRepository, - ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { - - ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = - ReactiveOAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken() - .clientCredentials() - .password() - .build(); - DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = - new DefaultReactiveOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - return authorizedClientManager; - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java deleted file mode 100644 index e1e0035ef13..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Rob Winch - */ -@Controller -public class IndexController { - @GetMapping("/") - String index() { - return "index"; - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java deleted file mode 100644 index a1ddae90aaa..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import reactor.core.publisher.Mono; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; - -/** - * @author Joe Grandja - * @author Rob Winch - */ -@Controller -@RequestMapping(path = {"/webclient", "/public/webclient"}) -public class OAuth2WebClientController { - private final WebClient webClient; - - public OAuth2WebClientController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/explicit") - String explicit(Model model) { - Mono<String> body = this.webClient - .get() - .attributes(clientRegistrationId("client-id")) - .retrieve() - .bodyToMono(String.class); - model.addAttribute("body", body); - return "response"; - } - - @GetMapping("/implicit") - String implicit(Model model) { - Mono<String> body = this.webClient - .get() - .retrieve() - .bodyToMono(String.class); - model.addAttribute("body", body); - return "response"; - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java deleted file mode 100644 index 558277394f9..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import reactor.core.publisher.Mono; - -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; - -/** - * @author Joe Grandja - * @author Rob Winch - */ -@Controller -@RequestMapping(path = {"/annotation", "/public/annotation"}) -public class RegisteredOAuth2AuthorizedClientController { - - private final WebClient webClient; - - public RegisteredOAuth2AuthorizedClientController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/explicit") - String explicit(Model model, @RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { - Mono<String> body = this.webClient - .get() - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(String.class); - model.addAttribute("body", body); - return "response"; - } - - @GetMapping("/implicit") - String implicit(Model model, @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) { - Mono<String> body = this.webClient - .get() - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(String.class); - model.addAttribute("body", body); - return "response"; - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/main/resources/application.yml b/samples/boot/oauth2webclient-webflux/src/main/resources/application.yml deleted file mode 100644 index 8528a06d0da..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/resources/application.yml +++ /dev/null @@ -1,21 +0,0 @@ -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - client-id: - client-id: replace-with-client-id - client-secret: replace-with-client-secret - provider: github - scope: read:user,public_repo - -resource-uri: https://api.github.com/user/repos diff --git a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html deleted file mode 100644 index 7787a75dd7b..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> -<head> - <title>OAuth2 WebClient Showcase</title> - <meta charset="utf-8" /> -</head> -<body> -<a th:href="@{/logout}">Log Out</a> -<h1>Examples</h1> - -<h2>@RegisteredOAuth2AuthorizedClient</h2> -<p> -Examples on RegisteredOAuth2AuthorizedClientController -<h3>Authenticated</h3> -<ul> - <li><a th:href="@{/annotation/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/annotation/implicit}">Implicit</a> - Use the currently logged in user's OAuth Token. This will - only work if the user authenticates with oauth2Login and the token provided is the correct token provided at - log in is authorized.</li> -</ul> -<h3>Public</h3> -<ul> - <li><a th:href="@{/public/annotation/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/public/annotation/implicit}">Implicit</a> - This will fail if the user is not authenticated. - Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then - authenticates with oauth2Login()</li> -</ul> - -<h2>ServerOAuth2AuthorizedClientExchangeFilterFunction</h2> -<p> - Examples on OAuth2WebClientController that demonstrate how to use ServerOAuth2AuthorizedClientExchangeFilterFunction -<h3>Authenticated</h3> -<ul> - <li><a th:href="@{/webclient/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/webclient/implicit}">Implicit</a> - Use the currently logged in user's OAuth Token. This will - only work if the user authenticates with oauth2Login and the token provided is the correct token provided at - log in is authorized.</li> -</ul> -<h3>Public</h3> -<ul> - <li><a th:href="@{/public/webclient/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/public/webclient/implicit}">Implicit</a> - This will fail if the user is not authenticated. - Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then - authenticates with oauth2Login()</li> -</ul> -</body> -</html> diff --git a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html deleted file mode 100644 index 210c18c29ae..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - ~ Copyright 2002-2018 the original author or authors. - ~ - ~ 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 - ~ - ~ https://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. - --> - -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> -<head> - <title>OAuth2 WebClient Showcase</title> - <meta charset="utf-8" /> -</head> -<body> -<a th:href="@{/}">Back</a> -<h1>Response</h1> -<pre><code id="json" class="json" th:text="${body}"></code></pre> -<script> - json.innerHTML = JSON.stringify(JSON.parse(json.innerHTML), null, 4); -</script> -</body> -</html> diff --git a/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientControllerTests.java b/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientControllerTests.java deleted file mode 100644 index 52445dd2128..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientControllerTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.AfterClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import sample.config.SecurityConfig; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Client; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Login; - -@WebFluxTest -@Import(SecurityConfig.class) -@AutoConfigureWebTestClient -@RunWith(SpringRunner.class) -public class OAuth2WebClientControllerTests { - private static MockWebServer web = new MockWebServer(); - - @Autowired - private WebTestClient client; - - @AfterClass - public static void shutdown() throws Exception { - web.shutdown(); - } - - @Test - public void explicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .mutateWith(mockOAuth2Client("client-id")) - .get().uri("/webclient/explicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void implicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .get().uri("/webclient/implicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void publicExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Client("client-id")) - .get().uri("/public/webclient/explicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void publicImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .get().uri("/public/webclient/implicit") - .exchange() - .expectStatus().isOk(); - } - - @TestConfiguration - static class WebClientConfig { - @Bean - WebClient web() { - return WebClient.create(web.url("/").toString()); - } - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java b/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java deleted file mode 100644 index 657a6f7a163..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * @author Rob Winch - */ -@SpringBootTest -@AutoConfigureWebTestClient -@RunWith(SpringRunner.class) -public class OAuth2WebClientWebFluxApplicationTests { - @Autowired - private WebTestClient client; - - @Test - public void annotationExplicitWhenNotAuthenticatedThenLoginRequested() { - this.client.get().uri("/annotation/explicit") - .exchange() - .expectStatus().is3xxRedirection(); - } -} diff --git a/samples/boot/oauth2webclient-webflux/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java b/samples/boot/oauth2webclient-webflux/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java deleted file mode 100644 index 32fc9095b7c..00000000000 --- a/samples/boot/oauth2webclient-webflux/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.AfterClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import sample.config.SecurityConfig; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Client; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockOAuth2Login; - -@WebFluxTest -@Import(SecurityConfig.class) -@AutoConfigureWebTestClient -@RunWith(SpringRunner.class) -public class RegisteredOAuth2AuthorizedClientControllerTests { - private static MockWebServer web = new MockWebServer(); - - @Autowired - private WebTestClient client; - - @AfterClass - public static void shutdown() throws Exception { - web.shutdown(); - } - - @Test - public void annotationExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .mutateWith(mockOAuth2Client("client-id")) - .get().uri("/annotation/explicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void annotationImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .get().uri("/annotation/implicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void publicAnnotationExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Client("client-id")) - .get().uri("/public/annotation/explicit") - .exchange() - .expectStatus().isOk(); - } - - @Test - public void publicAnnotationImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.client.mutateWith(mockOAuth2Login()) - .get().uri("/public/annotation/implicit") - .exchange() - .expectStatus().isOk(); - } - - @TestConfiguration - static class WebClientConfig { - @Bean - WebClient web() { - return WebClient.create(web.url("/").toString()); - } - } -} diff --git a/samples/boot/oauth2webclient/README.adoc b/samples/boot/oauth2webclient/README.adoc deleted file mode 100644 index b234f34f7e8..00000000000 --- a/samples/boot/oauth2webclient/README.adoc +++ /dev/null @@ -1,63 +0,0 @@ -= OAuth 2.0 WebClient (Servlet) Sample - -== GitHub Repositories - -This guide provides instructions on setting up the sample application, which leverages WebClient OAuth2 integration to display a list of public GitHub repositories that are accessible to the authenticated user. - -This includes repositories owned by the authenticated user, repositories where the authenticated user is a collaborator, and repositories that the authenticated user has access to through an organization membership. - -The following sections provide detailed steps for setting up the sample and covers the following topics: - -* <<github-register-application,Register OAuth application>> -* <<github-application-config,Configure application.yml>> -* <<github-boot-application,Boot up the application>> - -[[github-register-application]] -=== Register OAuth application - -To use GitHub's OAuth 2.0 authorization system, you must https://github.com/settings/applications/new[Register a new OAuth application]. - -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/client-id`. - -The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub and have granted access to the OAuth application on the _Authorize application_ page. - -[[github-application-config]] -=== Configure application.yml - -Now that you have a new OAuth application with GitHub, you need to configure the sample to use the OAuth application for the _authorization code grant flow_. -To do so: - -. Go to `application.yml` and set the following configuration: -+ -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: <1> - client-id: <2> - client-id: replace-with-client-id - client-secret: replace-with-client-secret - provider: github - scope: read:user,public_repo ----- -+ -.OAuth Client properties -==== -<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. -<2> Following the base property prefix is the ID for the `ClientRegistration`, which is github. -==== - -. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. - -[[github-boot-application]] -=== Boot up the application - -Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ form login page. -Log in using *'user'* (username) and *'password'* (password) or click the link to authenticate with GitHub and then you'll be redirected to GitHub for authentication. - -After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access and display your public repository information. diff --git a/samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle b/samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle deleted file mode 100644 index f65d82709c4..00000000000 --- a/samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-jose') - compile 'org.springframework:spring-webflux' - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - compile 'io.projectreactor.netty:reactor-netty' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'com.squareup.okhttp3:mockwebserver' -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java b/samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java deleted file mode 100644 index 4c4a6b7de15..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration; - -/** - * @author Joe Grandja - */ -// FIXME: Work around https://github.com/spring-projects/spring-boot/issues/14463 -@SpringBootApplication(exclude = ReactiveOAuth2ClientAutoConfiguration.class) -public class OAuth2WebClientApplication { - - public static void main(String[] args) { - SpringApplication.run(OAuth2WebClientApplication.class, args); - } -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java b/samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java deleted file mode 100644 index e5602d812d7..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Joe Grandja - */ -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .mvcMatchers("/", "/public/**").permitAll() - .anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .oauth2Login(withDefaults()) - .oauth2Client(withDefaults()); - } - - @Bean - public UserDetailsService userDetailsService() { - UserDetails userDetails = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build(); - return new InMemoryUserDetailsManager(userDetails); - } -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java b/samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java deleted file mode 100644 index da9510602bd..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; -import org.springframework.web.reactive.function.client.WebClient; - -/** - * @author Rob Winch - * @since 5.1 - */ -@Configuration -public class WebClientConfig { - - @Value("${resource-uri}") String resourceUri; - - @Bean - WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { - ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = - new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); - oauth2.setDefaultOAuth2AuthorizedClient(true); - return WebClient.builder() - .baseUrl(this.resourceUri) - .apply(oauth2.oauth2Configuration()) - .build(); - } - - @Bean - OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository, - OAuth2AuthorizedClientRepository authorizedClientRepository) { - OAuth2AuthorizedClientProvider authorizedClientProvider = - OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken() - .clientCredentials() - .password() - .build(); - DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - return authorizedClientManager; - } -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java deleted file mode 100644 index e1e0035ef13..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Rob Winch - */ -@Controller -public class IndexController { - @GetMapping("/") - String index() { - return "index"; - } -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java deleted file mode 100644 index f20b479d1fd..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; - -/** - * @author Joe Grandja - * @author Rob Winch - */ -@Controller -@RequestMapping(path = {"/webclient", "/public/webclient"}) -public class OAuth2WebClientController { - private final WebClient webClient; - - public OAuth2WebClientController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/explicit") - String explicit(Model model) { - String body = this.webClient - .get() - .attributes(clientRegistrationId("client-id")) - .retrieve() - .bodyToMono(String.class) - .block(); - model.addAttribute("body", body); - return "response"; - } - - @GetMapping("/implicit") - String implicit(Model model) { - String body = this.webClient - .get() - .retrieve() - .bodyToMono(String.class) - .block(); - model.addAttribute("body", body); - return "response"; - } -} diff --git a/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java deleted file mode 100644 index 883db445d06..00000000000 --- a/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; - -/** - * @author Joe Grandja - * @author Rob Winch - */ -@Controller -@RequestMapping(path = {"/annotation", "/public/annotation"}) -public class RegisteredOAuth2AuthorizedClientController { - private final WebClient webClient; - - public RegisteredOAuth2AuthorizedClientController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/explicit") - String explicit(Model model, @RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { - String body = this.webClient - .get() - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(String.class) - .block(); - model.addAttribute("body", body); - return "response"; - } - - @GetMapping("/implicit") - String implicit(Model model, @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) { - String body = this.webClient - .get() - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(String.class) - .block(); - model.addAttribute("body", body); - return "response"; - } -} diff --git a/samples/boot/oauth2webclient/src/main/resources/application.yml b/samples/boot/oauth2webclient/src/main/resources/application.yml deleted file mode 100644 index 8528a06d0da..00000000000 --- a/samples/boot/oauth2webclient/src/main/resources/application.yml +++ /dev/null @@ -1,21 +0,0 @@ -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - client-id: - client-id: replace-with-client-id - client-secret: replace-with-client-secret - provider: github - scope: read:user,public_repo - -resource-uri: https://api.github.com/user/repos diff --git a/samples/boot/oauth2webclient/src/main/resources/templates/index.html b/samples/boot/oauth2webclient/src/main/resources/templates/index.html deleted file mode 100644 index 07a8e680352..00000000000 --- a/samples/boot/oauth2webclient/src/main/resources/templates/index.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> -<head> - <title>OAuth2 WebClient Showcase</title> - <meta charset="utf-8" /> -</head> -<body> -<a th:href="@{/logout}">Log Out</a> -<h1>Examples</h1> - -<h2>@RegisteredOAuth2AuthorizedClient</h2> -<p> -Examples on RegisteredOAuth2AuthorizedClientController -<h3>Authenticated</h3> -<ul> - <li><a th:href="@{/annotation/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/annotation/implicit}">Implicit</a> - Use the currently logged in user's OAuth Token. This will - only work if the user authenticates with oauth2Login and the token provided is the correct token provided at - log in is authorized.</li> -</ul> -<h3>Public</h3> -<ul> - <li><a th:href="@{/public/annotation/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/public/annotation/implicit}">Implicit</a> - This will fail if the user is not authenticated. - Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then - authenticates with oauth2Login()</li> -</ul> - -<h2>ServletOAuth2AuthorizedClientExchangeFilterFunction</h2> -<p> - Examples on OAuth2WebClientController that demonstrate how to use ServletOAuth2AuthorizedClientExchangeFilterFunction -<h3>Authenticated</h3> -<ul> - <li><a th:href="@{/webclient/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/webclient/implicit}">Implicit</a> - Use the currently logged in user's OAuth Token. This will - only work if the user authenticates with oauth2Login and the token provided is the correct token provided at - log in is authorized.</li> -</ul> -<h3>Public</h3> -<ul> - <li><a th:href="@{/public/webclient/explicit}">Explicit</a> - Explicitly provide a Client Registration Id</li> - <li> - <a th:href="@{/public/webclient/implicit}">Implicit</a> - This will fail if the user is not authenticated. - Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then - authenticates with oauth2Login()</li> -</ul> -</body> -</html> diff --git a/samples/boot/oauth2webclient/src/main/resources/templates/response.html b/samples/boot/oauth2webclient/src/main/resources/templates/response.html deleted file mode 100644 index 210c18c29ae..00000000000 --- a/samples/boot/oauth2webclient/src/main/resources/templates/response.html +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - ~ Copyright 2002-2018 the original author or authors. - ~ - ~ 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 - ~ - ~ https://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. - --> - -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"> -<head> - <title>OAuth2 WebClient Showcase</title> - <meta charset="utf-8" /> -</head> -<body> -<a th:href="@{/}">Back</a> -<h1>Response</h1> -<pre><code id="json" class="json" th:text="${body}"></code></pre> -<script> - json.innerHTML = JSON.stringify(JSON.parse(json.innerHTML), null, 4); -</script> -</body> -</html> diff --git a/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java b/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java deleted file mode 100644 index 4e153b9961c..00000000000 --- a/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * @author Rob Winch - */ -@SpringBootTest -@AutoConfigureMockMvc -@RunWith(SpringRunner.class) -public class OAuth2WebClientApplicationTests { - @Autowired - private MockMvc mockMvc; - - @Test - public void annotationExplicitWhenNotAuthenticatedThenLoginRequested() throws Exception { - this.mockMvc.perform(get("/annotation/explicit")) - .andExpect(status().is3xxRedirection()); - } -} diff --git a/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientControllerTests.java b/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientControllerTests.java deleted file mode 100644 index 8829c004715..00000000000 --- a/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientControllerTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.AfterClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Client; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest -@AutoConfigureMockMvc -@RunWith(SpringRunner.class) -public class OAuth2WebClientControllerTests { - private static MockWebServer web = new MockWebServer(); - - @Autowired - private MockMvc mockMvc; - - @AfterClass - public static void shutdown() throws Exception { - web.shutdown(); - } - - @Test - public void explicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/webclient/explicit") - .with(oauth2Login()) - .with(oauth2Client("client-id"))) - .andExpect(status().isOk()); - } - - @Test - public void implicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/webclient/implicit") - .with(oauth2Login())) - .andExpect(status().isOk()); - } - - @Test - public void publicExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/public/webclient/explicit") - .with(oauth2Client("client-id"))) - .andExpect(status().isOk()); - } - - @Test - public void publicImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/public/webclient/implicit") - .with(oauth2Login())) - .andExpect(status().isOk()); - } - - @TestConfiguration - static class WebClientConfig { - @Bean - WebClient web() { - return WebClient.create(web.url("/").toString()); - } - } -} diff --git a/samples/boot/oauth2webclient/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java b/samples/boot/oauth2webclient/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java deleted file mode 100644 index 91e3a2ec913..00000000000 --- a/samples/boot/oauth2webclient/src/test/java/sample/RegisteredOAuth2AuthorizedClientControllerTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.AfterClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.reactive.function.client.WebClient; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Client; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@WebMvcTest -@AutoConfigureMockMvc -@RunWith(SpringRunner.class) -public class RegisteredOAuth2AuthorizedClientControllerTests { - private static MockWebServer web = new MockWebServer(); - - @Autowired - private MockMvc mockMvc; - - @AfterClass - public static void shutdown() throws Exception { - web.shutdown(); - } - - @Test - public void annotationExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/annotation/explicit") - .with(oauth2Login()) - .with(oauth2Client("client-id"))) - .andExpect(status().isOk()); - } - - @Test - public void annotationImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/annotation/implicit") - .with(oauth2Login())) - .andExpect(status().isOk()); - } - - @Test - public void publicAnnotationExplicitWhenAuthenticatedThenUsesClientIdRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/public/annotation/explicit") - .with(oauth2Client("client-id"))) - .andExpect(status().isOk()); - } - - @Test - public void publicAnnotationImplicitWhenAuthenticatedThenUsesDefaultRegistration() throws Exception { - web.enqueue(new MockResponse().setBody("body").setResponseCode(200)); - this.mockMvc.perform(get("/public/annotation/implicit") - .with(oauth2Login())) - .andExpect(status().isOk()); - } - - @TestConfiguration - static class WebClientConfig { - @Bean - WebClient web() { - return WebClient.create(web.url("/").toString()); - } - } -} diff --git a/samples/boot/saml2login/README.adoc b/samples/boot/saml2login/README.adoc deleted file mode 100644 index d51b1bba4be..00000000000 --- a/samples/boot/saml2login/README.adoc +++ /dev/null @@ -1,38 +0,0 @@ -= SAML 2.0 Login Sample - -This guide provides instructions on setting up this SAML 2.0 Login sample application. - -The sample application uses Spring Boot and the `spring-security-saml2-service-provider` -module which is new in Spring Security 5.2. - -== Goals - -`saml2Login()` provides a very simple implementation of a Service Provider that can receive a SAML 2.0 Response via the HTTP-POST and HTTP-REDIRECT bindings against the SimpleSAMLphp SAML 2.0 reference implementation. - -The following features are implemented in the MVP: - -1. Receive and validate a SAML 2.0 Response containing an assertion, and create a corresponding authentication in Spring Security -2. Send a SAML 2.0 AuthNRequest to an Identity Provider -3. Provide a framework for components used in SAML 2.0 authentication that can be swapped by configuration -4. Work against the SimpleSAMLphp reference implementation - -== Run the Sample - -=== Start up the Sample Boot Application -``` - ./gradlew :spring-security-samples-boot-saml2login:bootRun -``` - -=== Open a Browser - -http://localhost:8080/ - -You will be redirect to the SimpleSAMLphp IDP - -=== Type in your credentials - -``` -User: user -Password: password -``` - diff --git a/samples/boot/saml2login/spring-security-samples-boot-saml2login.gradle b/samples/boot/saml2login/spring-security-samples-boot-saml2login.gradle deleted file mode 100644 index f46ce4a8d98..00000000000 --- a/samples/boot/saml2login/spring-security-samples-boot-saml2login.gradle +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-saml2-service-provider') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-web' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - testCompile project(':spring-security-test') - testCompile 'net.sourceforge.htmlunit:htmlunit' - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/saml2login/src/integration-test/java/sample/Saml2LoginApplicationITests.java b/samples/boot/saml2login/src/integration-test/java/sample/Saml2LoginApplicationITests.java deleted file mode 100644 index c963ab3b2b6..00000000000 --- a/samples/boot/saml2login/src/integration-test/java/sample/Saml2LoginApplicationITests.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import javax.servlet.http.HttpSession; - -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlForm; -import com.gargoylesoftware.htmlunit.html.HtmlInput; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class Saml2LoginApplicationITests { - static final String SIGNED_RESPONSE = "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMS0wMS0yMFQwMTowMzoyNFoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvbG9naW4vc2FtbDIvc3NvL29uZSIgSW5SZXNwb25zZVRvPSJBUlFjOThmMjAwLWRjZjctNGRmNC1hNTIyLTA3MjA2MjA4YjA3ZCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2UyYjU4NTVjZjU5YmEyNDc4OTUyOGRkOGQzZDcyOGRiMGViZjNlNzNiIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+TXJUNS8wdTRScTl3QmMvejFQd2FrNURXZm1xOGlOVk52NldHZWFnZUVzUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+WWg1UFE5cFVBbkkvOW5tNzgxZ040bThTS0U4T1VRTDVOMlI3ZklPZmtHVmMzRjhzNlJlR3hRNWFneUFXYnQzUDRwQ3RWeGtqbk4rMk5KeUw4QmhRMHN0dEovb2JFTHJGUldLemYyYUJaS2NCN0JHTFNtRXdoUFE3N3BHL0psMjBhaDQyaGRyWFc3TE9Ob0VZOHMyY093dm16NkQ2bW9YQWprMHV2UEVTNjhUVndxU2VmT3JwNXV0QmRSQUt6cUJRQ2NQWFJCdnB5NWJ3QkpDL2RKNE5QLzJpalE3N2I3eWhvVDQ0R21hSUduSGo0YVFaeG9kY1JuNU9oQ2hYRk4ydUk2YW1mT0ZYOThjUXZ5KzhDWm9YYUZRMnJmT2dPbGdzbmNGWGMwaXhYK05MSjlvSlJWT2hxRVpjY2JoZ3hPM2hpQ2UwemRuZHloVWxpaGMwdFU2OVlBPT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il9jYjYzYmMzNmMyYzAzYjRlMWJjZDViMWIwY2MyZTE2NWQwNDQ1NDZlODgiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDIxLTAxLTIwVDAxOjAzOjI0WiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vc2ltcGxlc2FtbC1mb3Itc3ByaW5nLXNhbWwuYXBwcy5wY2ZvbmUuaW8vc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfY2I2M2JjMzZjMmMwM2I0ZTFiY2Q1YjFiMGNjMmUxNjVkMDQ0NTQ2ZTg4Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+VWFtN2NHVGlCd2xuRDBJdGd5aU5KVjN2Z0NPNytZZkRxSWJrWERkR3hrQT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+RkhpWUpETDlKTXM1Y2V5WXhUVVgrUndEQm45RFYzVE81dDFham4raGFtb1c2MUpBY0JaNjEwUHpYMzN3alA3Mk1kYmdDWnR5ZmNrSktZUUpPT0szRkxLTkJLQkphOTNsSS9rZWZjTXRTUGxBU2hESm9ydmU0U0tWa29WbzZLVnB0eC9OTnowRkhJNURFZTZiUUVjZWFiNERVNDFVdEpQMHUyWm16ejVjNC83VzhLdmt6MkxMbXhWZlE3Q2todmgvNzBhWHlkWVBVRml3bE4vV1lTV3JYVU9oOXNFTDFiZGVlQzFkYnpaeVdNNldnSkdRMUpJblBnSGd0YTlxMU96eGliOFlLRXpQSUMzVEZldkU1Y0phMFQvd1NzOVIxN0JSR09OclhTTWQvRCt4YkY0Z3lIYW5EZFlOYVN2TzdIS2p4bzRwYk1aY05peDhMTkVYZGtiZEx3PT08L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFRXpDQ0F2dWdBd0lCQWdJSkFJYzFxekxydis1bk1BMEdDU3FHU0liM0RRRUJDd1VBTUlHZk1Rc3dDUVlEVlFRR0V3SlZVekVMTUFrR0ExVUVDQXdDUTA4eEZEQVNCZ05WQkFjTUMwTmhjM1JzWlNCU2IyTnJNUnd3R2dZRFZRUUtEQk5UWVcxc0lGUmxjM1JwYm1jZ1UyVnlkbVZ5TVFzd0NRWURWUVFMREFKSlZERWdNQjRHQTFVRUF3d1hjMmx0Y0d4bGMyRnRiSEJvY0M1alptRndjSE11YVc4eElEQWVCZ2txaGtpRzl3MEJDUUVXRVdab1lXNXBhMEJ3YVhadmRHRnNMbWx2TUI0WERURTFNREl5TXpJeU5EVXdNMW9YRFRJMU1ESXlNakl5TkRVd00xb3dnWjh4Q3pBSkJnTlZCQVlUQWxWVE1Rc3dDUVlEVlFRSURBSkRUekVVTUJJR0ExVUVCd3dMUTJGemRHeGxJRkp2WTJzeEhEQWFCZ05WQkFvTUUxTmhiV3dnVkdWemRHbHVaeUJUWlhKMlpYSXhDekFKQmdOVkJBc01Ba2xVTVNBd0hnWURWUVFEREJkemFXMXdiR1Z6WVcxc2NHaHdMbU5tWVhCd2N5NXBiekVnTUI0R0NTcUdTSWIzRFFFSkFSWVJabWhoYm1sclFIQnBkbTkwWVd3dWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDNGNuNjJFMXhMcXBOMzRQbWJyS0Jia09YRmp6V2dKOWIrcFh1YVJmdDZBMzM5dXVJUWVvZUg1cWVTS1JWVGwzMkwwZ2R6Mlppdkx3WlhXK2NxdmZ0VlcxdHZFSHZ6SkZ5eGVUVzNmQ1VlQ1FzZWJMbkEycVJhMDdSa3hUbzZOZjI0NG1XV1JEb2Rjb0hFZkRVU2J4ZlRaNklFeFNvalNJVTJSbkQ2V2xsWVdGZEQxR0ZwQkpPbVFCOHJBYzh3SklCZEhGZFFuWDhUdGw3aFo2cnRncUVZTXpZVk11SjJGMnIxSFNVMXpTQXZ3cGRZUDZyUkdGUkpFZmRBOW1tM1dLZk5MU2M1Y2xqejBYL1RYeTB2VmxBVjk1bDlxY2ZGelBtcmtOSXN0OUZaU3dwdkI0OUx5QVZrZTA0RlFQUHdMZ1ZINGdwaGlKSDNqdlo3SStKNWxTOFZBZ01CQUFHalVEQk9NQjBHQTFVZERnUVdCQlRUeVA2Q2M1SGxCSjUrdWNWQ3dHYzVvZ0tOR3pBZkJnTlZIU01FR0RBV2dCVFR5UDZDYzVIbEJKNSt1Y1ZDd0djNW9nS05HekFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXZNUzRFUWVQL2lwVjRqT0c1bE82L3RZQ2IvaUplQWR1T25SaGtKazBEYlgzMjlsRExaaFRUTC94L3cvOW11Q1ZjdkxyekVwNlBOK1ZXZnc1RTVGV3RaTjB5aEd0UDlSK3ZabnJWK29jMnpHRCtubzEveVNGT2UzRWlKQ081ZGVoeEtqWUVtQlJ2NXNVL0xaRktacG96S04vQk1FYTZDcUx1eGJ6Yjd5a3hWcjdFVkZYd2x0UHh6RTlUbUw5T0FDTk55RjVlSkhXTVJNbGxhclV2a2NYbGg0cHV4NGtzOWU2elY5RFFCeTJ6ZHM5ZjFJM3F4ZzBlWDZKbkdyWGkvWmlDVCtsSmdWZTNaRlhpZWppTEFpS0IwNHNYVzN0aTBMVzNseDEzWTFZbFE0L3RscGdUZ2ZJSnhLVjZueVBpTG9LMG55d2JNZCt2cEFpckR0Mk9jK2hrPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQgU1BOYW1lUXVhbGlmaWVyPSJodHRwOi8vbG9jYWxob3N0OjgwODAvc2FtbDIvc2VydmljZS1wcm92aWRlci1tZXRhZGF0YS9vbmUiIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj50ZXN0dXNlckBzcHJpbmcuc2VjdXJpdHkuc2FtbDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiIFJlY2lwaWVudD0iaHR0cDovL2xvY2FsaG9zdDo4MDgwL2xvZ2luL3NhbWwyL3Nzby9vbmUiIEluUmVzcG9uc2VUbz0iQVJRYzk4ZjIwMC1kY2Y3LTRkZjQtYTUyMi0wNzIwNjIwOGIwN2QiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAyMS0wMS0yMFQwMTowMjo1NFoiIE5vdE9uT3JBZnRlcj0iMjA1Mi0wOS0yOFQwMjo1MDowNFoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbWwyL3NlcnZpY2UtcHJvdmlkZXItbWV0YWRhdGEvb25lPC9zYW1sOkF1ZGllbmNlPjwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDpDb25kaXRpb25zPjxzYW1sOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyMS0wMS0yMFQwMDo0ODoyOVoiIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwMjEtMDEtMjBUMDg6NDg6MjlaIiBTZXNzaW9uSW5kZXg9Il9lN2ExYTllNDk1YmZlMjI2NjQ5ZThkY2MzN2UxNDE1NDQ5NTIxNWQ2ZWIiPjxzYW1sOkF1dGhuQ29udGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpBdXRoblN0YXRlbWVudD48c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlIE5hbWU9InVpZCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZWR1UGVyc29uQWZmaWxpYXRpb24iIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPm1lbWJlcjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj51c2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9ImVtYWlsQWRkcmVzcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dGVzdHVzZXJAc3ByaW5nLnNlY3VyaXR5LnNhbWw8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4="; - - static final Map<String, List<Object>> USER_ATTRIBUTES = new LinkedHashMap<>(); - - static { - USER_ATTRIBUTES.put("uid", Arrays.asList("testuser@spring.security.saml")); - USER_ATTRIBUTES.put("eduPersonAffiliation", Arrays.asList("member", "user")); - USER_ATTRIBUTES.put("emailAddress", Arrays.asList("testuser@spring.security.saml")); - } - - @Autowired - MockMvc mvc; - - @Autowired - WebClient webClient; - - @Test - public void indexWhenSamlResponseThenShowsUserInformation() throws Exception { - HttpSession session = this.mvc.perform(get("http://localhost:8080/")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost:8080/saml2/authenticate/one")) - .andReturn() - .getRequest().getSession(); - - this.mvc.perform(post("http://localhost:8080/login/saml2/sso/one") - .param("SAMLResponse", SIGNED_RESPONSE) - .session((MockHttpSession) session)) - .andExpect(redirectedUrl("http://localhost:8080/")); - - this.mvc.perform(get("http://localhost:8080/") - .session((MockHttpSession) session)) - .andExpect(model().attribute("emailAddress", "testuser@spring.security.saml")) - .andExpect(model().attribute("userAttributes", USER_ATTRIBUTES)); - } - - @Test - public void authenticationAttemptWhenValidThenShowsUserEmailAddress() throws Exception { - HtmlPage assertingParty = this.webClient.getPage("/"); - HtmlForm form = assertingParty.getFormByName("f"); - HtmlInput username = form.getInputByName("username"); - HtmlInput password = form.getInputByName("password"); - HtmlSubmitInput submit = assertingParty.getHtmlElementById("submit_button"); - username.setValueAttribute("user"); - password.setValueAttribute("password"); - HtmlPage relyingParty = submit.click(); - assertThat(relyingParty.asText()) - .contains("You're email address is testuser@spring.security.saml"); - } -} diff --git a/samples/boot/saml2login/src/main/java/sample/IndexController.java b/samples/boot/saml2login/src/main/java/sample/IndexController.java deleted file mode 100644 index 8da3c251eb8..00000000000 --- a/samples/boot/saml2login/src/main/java/sample/IndexController.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class IndexController { - - @GetMapping("/") - public String index(Model model, - @AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) { - String emailAddress = principal.getFirstAttribute("emailAddress"); - model.addAttribute("emailAddress", emailAddress); - model.addAttribute("userAttributes", principal.getAttributes()); - return "index"; - } -} diff --git a/samples/boot/saml2login/src/main/java/sample/Saml2LoginApplication.java b/samples/boot/saml2login/src/main/java/sample/Saml2LoginApplication.java deleted file mode 100644 index 08202d28def..00000000000 --- a/samples/boot/saml2login/src/main/java/sample/Saml2LoginApplication.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Saml2LoginApplication { - - public static void main(String[] args) { - SpringApplication.run(Saml2LoginApplication.class, args); - } - -} diff --git a/samples/boot/saml2login/src/main/java/sample/SecurityConfig.java b/samples/boot/saml2login/src/main/java/sample/SecurityConfig.java deleted file mode 100644 index 434cf366681..00000000000 --- a/samples/boot/saml2login/src/main/java/sample/SecurityConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; - -@Configuration -public class SecurityConfig { - @Bean - RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { - RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations - .fromMetadataLocation("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php") - .registrationId("one") - .build(); - return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration); - } -} diff --git a/samples/boot/saml2login/src/main/resources/application.yml b/samples/boot/saml2login/src/main/resources/application.yml deleted file mode 100644 index 8b137891791..00000000000 --- a/samples/boot/saml2login/src/main/resources/application.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/samples/boot/saml2login/src/main/resources/templates/index.html b/samples/boot/saml2login/src/main/resources/templates/index.html deleted file mode 100644 index b72bb58ce00..00000000000 --- a/samples/boot/saml2login/src/main/resources/templates/index.html +++ /dev/null @@ -1,46 +0,0 @@ -<!-- - ~ Copyright 2002-2020 the original author or authors. - ~ - ~ 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 - ~ - ~ https://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. - --> - -<!doctype html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> -<head> - <title>Spring Security - SAML 2.0 Login</title> - <meta charset="utf-8" /> - <style> - span, dt { - font-weight: bold; - } - </style> -</head> -<body> - <div> - <form th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - <a href="https://simplesaml-for-spring-saml.apps.pcfone.io/module.php/core/authenticate.php?as=example-userpass&logout"> - Log out of SimpleSAMLphp - </a> - </div> - <h1>SAML 2.0 Login with Spring Security</h1> - <p>You are successfully logged in as <span sec:authentication="name"></span></p> - <p>You're email address is <span th:text="${emailAddress}"></span></p> - <h2>All Your Attributes</h2> - <dl th:each="userAttribute : ${userAttributes}"> - <dt th:text="${userAttribute.key}"></dt> - <dd th:text="${userAttribute.value}"></dd> - </dl> -</body> -</html> diff --git a/samples/boot/webflux-form/spring-security-samples-boot-webflux-form.gradle b/samples/boot/webflux-form/spring-security-samples-boot-webflux-form.gradle deleted file mode 100644 index 35d9a28149a..00000000000 --- a/samples/boot/webflux-form/spring-security-samples-boot-webflux-form.gradle +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-thymeleaf' - compile 'org.springframework.boot:spring-boot-starter-webflux' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'io.projectreactor:reactor-test' - testCompile 'org.skyscreamer:jsonassert' - testCompile 'org.springframework:spring-test' - - integrationTestCompile seleniumDependencies -} - diff --git a/samples/boot/webflux-form/src/integration-test/java/sample/WebfluxFormApplicationTests.java b/samples/boot/webflux-form/src/integration-test/java/sample/WebfluxFormApplicationTests.java deleted file mode 100644 index c9062faa59d..00000000000 --- a/samples/boot/webflux-form/src/integration-test/java/sample/WebfluxFormApplicationTests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.test.context.junit4.SpringRunner; - -import com.gargoylesoftware.htmlunit.BrowserVersion; - -import sample.webdriver.IndexPage; -import sample.webdriver.LoginPage; - -/** - * @author Rob Winch - * @since 5.0 - */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class WebfluxFormApplicationTests { - WebDriver driver; - - @LocalServerPort - int port; - - @Before - public void setup() { - this.driver = new HtmlUnitDriver(BrowserVersion.CHROME); - } - - @Test - public void loginWhenInvalidUsernameThenError() { - LoginPage login = IndexPage.to(this.driver, this.port, LoginPage.class); - login.assertAt(); - - login - .loginForm() - .username("invalid") - .password("password") - .submit(LoginPage.class) - .assertError(); - } - - @Test - public void loginAndLogout() { - LoginPage login = IndexPage.to(this.driver, this.port, LoginPage.class); - login.assertAt(); - - IndexPage index = login - .loginForm() - .username("user") - .password("password") - .submit(IndexPage.class); - index.assertAt(); - - login = index.logout(); - login - .assertAt() - .assertLogout(); - } -} diff --git a/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/IndexPage.java b/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/IndexPage.java deleted file mode 100644 index 9ca07b68f70..00000000000 --- a/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/IndexPage.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample.webdriver; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Rob Winch - * @since 5.0 - */ -public class IndexPage { - - private WebDriver driver; - - private WebElement logout; - - public IndexPage(WebDriver webDriver) { - this.driver = webDriver; - } - - public static <T> T to(WebDriver driver, int port, Class<T> page) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, page); - } - - public IndexPage assertAt() { - assertThat(this.driver.getTitle()).isEqualTo("Secured"); - return this; - } - - public LoginPage logout() { - this.logout.click(); - return LoginPage.create(this.driver); - } -} diff --git a/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/LoginPage.java b/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/LoginPage.java deleted file mode 100644 index 10585338660..00000000000 --- a/samples/boot/webflux-form/src/integration-test/java/sample/webdriver/LoginPage.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample.webdriver; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Rob Winch - * @since 5.0 - */ -public class LoginPage { - - private WebDriver driver; - @FindBy(css = "div[role=alert]") - private WebElement alert; - - private LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.driver = webDriver; - this.loginForm = PageFactory.initElements(webDriver, LoginForm.class); - } - - static LoginPage create(WebDriver driver) { - return PageFactory.initElements(driver, LoginPage.class); - } - - public LoginPage assertAt() { - assertThat(this.driver.getTitle()).isEqualTo("Please Log In"); - return this; - } - - public LoginPage assertError() { - assertThat(this.alert.getText()).isEqualTo("Invalid username and password."); - return this; - } - - public LoginPage assertLogout() { - assertThat(this.alert.getText()).isEqualTo("You have been logged out."); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver driver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver driver) { - this.driver = driver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public <T> T submit(Class<T> page) { - this.submit.click(); - return PageFactory.initElements(this.driver, page); - } - } -} diff --git a/samples/boot/webflux-form/src/main/java/sample/IndexController.java b/samples/boot/webflux-form/src/main/java/sample/IndexController.java deleted file mode 100644 index d84301d78b9..00000000000 --- a/samples/boot/webflux-form/src/main/java/sample/IndexController.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Rob Winch - * @since 5.0 - */ -@Controller -public class IndexController { - - @GetMapping("/") - public String index() { - return "index"; - } - - @GetMapping("/login") - public String login() { - return "login"; - } -} diff --git a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormApplication.java b/samples/boot/webflux-form/src/main/java/sample/WebfluxFormApplication.java deleted file mode 100644 index 48ddabcdb18..00000000000 --- a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * @author Rob Winch - * @since 5.0 - */ -@SpringBootApplication -public class WebfluxFormApplication { - - public static void main(String[] args) { - SpringApplication.run(WebfluxFormApplication.class, args); - } -} diff --git a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java b/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java deleted file mode 100644 index 94642742dce..00000000000 --- a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Rob Winch - * @since 5.0 - */ -@EnableWebFluxSecurity -public class WebfluxFormSecurityConfig { - - @Bean - public MapReactiveUserDetailsService userDetailsService() { - UserDetails user = User.withDefaultPasswordEncoder() - .username("user") - .password("password") - .roles("USER") - .build(); - return new MapReactiveUserDetailsService(user); - } - - @Bean - SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { - http - .authorizeExchange((exchanges) -> - exchanges - .pathMatchers("/login").permitAll() - .anyExchange().authenticated() - ) - .httpBasic(withDefaults()) - .formLogin((formLogin) -> - formLogin - .loginPage("/login") - ); - return http.build(); - } -} diff --git a/samples/boot/webflux-form/src/main/resources/logback.xml b/samples/boot/webflux-form/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/boot/webflux-form/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/boot/webflux-form/src/main/resources/templates/index.html b/samples/boot/webflux-form/src/main/resources/templates/index.html deleted file mode 100644 index 992450d43f5..00000000000 --- a/samples/boot/webflux-form/src/main/resources/templates/index.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <title>Secured</title> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -</head> -<body> -<div class="container"> - <h1>Secured</h1> - - <form th:action="@{/logout}" method="post"> - <input id="logout" type="submit" value="Log Out"/> - </form> -</div> -</body> -</html> diff --git a/samples/boot/webflux-form/src/main/resources/templates/login.html b/samples/boot/webflux-form/src/main/resources/templates/login.html deleted file mode 100644 index 4fa5c65688d..00000000000 --- a/samples/boot/webflux-form/src/main/resources/templates/login.html +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <meta name="description" content=""> - <meta name="author" content=""> - <title>Please Log In</title> - <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> - <link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/> -</head> -<body> -<div class="container"> - <form class="form-signin" method="post" th:action="@{/login}"> - <h2 class="form-signin-heading">Please Log In</h2> - <div th:if="${param.error}" class="alert alert-danger" role="alert">Invalid - username and password.</div> - <div th:if="${param.logout}" class="alert alert-success" role="alert">You - have been logged out.</div> - <p> - <label for="username" class="sr-only">Username</label> - <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus> - </p> - <p> - <label for="password" class="sr-only">Password</label> - <input type="password" id="password" name="password" class="form-control" placeholder="Password" required> - </p> - <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> - </form> -</div> -</body> -</html> diff --git a/samples/boot/webflux-x509/spring-security-samples-boot-webflux-x509.gradle b/samples/boot/webflux-x509/spring-security-samples-boot-webflux-x509.gradle deleted file mode 100644 index 57196d2b2bd..00000000000 --- a/samples/boot/webflux-x509/spring-security-samples-boot-webflux-x509.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-boot' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'org.springframework.boot:spring-boot-starter-webflux' - - testCompile project(':spring-security-test') - testCompile 'org.springframework.boot:spring-boot-starter-test' -} diff --git a/samples/boot/webflux-x509/src/main/java/sample/MeController.java b/samples/boot/webflux-x509/src/main/java/sample/MeController.java deleted file mode 100644 index 7c10c6aedfd..00000000000 --- a/samples/boot/webflux-x509/src/main/java/sample/MeController.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Mono; - -/** - * @author Alexey Nesterov - * @since 5.2 - */ -@RestController -@RequestMapping("/me") -public class MeController { - - @GetMapping - public Mono<String> me() { - return ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .map((authentication) -> "Hello, " + authentication.getName()); - } -} diff --git a/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java b/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java deleted file mode 100644 index 89813d073b9..00000000000 --- a/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.ReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.web.server.SecurityWebFilterChain; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * @author Alexey Nesterov - * @since 5.2 - */ -@SpringBootApplication -public class WebfluxX509Application { - - @Bean - public ReactiveUserDetailsService reactiveUserDetailsService() { - return new MapReactiveUserDetailsService( - User.withUsername("client").password("").authorities("ROLE_USER").build() - ); - } - - @Bean - public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { - // @formatter:off - http - .x509(withDefaults()) - .authorizeExchange((exchanges) -> - exchanges - .anyExchange().authenticated() - ); - // @formatter:on - - return http.build(); - } - - public static void main(String[] args) { - SpringApplication.run(WebfluxX509Application.class); - } -} diff --git a/samples/boot/webflux-x509/src/main/resources/application.yml b/samples/boot/webflux-x509/src/main/resources/application.yml deleted file mode 100644 index cdeb85fa7be..00000000000 --- a/samples/boot/webflux-x509/src/main/resources/application.yml +++ /dev/null @@ -1,8 +0,0 @@ -server: - port: 8443 - ssl: - key-store: 'classpath:./certs/server.p12' - key-store-password: 'password' - client-auth: need - trust-store: 'classpath:./certs/server.p12' - trust-store-password: 'password' diff --git a/samples/boot/webflux-x509/src/main/resources/certs/curl_app.sh b/samples/boot/webflux-x509/src/main/resources/certs/curl_app.sh deleted file mode 100644 index dbf7af4b43d..00000000000 --- a/samples/boot/webflux-x509/src/main/resources/certs/curl_app.sh +++ /dev/null @@ -1,2 +0,0 @@ - curl -vvvv --cacert out/DevCA.crt --cert out/localhost.crt --key out/localhost.key https://localhost:8443/me - diff --git a/samples/boot/webflux-x509/src/main/resources/certs/server.p12 b/samples/boot/webflux-x509/src/main/resources/certs/server.p12 deleted file mode 100644 index ec536b2f600..00000000000 Binary files a/samples/boot/webflux-x509/src/main/resources/certs/server.p12 and /dev/null differ diff --git a/samples/boot/webflux-x509/src/test/java/sample/WebfluxX509ApplicationTest.java b/samples/boot/webflux-x509/src/test/java/sample/WebfluxX509ApplicationTest.java deleted file mode 100644 index e6ed9cf200c..00000000000 --- a/samples/boot/webflux-x509/src/test/java/sample/WebfluxX509ApplicationTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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 sample; - -import io.netty.handler.ssl.ClientAuth; -import io.netty.handler.ssl.SslContextBuilder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.http.client.reactive.ReactorClientHttpConnector; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; -import reactor.netty.http.client.HttpClient; - -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableEntryException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import static org.assertj.core.api.Assertions.assertThat; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class WebfluxX509ApplicationTest { - - @LocalServerPort - int port; - - @Test - public void shouldExtractAuthenticationFromCertificate() throws Exception { - WebTestClient webTestClient = createWebTestClientWithClientCertificate(); - webTestClient - .get().uri("/me") - .exchange() - .expectStatus().isOk() - .expectBody() - .consumeWith((result) -> { - String responseBody = new String(result.getResponseBody()); - assertThat(responseBody).contains("Hello, client"); - }); - } - - private WebTestClient createWebTestClientWithClientCertificate() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException { - ClassPathResource serverKeystore = new ClassPathResource("/certs/server.p12"); - - KeyStore keyStore = KeyStore.getInstance("PKCS12"); - keyStore.load(serverKeystore.getInputStream(), "password".toCharArray()); - - X509Certificate devCA = (X509Certificate) keyStore.getCertificate("DevCA"); - - X509Certificate clientCrt = (X509Certificate) keyStore.getCertificate("client"); - KeyStore.Entry keyStoreEntry = keyStore.getEntry("client", - new KeyStore.PasswordProtection("password".toCharArray())); - PrivateKey clientKey = ((KeyStore.PrivateKeyEntry) keyStoreEntry).getPrivateKey(); - - SslContextBuilder sslContextBuilder = SslContextBuilder - .forClient().clientAuth(ClientAuth.REQUIRE) - .trustManager(devCA) - .keyManager(clientKey, clientCrt); - - HttpClient httpClient = HttpClient.create().secure((sslContextSpec) -> sslContextSpec.sslContext(sslContextBuilder)); - ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpClient); - - return WebTestClient - .bindToServer(httpConnector) - .baseUrl("https://localhost:" + port) - .build(); - } -} diff --git a/samples/certificates/Readme.txt b/samples/certificates/Readme.txt deleted file mode 100644 index 64b415cf83c..00000000000 --- a/samples/certificates/Readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -This directory contains certificates and keys for use with SSL in the sample applications. Certificates are issued by -our "Spring Security Test CA" certificate authority. - -ca.pem - the certificate authority's certificate. -server.jks - Java keystore containing the server certificate and privatekey. It Also contains the certificate authority - file and this is used as both keystore and truststore for they jetty server when running the samples with - the maven jetty plugin ("mvn jetty:run"). - -rod.p12, dianne.p12, scott.p12 are all certificate/key combinations for client authentication and can be installed in -your browser if you want to try out support for X.509 authentication. \ No newline at end of file diff --git a/samples/certificates/ca.pem b/samples/certificates/ca.pem deleted file mode 100644 index a5b52ca9d7e..00000000000 --- a/samples/certificates/ca.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDojCCAoqgAwIBAgIEMKX1dzANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMC -R0IxETAPBgNVBAgTCFNjb3RsYW5kMRAwDgYDVQQHEwdHbGFzZ293MRkwFwYDVQQK -ExBTcHJpbmcgRnJhbWV3b3JrMRgwFgYDVQQLEw9TcHJpbmcgU2VjdXJpdHkxIDAe -BgNVBAMTF1NwcmluZyBTZWN1cml0eSBUZXN0IENBMB4XDTA4MDEyNTExMTIyMVoX -DTE4MDIyNTAwMDAwMFowgYkxCzAJBgNVBAYTAkdCMREwDwYDVQQIEwhTY290bGFu -ZDEQMA4GA1UEBxMHR2xhc2dvdzEZMBcGA1UEChMQU3ByaW5nIEZyYW1ld29yazEY -MBYGA1UECxMPU3ByaW5nIFNlY3VyaXR5MSAwHgYDVQQDExdTcHJpbmcgU2VjdXJp -dHkgVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzl/wEe -snYrwqaGZuB8hmwACtptazh1+eXCfd66FkioxlLF7yTnjCC7DT+vmMgSuThIEIsN -xlxLpEgyU3bU8GIuR8wyYIyvuSMcptdFJLV7NKYuRycxpDuqimTM7Br0nfNgKVEv -1QwguGWr6YN3aZ68/xe/D5xyPhakKu++7VFXIXw9f0+nqojdrFTqQ6l9GAVRgfX6 -h4JOaV1VFx83y2pnFj0iFneVxRcvXyWnyXlcOvJDIyVuyS/hYxb+E5rtBvp5XQ0o -5CP4OMwCZGx/jEqlL8oO7BwEgu9aEBxKvoIKJmHDTHgWIxgawTrKabmong4utnMI -yNrhsI77bmh2U7UCAwEAAaMQMA4wDAYDVR0PBAUDAwcGADANBgkqhkiG9w0BAQUF -AAOCAQEAuD8W9Ukkfyi0y65mwguFVAqBC3RSTMRXcjbLQV4rMDM/Q9kjA6acY4Ta -WgxGTwNCydqaqwDVsmn+6Je8Lp2xm9KLDLypVdNopGs+Mlfo55dhwqymXkQw1oJI -CPhR3nBmGEnSWW0UY9bPlpxRF2D5GDVwpuxDtXvWa4baPwRRI9MxwPWHA3ITl+fc -s9QVKy+pRAnuP9MSIp755cJ1CODOn2ElNCqnxxsZmcWcmI3LkHAwTmegl3PVvhrk -MKMEA/neshh/M/hWGNTFt77Hoa7pU9dv5RCWFvZPqsUgPrwGrmUvcmSDir3lSWQm -SuSED2LKVo+BFqwWS+jp49AR9b8B/Q== ------END CERTIFICATE----- diff --git a/samples/certificates/dianne.p12 b/samples/certificates/dianne.p12 deleted file mode 100755 index 6e5ba218db7..00000000000 Binary files a/samples/certificates/dianne.p12 and /dev/null differ diff --git a/samples/certificates/localhost-with-ca/ca.crt b/samples/certificates/localhost-with-ca/ca.crt deleted file mode 100644 index 3371be56578..00000000000 --- a/samples/certificates/localhost-with-ca/ca.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB9TCCAV4CCQCmYJRrKq63RDANBgkqhkiG9w0BAQUFADA+MQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExEDAOBgNVBAoMB1Bpdm90YWwxEDAOBgNVBAMMB1Bpdm90 -YWwwIBcNMTMwODAxMTQzNTMyWhgPMjExMzA3MDgxNDM1MzJaMD4xCzAJBgNVBAYT -AlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHUGl2b3RhbDEQMA4GA1UEAwwHUGl2 -b3RhbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArcTaLxERklIzLRqWwvZK -arXaePSnq+U0fLmhlw6i9cn67vUGbUzjOkeezPf/fWVHK23bdZxbxTQHJh4g5gw5 -o80RYs7tfGuYJNF2EomAGg83TaqjttF3HW1Ewf2rvAJdfyQyMUS8CxxJeRDMYb9+ -jYE0g5A4oRgzNgYSinjB2M8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAN60FcUgjK -/FXKCOxJ81Y2HG2TE9n237uGKtXs/D2VEv6rogEqRTWBs8VvErH5dgOwmUHWo7Ys -UloaPWrMfEQ/MuQDHknVItUK1fmHxAhje4WsmX2vSnGLLeoWiL92DnO/E10tbMoI -Is0A7KS2r3FAoIKrMYZNkGhMYpV2aEbSKg== ------END CERTIFICATE----- diff --git a/samples/certificates/localhost-with-ca/ca.csr b/samples/certificates/localhost-with-ca/ca.csr deleted file mode 100644 index 839d47c4c16..00000000000 --- a/samples/certificates/localhost-with-ca/ca.csr +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBfTCB5wIBADA+MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEDAOBgNVBAoM -B1Bpdm90YWwxEDAOBgNVBAMMB1Bpdm90YWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0A -MIGJAoGBAK3E2i8REZJSMy0alsL2Smq12nj0p6vlNHy5oZcOovXJ+u71Bm1M4zpH -nsz3/31lRytt23WcW8U0ByYeIOYMOaPNEWLO7XxrmCTRdhKJgBoPN02qo7bRdx1t -RMH9q7wCXX8kMjFEvAscSXkQzGG/fo2BNIOQOKEYMzYGEop4wdjPAgMBAAGgADAN -BgkqhkiG9w0BAQUFAAOBgQAONY6xNj0ODLnb4sWdARQqmHt1yic0GRbN3GXTBVuA -IJ+tUF9OykTSqf5IzWpJL/7ATQFnTMW3qJ8e0sSn61QU7yKHlBHHLCy92mTV5Lq/ -CIe1uoC5dHaNe7HMfFouHBjydAnn9vlkvvu781xhS8VXoRgYt3Vi5edQ6AIZFf58 -CA== ------END CERTIFICATE REQUEST----- diff --git a/samples/certificates/localhost-with-ca/ca.key b/samples/certificates/localhost-with-ca/ca.key deleted file mode 100644 index 547c189f332..00000000000 --- a/samples/certificates/localhost-with-ca/ca.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQCtxNovERGSUjMtGpbC9kpqtdp49Ker5TR8uaGXDqL1yfru9QZt -TOM6R57M9/99ZUcrbdt1nFvFNAcmHiDmDDmjzRFizu18a5gk0XYSiYAaDzdNqqO2 -0XcdbUTB/au8Al1/JDIxRLwLHEl5EMxhv36NgTSDkDihGDM2BhKKeMHYzwIDAQAB -AoGBAITgykNBlyE/8FhmQ7EUazWMmGL0Gx+MZwWVYebET4MKj/OYtPBx4jSXvexZ -HCsEflbMbAxUo6x6K5lPYrLn2E6RxN3TFdlzG+lApuUi24oDON++p0Xa52aKQ6Ye -JSZLZZyDAUDN/byDgnf5BqnFwjJuv9tRdnguTB3W17uRqRrZAkEA3KarWpAKp0z5 -BiPZubNx9D94uwJVK4AaOIrdHZGpHe4qKkkIk7eZ95kopmavm5EsQBiRnTymWHJL -M+LU77i85QJBAMmbdN5mDd66HDdewjb3o125Kfcedu74gHoxOdeMnSZCop0GBtKQ -yeCi/pQzLm/wLaFwZ5NBurfipsY0YD71F6MCQQDEzO8mnjSyVWgCvvURuWhY9kej -XIhEfURlzA09s05IgMUg4/T/c5GjEfr8t7fHJCt4m7E8sfyYBJDonVdY3Me1AkEA -hptZ64e+KQCgCEQnbiXnmJMhttJLXIDk3zDwyr8iycHh6u90LLDpaSfKzE5j6e81 -uD1hmktfjJky+tFLlZ10+wJAVbart8oKuoNL7/J3TlDnk/ibOxiUrpGQ8GyEtUyY -/tnF0aeVhxfKaaOJy9E2wAaJ3ySqAvuuO5FYAo9sTu+KfQ== ------END RSA PRIVATE KEY----- diff --git a/samples/certificates/localhost-with-ca/ca.key.org b/samples/certificates/localhost-with-ca/ca.key.org deleted file mode 100644 index e04bb194f8d..00000000000 --- a/samples/certificates/localhost-with-ca/ca.key.org +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,102D7604E722947D - -lyUWkOAgjDtEJbc3/EvenlBpRN3T4N8lb2NNJu7cr6V03sgfayE/5yFk046GA4JV -J0JcbU913KN0iQy+oQMcqsMPRSUGmF4rNw2zCWGjVcfva/bHHr5HzKRZEGuwz4A1 -VD5tv0bZoL1XoZleFT3WQ0kquY/jHHSP/N5mzIBvv25E5ILo04hrVEqbOB4l2hby -MRPL4deWFZ2aW9RF4TuNVlI3FkWCseNAIj7Go5SfxyZ+s37DwHfHdil8U68AxSjf -1ccjAAzwLEv8I4UzXKn0P8OsnPb3WWuldEMAHFp6CshHRHHEPbe9bfseOE3ZHnq6 -YZ7qu8BWtHyKSYLtUh7axY6JRHc4fT5LXVly8aVLSFjqfjyM4de3qZ5SpTWnxPlR -8OZuu+pcqbNqTJtNB6R9j4GYWSxIi3blq3D1LVtxUh+wmxZxbK3UZOHAzWWFIojX -INHyfVXu70tIQKGoyCwLVWyArHIYzt7ZZF/Sa0lwbZRJnJGCGf1b2+bIX9AsUbWC -Zmi+Yu9hMtdzdhqsmRnnrEkH+yhsx+w6q4UuoPv0sPFTD3PXlk1cJtKGDbOjRzsQ -dAcShQFJK+z59LqPkhi76tBnQ8/LTJZHLdTZc/pL8myGT8rxYwkr05kM5fQu8+SJ -qIzcnm830fGr4eFJsJ24KKB38yUnNdtWOkeeSe80Tm1uLYd+ZbcWMVNdoESG4KQV -VURDP4zhZx2d0/4VCPr13USoQJ7En4qRvdb8vAsNpXZga9eDMcykeRDY6Szb3K5C -tQhX1pawMDNaHAMAAKGMvH9mm6D7gA4RlKc8LFgol6o22piuFhtHWA== ------END RSA PRIVATE KEY----- diff --git a/samples/certificates/localhost-with-ca/generate.sh b/samples/certificates/localhost-with-ca/generate.sh deleted file mode 100644 index 39506dab093..00000000000 --- a/samples/certificates/localhost-with-ca/generate.sh +++ /dev/null @@ -1,13 +0,0 @@ -openssl genrsa -des3 -passout pass:changeit -out ca.key 1024 -openssl req -new -passin pass:changeit -key ca.key -out ca.csr -cp ca.key ca.key.org -openssl rsa -in ca.key.org -passin pass:changeit -out ca.key -openssl x509 -req -days 36500 -passin pass:changeit -in ca.csr -signkey ca.key -out ca.crt - -keytool -genkey -storepass changeit -alias tomcat -keyalg RSA -keytool -storepass changeit -alias tomcat -certreq -file tomcat.csr -echo 02 > serial.txt -openssl x509 -CA ca.crt -passin pass:changeit -CAkey ca.key -CAserial serial.txt -req -in tomcat.csr -out tomcat.cer -days 36500 -rm serial.txt -keytool -storepass changeit -import -alias ca -file ca.crt -keytool -storepass changeit -import -alias tomcat -file tomcat.cer diff --git a/samples/certificates/localhost-with-ca/tomcat.cer b/samples/certificates/localhost-with-ca/tomcat.cer deleted file mode 100644 index f0569b66020..00000000000 --- a/samples/certificates/localhost-with-ca/tomcat.cer +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICkjCCAfsCAQMwDQYJKoZIhvcNAQEFBQAwPjELMAkGA1UEBhMCVVMxCzAJBgNV -BAgMAkNBMRAwDgYDVQQKDAdQaXZvdGFsMRAwDgYDVQQDDAdQaXZvdGFsMCAXDTEz -MDgwMTE0MzU1MVoYDzIxMTMwNzA4MTQzNTUxWjBfMQswCQYDVQQGEwJVUzELMAkG -A1UECBMCTU8xCzAJBgNVBAcTAktDMREwDwYDVQQKEwhTZWN1cml0eTEPMA0GA1UE -CxMGU3ByaW5nMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCKCyVI5D4h0lWU9D40P0kHkADWONRLQ9o0fcQvJmQaLwJN -tL0KpDiAwXiot4KKZWMGmYKoFtvhnd/t7ybZsODqsImDCZjKKvHaMTpFOyrl9SHW -PT7bvt7jBL47QqL/UJ0bApxVMmLD0DhcOmTkMmzm932F/he9w5z1nIsVl4POX+hQ -yyuuS+AvZdnUr5W2+COsvI1hsibrpnUnIvcXPHmSVrl4kD16OJ3Z/8Baia4mC3sy -pxqXCiBUeWbDsR5s9tZtMOJH7PpDbLsxGR3Zely4xWx7fmn/lW57EFyhMjXWZfUH -pTRsPWlFEYdTdnyM00MWWXrt+Y3kW9mb3w2DCf2fAgMBAAEwDQYJKoZIhvcNAQEF -BQADgYEAR1Y5IIfRrFIKTOc7gx4X2IOyByNdMYfd1+CWnEycUNuFwCE5iBJAbyN6 -yy2kViSFuTHwyJVD49QJNiFXOKZYmE8EFke1Y3nxwwe9MqfeTsrHpYGtpSZwDzv9 -64UM0qOPWxt+P9txQShcokldSt8BZ4iOJ9G6yY5EQdswE6rGkts= ------END CERTIFICATE----- diff --git a/samples/certificates/localhost-with-ca/tomcat.csr b/samples/certificates/localhost-with-ca/tomcat.csr deleted file mode 100644 index e6bbf82116d..00000000000 --- a/samples/certificates/localhost-with-ca/tomcat.csr +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN NEW CERTIFICATE REQUEST----- -MIIC1DCCAbwCAQAwXzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1PMQswCQYDVQQHEwJLQzERMA8G -A1UEChMIU2VjdXJpdHkxDzANBgNVBAsTBlNwcmluZzESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAigslSOQ+IdJVlPQ+ND9JB5AA1jjUS0PaNH3ELyZk -Gi8CTbS9CqQ4gMF4qLeCimVjBpmCqBbb4Z3f7e8m2bDg6rCJgwmYyirx2jE6RTsq5fUh1j0+277e -4wS+O0Ki/1CdGwKcVTJiw9A4XDpk5DJs5vd9hf4XvcOc9ZyLFZeDzl/oUMsrrkvgL2XZ1K+Vtvgj -rLyNYbIm66Z1JyL3Fzx5kla5eJA9ejid2f/AWomuJgt7MqcalwogVHlmw7EebPbWbTDiR+z6Q2y7 -MRkd2XpcuMVse35p/5VuexBcoTI11mX1B6U0bD1pRRGHU3Z8jNNDFll67fmN5FvZm98Ngwn9nwID -AQABoDAwLgYJKoZIhvcNAQkOMSEwHzAdBgNVHQ4EFgQUpQODsrGvfB6TWTCIEEdx0OKB1+QwDQYJ -KoZIhvcNAQELBQADggEBACL6M4Htn6tEebOH8vj3R8cVcgebxshQV/KD7+tWUq2RSno4JndsYxEF -H3Zh3vWhh5Q0nH55s1C/kiKYNP0jQXheeAiH6hatiCpSssgvDnw653ivBgqT3mo8sy1jpw9Pdx7F -6JuCksus+aI9PUKuI3DXXyAxKJfc/JmnnCXsyZz8sVu66bMrIel0kAODN6Da35QohDuStNuplu/R -ZHoiapQi3dxmWctC30fz0y7xqRVbRUKWHE7YWXqtWjFusUjXtZJobMeEb6DLeFfRsJ50OG8kgyZy -TvWQ9kP3ODeDqq74xiy7NxwGH8ytsWEwpSC10Z35vWb++rtl963A2itoK4E= ------END NEW CERTIFICATE REQUEST----- diff --git a/samples/certificates/rod.p12 b/samples/certificates/rod.p12 deleted file mode 100755 index 4cd05644305..00000000000 Binary files a/samples/certificates/rod.p12 and /dev/null differ diff --git a/samples/certificates/scott.p12 b/samples/certificates/scott.p12 deleted file mode 100644 index f0a6357e730..00000000000 Binary files a/samples/certificates/scott.p12 and /dev/null differ diff --git a/samples/certificates/server.jks b/samples/certificates/server.jks deleted file mode 100755 index aaa1119fff4..00000000000 Binary files a/samples/certificates/server.jks and /dev/null differ diff --git a/samples/javaconfig/aspectj/spring-security-samples-javaconfig-aspectj.gradle b/samples/javaconfig/aspectj/spring-security-samples-javaconfig-aspectj.gradle deleted file mode 100644 index 6487a14d436..00000000000 --- a/samples/javaconfig/aspectj/spring-security-samples-javaconfig-aspectj.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample' -apply plugin: 'io.freefair.aspectj.post-compile-weaving' - -repositories { - mavenCentral() -} - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - - aspect project(':spring-security-aspects') - - runtime project(':spring-security-aspects') -} - -aspectj { - version = aspectjVersion -} - diff --git a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/AspectjSecurityConfig.java b/samples/javaconfig/aspectj/src/main/java/sample/aspectj/AspectjSecurityConfig.java deleted file mode 100644 index 038b8271f32..00000000000 --- a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/AspectjSecurityConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.AdviceMode; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; - -/** - * @author Rob Winch - */ -@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, securedEnabled = true) -public class AspectjSecurityConfig { - @Bean - public Service service() { - return new Service(); - } - - @Bean - public SecuredService securedService() { - return new SecuredService(); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication(); - } -} diff --git a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/SecuredService.java b/samples/javaconfig/aspectj/src/main/java/sample/aspectj/SecuredService.java deleted file mode 100644 index 9f81cf17339..00000000000 --- a/samples/javaconfig/aspectj/src/main/java/sample/aspectj/SecuredService.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.springframework.security.access.annotation.Secured; - -/** - * Service which is secured on the class level - * - * @author Mike Wiesner - * @since 3.0 - */ -@Secured("ROLE_USER") -public class SecuredService { - - public void secureMethod() { - // nothing - } - -} diff --git a/samples/javaconfig/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java b/samples/javaconfig/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java deleted file mode 100644 index 344de63e11b..00000000000 --- a/samples/javaconfig/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; - - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import java.lang.reflect.Proxy; - -import static org.assertj.core.api.Assertions.assertThat; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = AspectjSecurityConfig.class) -public class AspectJInterceptorTests { - private Authentication admin = new UsernamePasswordAuthenticationToken("test", "xxx", - AuthorityUtils.createAuthorityList("ROLE_ADMIN")); - private Authentication user = new UsernamePasswordAuthenticationToken("test", "xxx", - AuthorityUtils.createAuthorityList("ROLE_USER")); - - @Autowired - private Service service; - - @Autowired - private SecuredService securedService; - - @Test - public void publicMethod() { - service.publicMethod(); - } - - @Test(expected = AuthenticationCredentialsNotFoundException.class) - public void securedMethodNotAuthenticated() { - service.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void securedMethodWrongRole() { - SecurityContextHolder.getContext().setAuthentication(admin); - service.secureMethod(); - } - - @Test - public void securedMethodEverythingOk() { - SecurityContextHolder.getContext().setAuthentication(user); - service.secureMethod(); - } - - @Test(expected = AuthenticationCredentialsNotFoundException.class) - public void securedClassNotAuthenticated() { - securedService.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void securedClassWrongRole() { - SecurityContextHolder.getContext().setAuthentication(admin); - securedService.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void securedClassWrongRoleOnNewedInstance() { - SecurityContextHolder.getContext().setAuthentication(admin); - new SecuredService().secureMethod(); - } - - @Test - public void securedClassEverythingOk() { - SecurityContextHolder.getContext().setAuthentication(user); - securedService.secureMethod(); - new SecuredService().secureMethod(); - } - - // SEC-2595 - @Test - public void notProxy() { - assertThat(Proxy.isProxyClass(securedService.getClass())).isFalse(); - } - - @After - public void tearDown() { - SecurityContextHolder.clearContext(); - } -} diff --git a/samples/javaconfig/aspectj/src/test/resources/logback-test.xml b/samples/javaconfig/aspectj/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/javaconfig/aspectj/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/concurrency/spring-security-samples-javaconfig-concurrency.gradle b/samples/javaconfig/concurrency/spring-security-samples-javaconfig-concurrency.gradle deleted file mode 100644 index 7a8e417d8ee..00000000000 --- a/samples/javaconfig/concurrency/spring-security-samples-javaconfig-concurrency.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api:3.0.1' - - runtime 'ch.qos.logback:logback-classic' - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index c33dc58cf28..00000000000 --- a/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; -import org.springframework.security.web.session.HttpSessionEventPublisher; - -/** - * We customize {@link AbstractSecurityWebApplicationInitializer} to enable the - * {@link HttpSessionEventPublisher}. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { - - @Override - protected boolean enableHttpSessionEventPublisher() { - return true; - } -} diff --git a/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 45773f3eb77..00000000000 --- a/samples/javaconfig/concurrency/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -import static org.springframework.security.config.Customizer.withDefaults; - -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class SecurityConfig extends WebSecurityConfigurerAdapter { - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER"); - } - // @formatter:on - - // @formatter:off - @Override - protected void configure( - HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .sessionManagement((sessionManagement) -> - sessionManagement - .sessionConcurrency((sessionConcurrency) -> - sessionConcurrency - .maximumSessions(1) - .expiredUrl("/login?expired") - ) - ); - } - // @formatter:on -} diff --git a/samples/javaconfig/concurrency/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/concurrency/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/concurrency/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/data/spring-security-samples-javaconfig-data.gradle b/samples/javaconfig/data/spring-security-samples-javaconfig-data.gradle deleted file mode 100644 index 94680d9b970..00000000000 --- a/samples/javaconfig/data/spring-security-samples-javaconfig-data.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-data') - compile 'javax.validation:validation-api' - compile 'org.eclipse.persistence:javax.persistence' - compile 'org.hibernate:hibernate-entitymanager' - compile 'org.hibernate:hibernate-validator' - compile 'org.hsqldb:hsqldb' - compile 'org.springframework.data:spring-data-jpa' -} diff --git a/samples/javaconfig/data/src/main/java/samples/DataConfig.java b/samples/javaconfig/data/src/main/java/samples/DataConfig.java deleted file mode 100644 index cd401a5df47..00000000000 --- a/samples/javaconfig/data/src/main/java/samples/DataConfig.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.Database; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; -import org.springframework.transaction.PlatformTransactionManager; -import samples.data.Message; - -import javax.sql.DataSource; - -/** - * @author Rob Winch - */ -@Configuration -@ComponentScan -@EnableJpaRepositories -public class DataConfig { - - @Bean - public SecurityEvaluationContextExtension expressionEvaluationContextProvider() { - return new SecurityEvaluationContextExtension(); - } - - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - vendorAdapter.setDatabase(Database.HSQL); - vendorAdapter.setGenerateDdl(true); - - LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); - factory.setJpaVendorAdapter(vendorAdapter); - factory.setPackagesToScan(Message.class.getPackage().getName()); - factory.setDataSource(dataSource()); - - return factory; - } - - @Bean - @DependsOn("entityManagerFactory") - public ResourceDatabasePopulator initDatabase(DataSource dataSource) throws Exception { - ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); - populator.addScript(new ClassPathResource("data.sql")); - populator.populate(dataSource.getConnection()); - return populator; - } - - @Bean - public PlatformTransactionManager transactionManager() { - JpaTransactionManager txManager = new JpaTransactionManager(); - txManager.setEntityManagerFactory(entityManagerFactory().getObject()); - return txManager; - } -} diff --git a/samples/javaconfig/data/src/main/java/samples/data/Message.java b/samples/javaconfig/data/src/main/java/samples/data/Message.java deleted file mode 100644 index 7c567e9c123..00000000000 --- a/samples/javaconfig/data/src/main/java/samples/data/Message.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.data; - -import java.util.Calendar; - -import javax.persistence.*; - -import org.hibernate.validator.constraints.NotEmpty; - -@Entity -public class Message { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @NotEmpty(message = "Message is required.") - private String text; - - @NotEmpty(message = "Summary is required.") - private String summary; - - private Calendar created = Calendar.getInstance(); - - @OneToOne - private User to; - - public User getTo() { - return to; - } - - public void setTo(User to) { - this.to = to; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Calendar getCreated() { - return created; - } - - public void setCreated(Calendar created) { - this.created = created; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } -} diff --git a/samples/javaconfig/data/src/main/java/samples/data/MessageRepository.java b/samples/javaconfig/data/src/main/java/samples/data/MessageRepository.java deleted file mode 100644 index c98a1d3fde5..00000000000 --- a/samples/javaconfig/data/src/main/java/samples/data/MessageRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.data; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -/** - * @author Rob Winch - */ -@Repository -public interface MessageRepository extends JpaRepository<Message, Long> { -} diff --git a/samples/javaconfig/data/src/main/java/samples/data/SecurityMessageRepository.java b/samples/javaconfig/data/src/main/java/samples/data/SecurityMessageRepository.java deleted file mode 100644 index f6b2ac956d4..00000000000 --- a/samples/javaconfig/data/src/main/java/samples/data/SecurityMessageRepository.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.data; - -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * @author Rob Winch - */ -@Repository -public interface SecurityMessageRepository extends MessageRepository { - @Query("select m from Message m where m.to.id = ?#{ principal?.id }") - List<Message> findAll(); -} \ No newline at end of file diff --git a/samples/javaconfig/data/src/main/java/samples/data/User.java b/samples/javaconfig/data/src/main/java/samples/data/User.java deleted file mode 100644 index 15d4f37d77c..00000000000 --- a/samples/javaconfig/data/src/main/java/samples/data/User.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.data; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -/** - * @author Rob Winch - */ -@Entity -public class User { - @GeneratedValue(strategy = GenerationType.AUTO) - @Id - private Long id; - - private String firstName; - - private String lastName; - - private String email; - - private String password; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} \ No newline at end of file diff --git a/samples/javaconfig/data/src/test/java/samples/data/SecurityMessageRepositoryTests.java b/samples/javaconfig/data/src/test/java/samples/data/SecurityMessageRepositoryTests.java deleted file mode 100644 index 9972659fd2c..00000000000 --- a/samples/javaconfig/data/src/test/java/samples/data/SecurityMessageRepositoryTests.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 samples.data; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import samples.DataConfig; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Rob Winch - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = DataConfig.class) -public class SecurityMessageRepositoryTests { - @Autowired - SecurityMessageRepository repository; - - User user; - - @Before - public void setup() { - user = new User(); - user.setId(0L); - List<GrantedAuthority> authorities = AuthorityUtils - .createAuthorityList("ROLE_USER"); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - user, "notused", authorities); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - @After - public void cleanup() { - SecurityContextHolder.clearContext(); - } - - @Test - public void findAllOnlyToCurrentUser() { - Long expectedId = user.getId(); - List<Message> messages = repository.findAll(); - assertThat(messages).hasSize(3); - for (Message m : messages) { - assertThat(m.getTo().getId()).isEqualTo(expectedId); - } - } -} diff --git a/samples/javaconfig/data/src/test/resources/data.sql b/samples/javaconfig/data/src/test/resources/data.sql deleted file mode 100644 index 97c60b155bd..00000000000 --- a/samples/javaconfig/data/src/test/resources/data.sql +++ /dev/null @@ -1,10 +0,0 @@ -insert into user(id,email,password,firstName,lastName) values (0,'rob@example.com','password','Rob','Winch'); -insert into user(id,email,password,firstName,lastName) values (1,'luke@example.com','password','Luke','Taylor'); - -insert into message(id,created,to_id,summary,text) values (100,'2014-07-10 10:00:00',0,'Hello Rob','This message is for Rob'); -insert into message(id,created,to_id,summary,text) values (101,'2014-07-10 14:00:00',0,'How are you Rob?','This message is for Rob'); -insert into message(id,created,to_id,summary,text) values (102,'2014-07-11 22:00:00',0,'Is this secure?','This message is for Rob'); - -insert into message(id,created,to_id,summary,text) values (110,'2014-07-12 10:00:00',1,'Hello Luke','This message is for Luke'); -insert into message(id,created,to_id,summary,text) values (111,'2014-07-12 10:00:00',1,'Greetings Luke','This message is for Luke'); -insert into message(id,created,to_id,summary,text) values (112,'2014-07-12 10:00:00',1,'Is this secure?','This message is for Luke'); \ No newline at end of file diff --git a/samples/javaconfig/form/spring-security-samples-javaconfig-form.gradle b/samples/javaconfig/form/spring-security-samples-javaconfig-form.gradle deleted file mode 100644 index 3dee8959666..00000000000 --- a/samples/javaconfig/form/spring-security-samples-javaconfig-form.gradle +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - compile 'javax.xml.bind:jaxb-api' - compile 'com.sun.xml.bind:jaxb-core' - compile 'com.sun.xml.bind:jaxb-impl' - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api' - - runtime 'opensymphony:sitemesh' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/FormJcTests.java b/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/FormJcTests.java deleted file mode 100644 index 1ca1e669a1c..00000000000 --- a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/FormJcTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; - -/** - * @author Michael Simons - */ -public class FormJcTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final String userName = "user"; - final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() - .username(userName) - .password("password") - .submit(); - homePage - .assertAt() - .andTheUserNameDisplayedIs(userName); - } - - @Test - public void authenticatedUserLogsOut() { - LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit() - .logout(); - loginPage.assertAt(); - - loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 22ddd1c6391..00000000000 --- a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("SecureMail: View All"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p.navbar-text") - private WebElement message; - - public Content andTheUserNameDisplayedIs(final String userName) { - assertThat(message.getText()).isEqualTo(userName); - return this; - } - } -} diff --git a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index af6fbaa9881..00000000000 --- a/samples/javaconfig/form/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("SecureMail: Please Login"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public HomePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - } -} diff --git a/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 8f9cbba6f65..00000000000 --- a/samples/javaconfig/form/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.User; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/resources/**").permitAll() - .anyRequest().authenticated() - ) - .formLogin((formLogin) -> - formLogin - .loginPage("/login") - .permitAll() - ) - .logout((logout) -> - logout - .permitAll() - ); - } - // @formatter:on - - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")); - } - // @formatter:on -} diff --git a/samples/javaconfig/form/src/main/resources/logback.xml b/samples/javaconfig/form/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/form/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/form/src/main/resources/views/login.html b/samples/javaconfig/form/src/main/resources/views/login.html deleted file mode 100644 index c28c5b2add9..00000000000 --- a/samples/javaconfig/form/src/main/resources/views/login.html +++ /dev/null @@ -1,24 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Please Login</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <form name="f" th:action="@{/login}" method="post"> - <fieldset> - <legend>Please Login</legend> - <div th:if="${param.error}" class="alert alert-error">Invalid - username and password.</div> - <div th:if="${param.logout}" class="alert alert-success">You - have been logged out.</div> - <label for="username">Username</label> <input type="text" - id="username" name="username" /> <label for="password">Password</label> - <input type="password" id="password" name="password" /> - <div class="form-actions"> - <button type="submit" class="btn">Log in</button> - </div> - </fieldset> - </form> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/login.jspx b/samples/javaconfig/form/src/main/webapp/WEB-INF/views/login.jspx deleted file mode 100644 index 1f472ba566d..00000000000 --- a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/login.jspx +++ /dev/null @@ -1,36 +0,0 @@ -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" - xmlns:form="http://www.springframework.org/tags/form" version="2.0"> - <jsp:directive.page language="java" contentType="text/html" /> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> -<head> -<title>Please Login</title> -</head> -<body> - <c:url value="/login" var="loginUrl"/> - <form:form name="f" action="${loginUrl}" method="post"> - <fieldset> - <legend>Please Login</legend> - <c:if test="${param.error != null}"> - <div class="alert alert-error"> - Invalid username and password. - </div> - </c:if> - <c:if test="${param.logout != null}"> - <div class="alert alert-success"> - You have been logged out. - </div> - </c:if> - <label for="username">Username</label> - <input type="text" id="username" name="username" value="${username}"/> - <label for="password">Password</label> - <input type="password" id="password" name="password"/> - <div class="form-actions"> - <button type="submit" class="btn">Log in</button> - </div> - </fieldset> - </form:form> -</body> -</html> -</jsp:root> diff --git a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/compose.jspx b/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/compose.jspx deleted file mode 100644 index a92f7e2d6cf..00000000000 --- a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/compose.jspx +++ /dev/null @@ -1,26 +0,0 @@ -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" - xmlns:form="http://www.springframework.org/tags/form" version="2.0"> - <jsp:directive.page language="java" contentType="text/html" /> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title>Compose</title> - </head> - <body> - <div class="container"> - <h1>Messages : Create</h1> - <form:form action="./" method="post" modelAttribute="message"> - <form:errors path="*" element="div" cssClass="alert alert-error" /> - <label for="summary">Summary</label> - <form:input type="text" path="summary" class="input-xxlarge" /> - <label for="text">Message</label> - <form:textarea path="text" class="input-xxlarge"></form:textarea> - <div class="form-actions"> - <input type="submit" value="Create" /> - </div> - </form:form> - </div> - </body> - </html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/inbox.jspx b/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/inbox.jspx deleted file mode 100644 index ed02d313c6f..00000000000 --- a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/inbox.jspx +++ /dev/null @@ -1,40 +0,0 @@ -<jsp:root - xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" - xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" - version="2.0"> - <jsp:directive.page language="java" contentType="text/html"/> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title>Inbox</title> - </head> - <body> - <h1>Inbox</h1> - <table class="table"> - <thead> - <tr> - <th>Created</th> - <th>Summary</th> - </tr> - </thead> - <tbody> - <c:if test="${empty messages}"> - <tr> - <td colspan="2" class="msg">You have not received any mail yet.</td> - </tr> - </c:if> - <c:forEach items="${messages}" var="message"> - <tr> - <td><fmt:formatDate value="${message.created.time}"/></td> - <spring:url var="messageUrl" value="/{id}"> - <spring:param name="id" value="${message.id}"/> - </spring:url> - <td><a href="${messageUrl}"><c:out value="${message.summary}"/></a></td> - </tr> - </c:forEach> - </tbody> - </table> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/show.jspx b/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/show.jspx deleted file mode 100644 index 82007c267b0..00000000000 --- a/samples/javaconfig/form/src/main/webapp/WEB-INF/views/messages/show.jspx +++ /dev/null @@ -1,24 +0,0 @@ -<jsp:root - xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" - xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" - version="2.0"> - <jsp:directive.page language="java" contentType="text/html"/> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title><c:out value="${message.summary}"/></title> - </head> - <body> - <div class="container"> - <h1>Message : <c:out value="${message.summary}"/></h1> - <dl> - <dt>Created</dt> - <dd><fmt:formatDate value="${message.created.time}"/></dd> - <dt>Message</dt> - <dd><c:out value="${message.text}"/></dd> - </dl> - </div> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/form/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/form/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/form/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/hellojs/spring-security-samples-javaconfig-hellojs.gradle b/samples/javaconfig/hellojs/spring-security-samples-javaconfig-hellojs.gradle deleted file mode 100644 index 86700d410d5..00000000000 --- a/samples/javaconfig/hellojs/spring-security-samples-javaconfig-hellojs.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'com.fasterxml.jackson.core:jackson-databind' - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 595d2413e0d..00000000000 --- a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - -@EnableWebSecurity -public class SecurityConfig { - - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("{noop}password").roles("USER"); - } - // @formatter:on -} diff --git a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/mvc/MessageJsonController.java b/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/mvc/MessageJsonController.java deleted file mode 100644 index 696d3c75e71..00000000000 --- a/samples/javaconfig/hellojs/src/main/java/org/springframework/security/samples/mvc/MessageJsonController.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.mvc; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Optional; - -import javax.validation.Valid; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.samples.data.Message; -import org.springframework.security.samples.data.MessageRepository; -import org.springframework.stereotype.Controller; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.mvc.support.RedirectAttributes; - -@Controller -@RequestMapping(value = "/", produces = "application/json") -public class MessageJsonController { - private MessageRepository messageRepository; - - @Autowired - public MessageJsonController(MessageRepository messageRepository) { - this.messageRepository = messageRepository; - } - - @RequestMapping - public ResponseEntity<Iterable<Message>> list() { - Iterable<Message> messages = messageRepository.findAll(); - return new ResponseEntity<>(messages, HttpStatus.OK); - } - - @RequestMapping("{id}") - public ResponseEntity<Optional<Message>> view(@PathVariable Long id) { - Optional<Message> message = messageRepository.findById(id); - return new ResponseEntity<>(message, HttpStatus.OK); - } - - @RequestMapping(method = RequestMethod.POST, consumes = "application/json") - public ResponseEntity<?> create(@Valid @RequestBody Message message, - BindingResult result, RedirectAttributes redirect) { - if (result.hasErrors()) { - List<String> errors = new ArrayList<>(result.getErrorCount()); - for (ObjectError r : result.getAllErrors()) { - errors.add(r.getDefaultMessage()); - } - return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); - } - message.setCreated(Calendar.getInstance()); - message = messageRepository.save(message); - return new ResponseEntity<>(message, HttpStatus.OK); - } -} diff --git a/samples/javaconfig/hellojs/src/main/resources/resources/js/bootstrap.js b/samples/javaconfig/hellojs/src/main/resources/resources/js/bootstrap.js deleted file mode 100644 index ee5a14587b9..00000000000 --- a/samples/javaconfig/hellojs/src/main/resources/resources/js/bootstrap.js +++ /dev/null @@ -1,2280 +0,0 @@ -/* =================================================== - * bootstrap-transition.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#transitions - * =================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* CSS TRANSITION SUPPORT (https://www.modernizr.com/) - * ======================================================= */ - - $(function () { - - $.support.transition = (function () { - - var transitionEnd = (function () { - - var el = document.createElement('bootstrap') - , transEndEventNames = { - 'WebkitTransition' : 'webkitTransitionEnd' - , 'MozTransition' : 'transitionend' - , 'OTransition' : 'oTransitionEnd otransitionend' - , 'transition' : 'transitionend' - } - , name - - for (name in transEndEventNames){ - if (el.style[name] !== undefined) { - return transEndEventNames[name] - } - } - - }()) - - return transitionEnd && { - end: transitionEnd - } - - })() - - }) - -}(window.jQuery);/* ========================================================== - * bootstrap-alert.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#alerts - * ========================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* ALERT CLASS DEFINITION - * ====================== */ - - var dismiss = '[data-dismiss="alert"]' - , Alert = function (el) { - $(el).on('click', dismiss, this.close) - } - - Alert.prototype.close = function (e) { - var $this = $(this) - , selector = $this.attr('data-target') - , $parent - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - $parent = $(selector) - - e && e.preventDefault() - - $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) - - $parent.trigger(e = $.Event('close')) - - if (e.isDefaultPrevented()) return - - $parent.removeClass('in') - - function removeElement() { - $parent - .trigger('closed') - .remove() - } - - $.support.transition && $parent.hasClass('fade') ? - $parent.on($.support.transition.end, removeElement) : - removeElement() - } - - - /* ALERT PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.alert - - $.fn.alert = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('alert') - if (!data) $this.data('alert', (data = new Alert(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - $.fn.alert.Constructor = Alert - - - /* ALERT NO CONFLICT - * ================= */ - - $.fn.alert.noConflict = function () { - $.fn.alert = old - return this - } - - - /* ALERT DATA-API - * ============== */ - - $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) - -}(window.jQuery);/* ============================================================ - * bootstrap-button.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#buttons - * ============================================================ - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* BUTTON PUBLIC CLASS DEFINITION - * ============================== */ - - var Button = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.button.defaults, options) - } - - Button.prototype.setState = function (state) { - var d = 'disabled' - , $el = this.$element - , data = $el.data() - , val = $el.is('input') ? 'val' : 'html' - - state = state + 'Text' - data.resetText || $el.data('resetText', $el[val]()) - - $el[val](data[state] || this.options[state]) - - // push to event loop to allow forms to submit - setTimeout(function () { - state == 'loadingText' ? - $el.addClass(d).attr(d, d) : - $el.removeClass(d).removeAttr(d) - }, 0) - } - - Button.prototype.toggle = function () { - var $parent = this.$element.closest('[data-toggle="buttons-radio"]') - - $parent && $parent - .find('.active') - .removeClass('active') - - this.$element.toggleClass('active') - } - - - /* BUTTON PLUGIN DEFINITION - * ======================== */ - - var old = $.fn.button - - $.fn.button = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('button') - , options = typeof option == 'object' && option - if (!data) $this.data('button', (data = new Button(this, options))) - if (option == 'toggle') data.toggle() - else if (option) data.setState(option) - }) - } - - $.fn.button.defaults = { - loadingText: 'loading...' - } - - $.fn.button.Constructor = Button - - - /* BUTTON NO CONFLICT - * ================== */ - - $.fn.button.noConflict = function () { - $.fn.button = old - return this - } - - - /* BUTTON DATA-API - * =============== */ - - $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - $btn.button('toggle') - }) - -}(window.jQuery);/* ========================================================== - * bootstrap-carousel.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#carousel - * ========================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* CAROUSEL CLASS DEFINITION - * ========================= */ - - var Carousel = function (element, options) { - this.$element = $(element) - this.$indicators = this.$element.find('.carousel-indicators') - this.options = options - this.options.pause == 'hover' && this.$element - .on('mouseenter', $.proxy(this.pause, this)) - .on('mouseleave', $.proxy(this.cycle, this)) - } - - Carousel.prototype = { - - cycle: function (e) { - if (!e) this.paused = false - if (this.interval) clearInterval(this.interval); - this.options.interval - && !this.paused - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) - return this - } - - , getActiveIndex: function () { - this.$active = this.$element.find('.item.active') - this.$items = this.$active.parent().children() - return this.$items.index(this.$active) - } - - , to: function (pos) { - var activeIndex = this.getActiveIndex() - , that = this - - if (pos > (this.$items.length - 1) || pos < 0) return - - if (this.sliding) { - return this.$element.one('slid', function () { - that.to(pos) - }) - } - - if (activeIndex == pos) { - return this.pause().cycle() - } - - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) - } - - , pause: function (e) { - if (!e) this.paused = true - if (this.$element.find('.next, .prev').length && $.support.transition.end) { - this.$element.trigger($.support.transition.end) - this.cycle(true) - } - clearInterval(this.interval) - this.interval = null - return this - } - - , next: function () { - if (this.sliding) return - return this.slide('next') - } - - , prev: function () { - if (this.sliding) return - return this.slide('prev') - } - - , slide: function (type, next) { - var $active = this.$element.find('.item.active') - , $next = next || $active[type]() - , isCycling = this.interval - , direction = type == 'next' ? 'left' : 'right' - , fallback = type == 'next' ? 'first' : 'last' - , that = this - , e - - this.sliding = true - - isCycling && this.pause() - - $next = $next.length ? $next : this.$element.find('.item')[fallback]() - - e = $.Event('slide', { - relatedTarget: $next[0] - , direction: direction - }) - - if ($next.hasClass('active')) return - - if (this.$indicators.length) { - this.$indicators.find('.active').removeClass('active') - this.$element.one('slid', function () { - var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) - $nextIndicator && $nextIndicator.addClass('active') - }) - } - - if ($.support.transition && this.$element.hasClass('slide')) { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $next.addClass(type) - $next[0].offsetWidth // force reflow - $active.addClass(direction) - $next.addClass(direction) - this.$element.one($.support.transition.end, function () { - $next.removeClass([type, direction].join(' ')).addClass('active') - $active.removeClass(['active', direction].join(' ')) - that.sliding = false - setTimeout(function () { that.$element.trigger('slid') }, 0) - }) - } else { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger('slid') - } - - isCycling && this.cycle() - - return this - } - - } - - - /* CAROUSEL PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.carousel - - $.fn.carousel = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('carousel') - , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) - , action = typeof option == 'string' ? option : options.slide - if (!data) $this.data('carousel', (data = new Carousel(this, options))) - if (typeof option == 'number') data.to(option) - else if (action) data[action]() - else if (options.interval) data.pause().cycle() - }) - } - - $.fn.carousel.defaults = { - interval: 5000 - , pause: 'hover' - } - - $.fn.carousel.Constructor = Carousel - - - /* CAROUSEL NO CONFLICT - * ==================== */ - - $.fn.carousel.noConflict = function () { - $.fn.carousel = old - return this - } - - /* CAROUSEL DATA-API - * ================= */ - - $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { - var $this = $(this), href - , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - , options = $.extend({}, $target.data(), $this.data()) - , slideIndex - - $target.carousel(options) - - if (slideIndex = $this.attr('data-slide-to')) { - $target.data('carousel').pause().to(slideIndex).cycle() - } - - e.preventDefault() - }) - -}(window.jQuery);/* ============================================================= - * bootstrap-collapse.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#collapse - * ============================================================= - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* COLLAPSE PUBLIC CLASS DEFINITION - * ================================ */ - - var Collapse = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.collapse.defaults, options) - - if (this.options.parent) { - this.$parent = $(this.options.parent) - } - - this.options.toggle && this.toggle() - } - - Collapse.prototype = { - - constructor: Collapse - - , dimension: function () { - var hasWidth = this.$element.hasClass('width') - return hasWidth ? 'width' : 'height' - } - - , show: function () { - var dimension - , scroll - , actives - , hasData - - if (this.transitioning || this.$element.hasClass('in')) return - - dimension = this.dimension() - scroll = $.camelCase(['scroll', dimension].join('-')) - actives = this.$parent && this.$parent.find('> .accordion-group > .in') - - if (actives && actives.length) { - hasData = actives.data('collapse') - if (hasData && hasData.transitioning) return - actives.collapse('hide') - hasData || actives.data('collapse', null) - } - - this.$element[dimension](0) - this.transition('addClass', $.Event('show'), 'shown') - $.support.transition && this.$element[dimension](this.$element[0][scroll]) - } - - , hide: function () { - var dimension - if (this.transitioning || !this.$element.hasClass('in')) return - dimension = this.dimension() - this.reset(this.$element[dimension]()) - this.transition('removeClass', $.Event('hide'), 'hidden') - this.$element[dimension](0) - } - - , reset: function (size) { - var dimension = this.dimension() - - this.$element - .removeClass('collapse') - [dimension](size || 'auto') - [0].offsetWidth - - this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') - - return this - } - - , transition: function (method, startEvent, completeEvent) { - var that = this - , complete = function () { - if (startEvent.type == 'show') that.reset() - that.transitioning = 0 - that.$element.trigger(completeEvent) - } - - this.$element.trigger(startEvent) - - if (startEvent.isDefaultPrevented()) return - - this.transitioning = 1 - - this.$element[method]('in') - - $.support.transition && this.$element.hasClass('collapse') ? - this.$element.one($.support.transition.end, complete) : - complete() - } - - , toggle: function () { - this[this.$element.hasClass('in') ? 'hide' : 'show']() - } - - } - - - /* COLLAPSE PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.collapse - - $.fn.collapse = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('collapse') - , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) - if (!data) $this.data('collapse', (data = new Collapse(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.collapse.defaults = { - toggle: true - } - - $.fn.collapse.Constructor = Collapse - - - /* COLLAPSE NO CONFLICT - * ==================== */ - - $.fn.collapse.noConflict = function () { - $.fn.collapse = old - return this - } - - - /* COLLAPSE DATA-API - * ================= */ - - $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { - var $this = $(this), href - , target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 - , option = $(target).data('collapse') ? 'toggle' : $this.data() - $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - $(target).collapse(option) - }) - -}(window.jQuery);/* ============================================================ - * bootstrap-dropdown.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#dropdowns - * ============================================================ - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ============================================================ */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* DROPDOWN CLASS DEFINITION - * ========================= */ - - var toggle = '[data-toggle=dropdown]' - , Dropdown = function (element) { - var $el = $(element).on('click.dropdown.data-api', this.toggle) - $('html').on('click.dropdown.data-api', function () { - $el.parent().removeClass('open') - }) - } - - Dropdown.prototype = { - - constructor: Dropdown - - , toggle: function (e) { - var $this = $(this) - , $parent - , isActive - - if ($this.is('.disabled, :disabled')) return - - $parent = getParent($this) - - isActive = $parent.hasClass('open') - - clearMenus() - - if (!isActive) { - if ('ontouchstart' in document.documentElement) { - // if mobile we we use a backdrop because click events don't delegate - $('<div class="dropdown-backdrop"/>').insertBefore($(this)).on('click', clearMenus) - } - $parent.toggleClass('open') - } - - $this.focus() - - return false - } - - , keydown: function (e) { - var $this - , $items - , $active - , $parent - , isActive - , index - - if (!/(38|40|27)/.test(e.keyCode)) return - - $this = $(this) - - e.preventDefault() - e.stopPropagation() - - if ($this.is('.disabled, :disabled')) return - - $parent = getParent($this) - - isActive = $parent.hasClass('open') - - if (!isActive || (isActive && e.keyCode == 27)) { - if (e.which == 27) $parent.find(toggle).focus() - return $this.click() - } - - $items = $('[role=menu] li:not(.divider):visible a', $parent) - - if (!$items.length) return - - index = $items.index($items.filter(':focus')) - - if (e.keyCode == 38 && index > 0) index-- // up - if (e.keyCode == 40 && index < $items.length - 1) index++ // down - if (!~index) index = 0 - - $items - .eq(index) - .focus() - } - - } - - function clearMenus() { - $('.dropdown-backdrop').remove() - $(toggle).each(function () { - getParent($(this)).removeClass('open') - }) - } - - function getParent($this) { - var selector = $this.attr('data-target') - , $parent - - if (!selector) { - selector = $this.attr('href') - selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - $parent = selector && $(selector) - - if (!$parent || !$parent.length) $parent = $this.parent() - - return $parent - } - - - /* DROPDOWN PLUGIN DEFINITION - * ========================== */ - - var old = $.fn.dropdown - - $.fn.dropdown = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('dropdown') - if (!data) $this.data('dropdown', (data = new Dropdown(this))) - if (typeof option == 'string') data[option].call($this) - }) - } - - $.fn.dropdown.Constructor = Dropdown - - - /* DROPDOWN NO CONFLICT - * ==================== */ - - $.fn.dropdown.noConflict = function () { - $.fn.dropdown = old - return this - } - - - /* APPLY TO STANDARD DROPDOWN ELEMENTS - * =================================== */ - - $(document) - .on('click.dropdown.data-api', clearMenus) - .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) - .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle) - .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) - -}(window.jQuery); -/* ========================================================= - * bootstrap-modal.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#modals - * ========================================================= - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================= */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* MODAL CLASS DEFINITION - * ====================== */ - - var Modal = function (element, options) { - this.options = options - this.$element = $(element) - .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) - this.options.remote && this.$element.find('.modal-body').load(this.options.remote) - } - - Modal.prototype = { - - constructor: Modal - - , toggle: function () { - return this[!this.isShown ? 'show' : 'hide']() - } - - , show: function () { - var that = this - , e = $.Event('show') - - this.$element.trigger(e) - - if (this.isShown || e.isDefaultPrevented()) return - - this.isShown = true - - this.escape() - - this.backdrop(function () { - var transition = $.support.transition && that.$element.hasClass('fade') - - if (!that.$element.parent().length) { - that.$element.appendTo(document.body) //don't move modals dom position - } - - that.$element.show() - - if (transition) { - that.$element[0].offsetWidth // force reflow - } - - that.$element - .addClass('in') - .attr('aria-hidden', false) - - that.enforceFocus() - - transition ? - that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : - that.$element.focus().trigger('shown') - - }) - } - - , hide: function (e) { - e && e.preventDefault() - - var that = this - - e = $.Event('hide') - - this.$element.trigger(e) - - if (!this.isShown || e.isDefaultPrevented()) return - - this.isShown = false - - this.escape() - - $(document).off('focusin.modal') - - this.$element - .removeClass('in') - .attr('aria-hidden', true) - - $.support.transition && this.$element.hasClass('fade') ? - this.hideWithTransition() : - this.hideModal() - } - - , enforceFocus: function () { - var that = this - $(document).on('focusin.modal', function (e) { - if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { - that.$element.focus() - } - }) - } - - , escape: function () { - var that = this - if (this.isShown && this.options.keyboard) { - this.$element.on('keyup.dismiss.modal', function ( e ) { - e.which == 27 && that.hide() - }) - } else if (!this.isShown) { - this.$element.off('keyup.dismiss.modal') - } - } - - , hideWithTransition: function () { - var that = this - , timeout = setTimeout(function () { - that.$element.off($.support.transition.end) - that.hideModal() - }, 500) - - this.$element.one($.support.transition.end, function () { - clearTimeout(timeout) - that.hideModal() - }) - } - - , hideModal: function () { - var that = this - this.$element.hide() - this.backdrop(function () { - that.removeBackdrop() - that.$element.trigger('hidden') - }) - } - - , removeBackdrop: function () { - this.$backdrop && this.$backdrop.remove() - this.$backdrop = null - } - - , backdrop: function (callback) { - var that = this - , animate = this.$element.hasClass('fade') ? 'fade' : '' - - if (this.isShown && this.options.backdrop) { - var doAnimate = $.support.transition && animate - - this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') - .appendTo(document.body) - - this.$backdrop.click( - this.options.backdrop == 'static' ? - $.proxy(this.$element[0].focus, this.$element[0]) - : $.proxy(this.hide, this) - ) - - if (doAnimate) this.$backdrop[0].offsetWidth // force reflow - - this.$backdrop.addClass('in') - - if (!callback) return - - doAnimate ? - this.$backdrop.one($.support.transition.end, callback) : - callback() - - } else if (!this.isShown && this.$backdrop) { - this.$backdrop.removeClass('in') - - $.support.transition && this.$element.hasClass('fade')? - this.$backdrop.one($.support.transition.end, callback) : - callback() - - } else if (callback) { - callback() - } - } - } - - - /* MODAL PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.modal - - $.fn.modal = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('modal') - , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option) - if (!data) $this.data('modal', (data = new Modal(this, options))) - if (typeof option == 'string') data[option]() - else if (options.show) data.show() - }) - } - - $.fn.modal.defaults = { - backdrop: true - , keyboard: true - , show: true - } - - $.fn.modal.Constructor = Modal - - - /* MODAL NO CONFLICT - * ================= */ - - $.fn.modal.noConflict = function () { - $.fn.modal = old - return this - } - - - /* MODAL DATA-API - * ============== */ - - $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) { - var $this = $(this) - , href = $this.attr('href') - , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7 - , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data()) - - e.preventDefault() - - $target - .modal(option) - .one('hide', function () { - $this.focus() - }) - }) - -}(window.jQuery); -/* =========================================================== - * bootstrap-tooltip.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#tooltips - * Inspired by the original jQuery.tipsy by Jason Frame - * =========================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* TOOLTIP PUBLIC CLASS DEFINITION - * =============================== */ - - var Tooltip = function (element, options) { - this.init('tooltip', element, options) - } - - Tooltip.prototype = { - - constructor: Tooltip - - , init: function (type, element, options) { - var eventIn - , eventOut - , triggers - , trigger - , i - - this.type = type - this.$element = $(element) - this.options = this.getOptions(options) - this.enabled = true - - triggers = this.options.trigger.split(' ') - - for (i = triggers.length; i--;) { - trigger = triggers[i] - if (trigger == 'click') { - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) - } else if (trigger != 'manual') { - eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' - eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) - } - } - - this.options.selector ? - (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : - this.fixTitle() - } - - , getOptions: function (options) { - options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options) - - if (options.delay && typeof options.delay == 'number') { - options.delay = { - show: options.delay - , hide: options.delay - } - } - - return options - } - - , enter: function (e) { - var defaults = $.fn[this.type].defaults - , options = {} - , self - - this._options && $.each(this._options, function (key, value) { - if (defaults[key] != value) options[key] = value - }, this) - - self = $(e.currentTarget)[this.type](options).data(this.type) - - if (!self.options.delay || !self.options.delay.show) return self.show() - - clearTimeout(this.timeout) - self.hoverState = 'in' - this.timeout = setTimeout(function() { - if (self.hoverState == 'in') self.show() - }, self.options.delay.show) - } - - , leave: function (e) { - var self = $(e.currentTarget)[this.type](this._options).data(this.type) - - if (this.timeout) clearTimeout(this.timeout) - if (!self.options.delay || !self.options.delay.hide) return self.hide() - - self.hoverState = 'out' - this.timeout = setTimeout(function() { - if (self.hoverState == 'out') self.hide() - }, self.options.delay.hide) - } - - , show: function () { - var $tip - , pos - , actualWidth - , actualHeight - , placement - , tp - , e = $.Event('show') - - if (this.hasContent() && this.enabled) { - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - $tip = this.tip() - this.setContent() - - if (this.options.animation) { - $tip.addClass('fade') - } - - placement = typeof this.options.placement == 'function' ? - this.options.placement.call(this, $tip[0], this.$element[0]) : - this.options.placement - - $tip - .detach() - .css({ top: 0, left: 0, display: 'block' }) - - this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) - - pos = this.getPosition() - - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - - switch (placement) { - case 'bottom': - tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} - break - case 'top': - tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} - break - case 'left': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} - break - case 'right': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} - break - } - - this.applyPlacement(tp, placement) - this.$element.trigger('shown') - } - } - - , applyPlacement: function(offset, placement){ - var $tip = this.tip() - , width = $tip[0].offsetWidth - , height = $tip[0].offsetHeight - , actualWidth - , actualHeight - , delta - , replace - - $tip - .offset(offset) - .addClass(placement) - .addClass('in') - - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - - if (placement == 'top' && actualHeight != height) { - offset.top = offset.top + height - actualHeight - replace = true - } - - if (placement == 'bottom' || placement == 'top') { - delta = 0 - - if (offset.left < 0){ - delta = offset.left * -2 - offset.left = 0 - $tip.offset(offset) - actualWidth = $tip[0].offsetWidth - actualHeight = $tip[0].offsetHeight - } - - this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') - } else { - this.replaceArrow(actualHeight - height, actualHeight, 'top') - } - - if (replace) $tip.offset(offset) - } - - , replaceArrow: function(delta, dimension, position){ - this - .arrow() - .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '') - } - - , setContent: function () { - var $tip = this.tip() - , title = this.getTitle() - - $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) - $tip.removeClass('fade in top bottom left right') - } - - , hide: function () { - var that = this - , $tip = this.tip() - , e = $.Event('hide') - - this.$element.trigger(e) - if (e.isDefaultPrevented()) return - - $tip.removeClass('in') - - function removeWithAnimation() { - var timeout = setTimeout(function () { - $tip.off($.support.transition.end).detach() - }, 500) - - $tip.one($.support.transition.end, function () { - clearTimeout(timeout) - $tip.detach() - }) - } - - $.support.transition && this.$tip.hasClass('fade') ? - removeWithAnimation() : - $tip.detach() - - this.$element.trigger('hidden') - - return this - } - - , fixTitle: function () { - var $e = this.$element - if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { - $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') - } - } - - , hasContent: function () { - return this.getTitle() - } - - , getPosition: function () { - var el = this.$element[0] - return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { - width: el.offsetWidth - , height: el.offsetHeight - }, this.$element.offset()) - } - - , getTitle: function () { - var title - , $e = this.$element - , o = this.options - - title = $e.attr('data-original-title') - || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) - - return title - } - - , tip: function () { - return this.$tip = this.$tip || $(this.options.template) - } - - , arrow: function(){ - return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") - } - - , validate: function () { - if (!this.$element[0].parentNode) { - this.hide() - this.$element = null - this.options = null - } - } - - , enable: function () { - this.enabled = true - } - - , disable: function () { - this.enabled = false - } - - , toggleEnabled: function () { - this.enabled = !this.enabled - } - - , toggle: function (e) { - var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this - self.tip().hasClass('in') ? self.hide() : self.show() - } - - , destroy: function () { - this.hide().$element.off('.' + this.type).removeData(this.type) - } - - } - - - /* TOOLTIP PLUGIN DEFINITION - * ========================= */ - - var old = $.fn.tooltip - - $.fn.tooltip = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('tooltip') - , options = typeof option == 'object' && option - if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.tooltip.Constructor = Tooltip - - $.fn.tooltip.defaults = { - animation: true - , placement: 'top' - , selector: false - , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' - , trigger: 'hover focus' - , title: '' - , delay: 0 - , html: false - , container: false - } - - - /* TOOLTIP NO CONFLICT - * =================== */ - - $.fn.tooltip.noConflict = function () { - $.fn.tooltip = old - return this - } - -}(window.jQuery); -/* =========================================================== - * bootstrap-popover.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#popovers - * =========================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * =========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* POPOVER PUBLIC CLASS DEFINITION - * =============================== */ - - var Popover = function (element, options) { - this.init('popover', element, options) - } - - - /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js - ========================================== */ - - Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { - - constructor: Popover - - , setContent: function () { - var $tip = this.tip() - , title = this.getTitle() - , content = this.getContent() - - $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) - $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) - - $tip.removeClass('fade top bottom left right in') - } - - , hasContent: function () { - return this.getTitle() || this.getContent() - } - - , getContent: function () { - var content - , $e = this.$element - , o = this.options - - content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) - || $e.attr('data-content') - - return content - } - - , tip: function () { - if (!this.$tip) { - this.$tip = $(this.options.template) - } - return this.$tip - } - - , destroy: function () { - this.hide().$element.off('.' + this.type).removeData(this.type) - } - - }) - - - /* POPOVER PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.popover - - $.fn.popover = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('popover') - , options = typeof option == 'object' && option - if (!data) $this.data('popover', (data = new Popover(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.popover.Constructor = Popover - - $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { - placement: 'right' - , trigger: 'click' - , content: '' - , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' - }) - - - /* POPOVER NO CONFLICT - * =================== */ - - $.fn.popover.noConflict = function () { - $.fn.popover = old - return this - } - -}(window.jQuery); -/* ============================================================= - * bootstrap-scrollspy.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#scrollspy - * ============================================================= - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ============================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* SCROLLSPY CLASS DEFINITION - * ========================== */ - - function ScrollSpy(element, options) { - var process = $.proxy(this.process, this) - , $element = $(element).is('body') ? $(window) : $(element) - , href - this.options = $.extend({}, $.fn.scrollspy.defaults, options) - this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process) - this.selector = (this.options.target - || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - || '') + ' .nav li > a' - this.$body = $('body') - this.refresh() - this.process() - } - - ScrollSpy.prototype = { - - constructor: ScrollSpy - - , refresh: function () { - var self = this - , $targets - - this.offsets = $([]) - this.targets = $([]) - - $targets = this.$body - .find(this.selector) - .map(function () { - var $el = $(this) - , href = $el.data('target') || $el.attr('href') - , $href = /^#\w/.test(href) && $(href) - return ( $href - && $href.length - && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null - }) - .sort(function (a, b) { return a[0] - b[0] }) - .each(function () { - self.offsets.push(this[0]) - self.targets.push(this[1]) - }) - } - - , process: function () { - var scrollTop = this.$scrollElement.scrollTop() + this.options.offset - , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight - , maxScroll = scrollHeight - this.$scrollElement.height() - , offsets = this.offsets - , targets = this.targets - , activeTarget = this.activeTarget - , i - - if (scrollTop >= maxScroll) { - return activeTarget != (i = targets.last()[0]) - && this.activate ( i ) - } - - for (i = offsets.length; i--;) { - activeTarget != targets[i] - && scrollTop >= offsets[i] - && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) - && this.activate( targets[i] ) - } - } - - , activate: function (target) { - var active - , selector - - this.activeTarget = target - - $(this.selector) - .parent('.active') - .removeClass('active') - - selector = this.selector - + '[data-target="' + target + '"],' - + this.selector + '[href="' + target + '"]' - - active = $(selector) - .parent('li') - .addClass('active') - - if (active.parent('.dropdown-menu').length) { - active = active.closest('li.dropdown').addClass('active') - } - - active.trigger('activate') - } - - } - - - /* SCROLLSPY PLUGIN DEFINITION - * =========================== */ - - var old = $.fn.scrollspy - - $.fn.scrollspy = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('scrollspy') - , options = typeof option == 'object' && option - if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.scrollspy.Constructor = ScrollSpy - - $.fn.scrollspy.defaults = { - offset: 10 - } - - - /* SCROLLSPY NO CONFLICT - * ===================== */ - - $.fn.scrollspy.noConflict = function () { - $.fn.scrollspy = old - return this - } - - - /* SCROLLSPY DATA-API - * ================== */ - - $(window).on('load', function () { - $('[data-spy="scroll"]').each(function () { - var $spy = $(this) - $spy.scrollspy($spy.data()) - }) - }) - -}(window.jQuery);/* ======================================================== - * bootstrap-tab.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#tabs - * ======================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ======================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* TAB CLASS DEFINITION - * ==================== */ - - var Tab = function (element) { - this.element = $(element) - } - - Tab.prototype = { - - constructor: Tab - - , show: function () { - var $this = this.element - , $ul = $this.closest('ul:not(.dropdown-menu)') - , selector = $this.attr('data-target') - , previous - , $target - , e - - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } - - if ( $this.parent('li').hasClass('active') ) return - - previous = $ul.find('.active:last a')[0] - - e = $.Event('show', { - relatedTarget: previous - }) - - $this.trigger(e) - - if (e.isDefaultPrevented()) return - - $target = $(selector) - - this.activate($this.parent('li'), $ul) - this.activate($target, $target.parent(), function () { - $this.trigger({ - type: 'shown' - , relatedTarget: previous - }) - }) - } - - , activate: function ( element, container, callback) { - var $active = container.find('> .active') - , transition = callback - && $.support.transition - && $active.hasClass('fade') - - function next() { - $active - .removeClass('active') - .find('> .dropdown-menu > .active') - .removeClass('active') - - element.addClass('active') - - if (transition) { - element[0].offsetWidth // reflow for transition - element.addClass('in') - } else { - element.removeClass('fade') - } - - if ( element.parent('.dropdown-menu') ) { - element.closest('li.dropdown').addClass('active') - } - - callback && callback() - } - - transition ? - $active.one($.support.transition.end, next) : - next() - - $active.removeClass('in') - } - } - - - /* TAB PLUGIN DEFINITION - * ===================== */ - - var old = $.fn.tab - - $.fn.tab = function ( option ) { - return this.each(function () { - var $this = $(this) - , data = $this.data('tab') - if (!data) $this.data('tab', (data = new Tab(this))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.tab.Constructor = Tab - - - /* TAB NO CONFLICT - * =============== */ - - $.fn.tab.noConflict = function () { - $.fn.tab = old - return this - } - - - /* TAB DATA-API - * ============ */ - - $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { - e.preventDefault() - $(this).tab('show') - }) - -}(window.jQuery);/* ============================================================= - * bootstrap-typeahead.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#typeahead - * ============================================================= - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ============================================================ */ - - -!function($){ - - "use strict"; // jshint ;_; - - - /* TYPEAHEAD PUBLIC CLASS DEFINITION - * ================================= */ - - var Typeahead = function (element, options) { - this.$element = $(element) - this.options = $.extend({}, $.fn.typeahead.defaults, options) - this.matcher = this.options.matcher || this.matcher - this.sorter = this.options.sorter || this.sorter - this.highlighter = this.options.highlighter || this.highlighter - this.updater = this.options.updater || this.updater - this.source = this.options.source - this.$menu = $(this.options.menu) - this.shown = false - this.listen() - } - - Typeahead.prototype = { - - constructor: Typeahead - - , select: function () { - var val = this.$menu.find('.active').attr('data-value') - this.$element - .val(this.updater(val)) - .change() - return this.hide() - } - - , updater: function (item) { - return item - } - - , show: function () { - var pos = $.extend({}, this.$element.position(), { - height: this.$element[0].offsetHeight - }) - - this.$menu - .insertAfter(this.$element) - .css({ - top: pos.top + pos.height - , left: pos.left - }) - .show() - - this.shown = true - return this - } - - , hide: function () { - this.$menu.hide() - this.shown = false - return this - } - - , lookup: function (event) { - var items - - this.query = this.$element.val() - - if (!this.query || this.query.length < this.options.minLength) { - return this.shown ? this.hide() : this - } - - items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source - - return items ? this.process(items) : this - } - - , process: function (items) { - var that = this - - items = $.grep(items, function (item) { - return that.matcher(item) - }) - - items = this.sorter(items) - - if (!items.length) { - return this.shown ? this.hide() : this - } - - return this.render(items.slice(0, this.options.items)).show() - } - - , matcher: function (item) { - return ~item.toLowerCase().indexOf(this.query.toLowerCase()) - } - - , sorter: function (items) { - var beginswith = [] - , caseSensitive = [] - , caseInsensitive = [] - , item - - while (item = items.shift()) { - if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) - else if (~item.indexOf(this.query)) caseSensitive.push(item) - else caseInsensitive.push(item) - } - - return beginswith.concat(caseSensitive, caseInsensitive) - } - - , highlighter: function (item) { - var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') - return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { - return '<strong>' + match + '</strong>' - }) - } - - , render: function (items) { - var that = this - - items = $(items).map(function (i, item) { - i = $(that.options.item).attr('data-value', item) - i.find('a').html(that.highlighter(item)) - return i[0] - }) - - items.first().addClass('active') - this.$menu.html(items) - return this - } - - , next: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , next = active.next() - - if (!next.length) { - next = $(this.$menu.find('li')[0]) - } - - next.addClass('active') - } - - , prev: function (event) { - var active = this.$menu.find('.active').removeClass('active') - , prev = active.prev() - - if (!prev.length) { - prev = this.$menu.find('li').last() - } - - prev.addClass('active') - } - - , listen: function () { - this.$element - .on('focus', $.proxy(this.focus, this)) - .on('blur', $.proxy(this.blur, this)) - .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', $.proxy(this.keyup, this)) - - if (this.eventSupported('keydown')) { - this.$element.on('keydown', $.proxy(this.keydown, this)) - } - - this.$menu - .on('click', $.proxy(this.click, this)) - .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) - .on('mouseleave', 'li', $.proxy(this.mouseleave, this)) - } - - , eventSupported: function(eventName) { - var isSupported = eventName in this.$element - if (!isSupported) { - this.$element.setAttribute(eventName, 'return;') - isSupported = typeof this.$element[eventName] === 'function' - } - return isSupported - } - - , move: function (e) { - if (!this.shown) return - - switch(e.keyCode) { - case 9: // tab - case 13: // enter - case 27: // escape - e.preventDefault() - break - - case 38: // up arrow - e.preventDefault() - this.prev() - break - - case 40: // down arrow - e.preventDefault() - this.next() - break - } - - e.stopPropagation() - } - - , keydown: function (e) { - this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]) - this.move(e) - } - - , keypress: function (e) { - if (this.suppressKeyPressRepeat) return - this.move(e) - } - - , keyup: function (e) { - switch(e.keyCode) { - case 40: // down arrow - case 38: // up arrow - case 16: // shift - case 17: // ctrl - case 18: // alt - break - - case 9: // tab - case 13: // enter - if (!this.shown) return - this.select() - break - - case 27: // escape - if (!this.shown) return - this.hide() - break - - default: - this.lookup() - } - - e.stopPropagation() - e.preventDefault() - } - - , focus: function (e) { - this.focused = true - } - - , blur: function (e) { - this.focused = false - if (!this.mousedover && this.shown) this.hide() - } - - , click: function (e) { - e.stopPropagation() - e.preventDefault() - this.select() - this.$element.focus() - } - - , mouseenter: function (e) { - this.mousedover = true - this.$menu.find('.active').removeClass('active') - $(e.currentTarget).addClass('active') - } - - , mouseleave: function (e) { - this.mousedover = false - if (!this.focused && this.shown) this.hide() - } - - } - - - /* TYPEAHEAD PLUGIN DEFINITION - * =========================== */ - - var old = $.fn.typeahead - - $.fn.typeahead = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('typeahead') - , options = typeof option == 'object' && option - if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.typeahead.defaults = { - source: [] - , items: 8 - , menu: '<ul class="typeahead dropdown-menu"></ul>' - , item: '<li><a href="#"></a></li>' - , minLength: 1 - } - - $.fn.typeahead.Constructor = Typeahead - - - /* TYPEAHEAD NO CONFLICT - * =================== */ - - $.fn.typeahead.noConflict = function () { - $.fn.typeahead = old - return this - } - - - /* TYPEAHEAD DATA-API - * ================== */ - - $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { - var $this = $(this) - if ($this.data('typeahead')) return - $this.typeahead($this.data()) - }) - -}(window.jQuery); -/* ========================================================== - * bootstrap-affix.js v2.3.2 - * https://twitter.github.com/bootstrap/javascript.html#affix - * ========================================================== - * Copyright 2012 Twitter, Inc. - * - * 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 - * - * https://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. - * ========================================================== */ - - -!function ($) { - - "use strict"; // jshint ;_; - - - /* AFFIX CLASS DEFINITION - * ====================== */ - - var Affix = function (element, options) { - this.options = $.extend({}, $.fn.affix.defaults, options) - this.$window = $(window) - .on('scroll.affix.data-api', $.proxy(this.checkPosition, this)) - .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this)) - this.$element = $(element) - this.checkPosition() - } - - Affix.prototype.checkPosition = function () { - if (!this.$element.is(':visible')) return - - var scrollHeight = $(document).height() - , scrollTop = this.$window.scrollTop() - , position = this.$element.offset() - , offset = this.options.offset - , offsetBottom = offset.bottom - , offsetTop = offset.top - , reset = 'affix affix-top affix-bottom' - , affix - - if (typeof offset != 'object') offsetBottom = offsetTop = offset - if (typeof offsetTop == 'function') offsetTop = offset.top() - if (typeof offsetBottom == 'function') offsetBottom = offset.bottom() - - affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? - false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? - 'bottom' : offsetTop != null && scrollTop <= offsetTop ? - 'top' : false - - if (this.affixed === affix) return - - this.affixed = affix - this.unpin = affix == 'bottom' ? position.top - scrollTop : null - - this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : '')) - } - - - /* AFFIX PLUGIN DEFINITION - * ======================= */ - - var old = $.fn.affix - - $.fn.affix = function (option) { - return this.each(function () { - var $this = $(this) - , data = $this.data('affix') - , options = typeof option == 'object' && option - if (!data) $this.data('affix', (data = new Affix(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - $.fn.affix.Constructor = Affix - - $.fn.affix.defaults = { - offset: 0 - } - - - /* AFFIX NO CONFLICT - * ================= */ - - $.fn.affix.noConflict = function () { - $.fn.affix = old - return this - } - - - /* AFFIX DATA-API - * ============== */ - - $(window).on('load', function () { - $('[data-spy="affix"]').each(function () { - var $spy = $(this) - , data = $spy.data() - - data.offset = data.offset || {} - - data.offsetBottom && (data.offset.bottom = data.offsetBottom) - data.offsetTop && (data.offset.top = data.offsetTop) - - $spy.affix(data) - }) - }) - - -}(window.jQuery); \ No newline at end of file diff --git a/samples/javaconfig/hellojs/src/main/resources/resources/js/jquery-3.5.1.min.js b/samples/javaconfig/hellojs/src/main/resources/resources/js/jquery-3.5.1.min.js deleted file mode 100644 index b0614034ad3..00000000000 --- a/samples/javaconfig/hellojs/src/main/resources/resources/js/jquery-3.5.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/samples/javaconfig/hellojs/src/main/resources/resources/js/knockout-2.3.0.js b/samples/javaconfig/hellojs/src/main/resources/resources/js/knockout-2.3.0.js deleted file mode 100644 index 752a084f99e..00000000000 --- a/samples/javaconfig/hellojs/src/main/resources/resources/js/knockout-2.3.0.js +++ /dev/null @@ -1,88 +0,0 @@ -// Knockout JavaScript library v2.3.0 -// (c) Steven Sanderson - https://knockoutjs.com/ -// License: MIT (https://www.opensource.org/licenses/mit-license.php) - -(function() {function F(q){return function(){return q}};(function(q){var w=this||(0,eval)("this"),s=w.document,H=w.navigator,t=w.jQuery,y=w.JSON;(function(q){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?q(module.exports||exports):"function"===typeof define&&define.amd?define(["exports"],q):q(w.ko={})})(function(C){function G(b,c,d,f){a.d[b]={init:function(b){a.a.f.set(b,I,{});return{controlsDescendantBindings:!0}},update:function(b,e,m,h,k){m=a.a.f.get(b,I);e=a.a.c(e());h=!d!==!e;var l=!m.fb;if(l||c||h!==m.vb)l&&(m.fb= -a.a.Oa(a.e.childNodes(b),!0)),h?(l||a.e.P(b,a.a.Oa(m.fb)),a.Ja(f?f(k,e):k,b)):a.e.ba(b),m.vb=h}};a.g.S[b]=!1;a.e.L[b]=!0}function J(b,c,d){d&&c!==a.h.n(b)&&a.h.W(b,c);c!==a.h.n(b)&&a.q.I(a.a.Ga,null,[b,"change"])}var a="undefined"!==typeof C?C:{};a.b=function(b,c){for(var d=b.split("."),f=a,g=0;g<d.length-1;g++)f=f[d[g]];f[d[d.length-1]]=c};a.r=function(a,c,d){a[c]=d};a.version="2.3.0";a.b("version",a.version);a.a=function(){function b(a,b){for(var e in a)a.hasOwnProperty(e)&&b(e,a[e])}function c(b, -e){if("input"!==a.a.u(b)||!b.type||"click"!=e.toLowerCase())return!1;var k=b.type;return"checkbox"==k||"radio"==k}var d={},f={};d[H&&/Firefox\/2/i.test(H.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];d.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(d,function(a,b){if(b.length)for(var e=0,c=b.length;e<c;e++)f[b[e]]=a});var g={propertychange:!0},e=s&&function(){for(var a=3,b=s.createElement("div"),e=b.getElementsByTagName("i");b.innerHTML= -"\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",e[0];);return 4<a?a:q}();return{Ta:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],p:function(a,b){for(var e=0,c=a.length;e<c;e++)b(a[e])},k:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var e=0,c=a.length;e<c;e++)if(a[e]===b)return e;return-1},La:function(a,b,e){for(var c=0,d=a.length;c<d;c++)if(b.call(e,a[c]))return a[c];return null},ka:function(b,e){var c=a.a.k(b,e);0<=c&& -b.splice(c,1)},Ma:function(b){b=b||[];for(var e=[],c=0,d=b.length;c<d;c++)0>a.a.k(e,b[c])&&e.push(b[c]);return e},Z:function(a,b){a=a||[];for(var e=[],c=0,d=a.length;c<d;c++)e.push(b(a[c]));return e},Y:function(a,b){a=a||[];for(var e=[],c=0,d=a.length;c<d;c++)b(a[c])&&e.push(a[c]);return e},R:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var e=0,c=b.length;e<c;e++)a.push(b[e]);return a},ja:function(b,e,c){var d=b.indexOf?b.indexOf(e):a.a.k(b,e);0>d?c&&b.push(e):c||b.splice(d,1)}, -extend:function(a,b){if(b)for(var e in b)b.hasOwnProperty(e)&&(a[e]=b[e]);return a},w:b,oa:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Mb:function(b){b=a.a.N(b);for(var e=s.createElement("div"),c=0,d=b.length;c<d;c++)e.appendChild(a.H(b[c]));return e},Oa:function(b,e){for(var c=0,d=b.length,g=[];c<d;c++){var f=b[c].cloneNode(!0);g.push(e?a.H(f):f)}return g},P:function(b,e){a.a.oa(b);if(e)for(var c=0,d=e.length;c<d;c++)b.appendChild(e[c])},eb:function(b,e){var c=b.nodeType?[b]:b;if(0< -c.length){for(var d=c[0],g=d.parentNode,f=0,r=e.length;f<r;f++)g.insertBefore(e[f],d);f=0;for(r=c.length;f<r;f++)a.removeNode(c[f])}},hb:function(a,b){7>e?a.setAttribute("selected",b):a.selected=b},F:function(a){return null===a||a===q?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Wb:function(b,e){for(var c=[],d=(b||"").split(e),g=0,f=d.length;g<f;g++){var r=a.a.F(d[g]);""!==r&&c.push(r)}return c},Tb:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)=== -b},yb:function(a,b){if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;null!=a;){if(a==b)return!0;a=a.parentNode}return!1},aa:function(b){return a.a.yb(b,b.ownerDocument)},pb:function(b){return!!a.a.La(b,a.a.aa)},u:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},o:function(b,d,k){var f=e&&g[d];if(f||"undefined"==typeof t)if(f||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var n=function(a){k.call(b,a)},p="on"+d;b.attachEvent(p,n); -a.a.C.ia(b,function(){b.detachEvent(p,n)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(d,k,!1);else{if(c(b,d)){var r=k;k=function(a,b){var e=this.checked;b&&(this.checked=!0!==b.sb);r.call(this,a);this.checked=e}}t(b).bind(d,k)}},Ga:function(a,b){if(!a||!a.nodeType)throw Error("element must be a DOM node when calling triggerEvent");if("undefined"!=typeof t){var e=[];c(a,b)&&e.push({sb:a.checked});t(a).trigger(b,e)}else if("function"==typeof s.createEvent)if("function"== -typeof a.dispatchEvent)e=s.createEvent(f[b]||"HTMLEvents"),e.initEvent(b,!0,!0,w,0,0,0,0,0,!1,!1,!1,!1,0,a),a.dispatchEvent(e);else throw Error("The supplied element doesn't support dispatchEvent");else if("undefined"!=typeof a.fireEvent)c(a,b)&&(a.checked=!0!==a.checked),a.fireEvent("on"+b);else throw Error("Browser doesn't support triggering events");},c:function(b){return a.T(b)?b():b},ya:function(b){return a.T(b)?b.t():b},ga:function(b,e,c){if(e){var d=/\S+/g,g=b.className.match(d)||[];a.a.p(e.match(d), -function(b){a.a.ja(g,b,c)});b.className=g.join(" ")}},ib:function(b,e){var c=a.a.c(e);if(null===c||c===q)c="";var d=a.e.firstChild(b);!d||3!=d.nodeType||a.e.nextSibling(d)?a.e.P(b,[s.createTextNode(c)]):d.data=c;a.a.Bb(b)},gb:function(a,b){a.name=b;if(7>=e)try{a.mergeAttributes(s.createElement("<input name='"+a.name+"'/>"),!1)}catch(c){}},Bb:function(a){9<=e&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},zb:function(a){if(e){var b=a.style.width;a.style.width=0;a.style.width= -b}},Qb:function(b,e){b=a.a.c(b);e=a.a.c(e);for(var c=[],d=b;d<=e;d++)c.push(d);return c},N:function(a){for(var b=[],e=0,c=a.length;e<c;e++)b.push(a[e]);return b},Ub:6===e,Vb:7===e,ca:e,Ua:function(b,e){for(var c=a.a.N(b.getElementsByTagName("input")).concat(a.a.N(b.getElementsByTagName("textarea"))),d="string"==typeof e?function(a){return a.name===e}:function(a){return e.test(a.name)},g=[],f=c.length-1;0<=f;f--)d(c[f])&&g.push(c[f]);return g},Nb:function(b){return"string"==typeof b&&(b=a.a.F(b))? -y&&y.parse?y.parse(b):(new Function("return "+b))():null},Ca:function(b,e,c){if(!y||!y.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from https://www.json.org/json2.js");return y.stringify(a.a.c(b),e,c)},Ob:function(e,c,d){d=d||{};var g=d.params||{},f=d.includeFields||this.Ta,p=e;if("object"==typeof e&&"form"===a.a.u(e))for(var p=e.action,r=f.length-1;0<=r;r--)for(var z= -a.a.Ua(e,f[r]),D=z.length-1;0<=D;D--)g[z[D].name]=z[D].value;c=a.a.c(c);var q=s.createElement("form");q.style.display="none";q.action=p;q.method="post";for(var v in c)e=s.createElement("input"),e.name=v,e.value=a.a.Ca(a.a.c(c[v])),q.appendChild(e);b(g,function(a,b){var e=s.createElement("input");e.name=a;e.value=b;q.appendChild(e)});s.body.appendChild(q);d.submitter?d.submitter(q):q.submit();setTimeout(function(){q.parentNode.removeChild(q)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.p); -a.b("utils.arrayFirst",a.a.La);a.b("utils.arrayFilter",a.a.Y);a.b("utils.arrayGetDistinctValues",a.a.Ma);a.b("utils.arrayIndexOf",a.a.k);a.b("utils.arrayMap",a.a.Z);a.b("utils.arrayPushAll",a.a.R);a.b("utils.arrayRemoveItem",a.a.ka);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",a.a.Ta);a.b("utils.getFormFields",a.a.Ua);a.b("utils.peekObservable",a.a.ya);a.b("utils.postJson",a.a.Ob);a.b("utils.parseJson",a.a.Nb);a.b("utils.registerEventHandler",a.a.o);a.b("utils.stringifyJson", -a.a.Ca);a.b("utils.range",a.a.Qb);a.b("utils.toggleDomNodeCssClass",a.a.ga);a.b("utils.triggerEvent",a.a.Ga);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.w);a.b("utils.addOrRemoveItem",a.a.ja);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=function(a){var c=this,d=Array.prototype.slice.call(arguments);a=d.shift();return function(){return c.apply(a,d.concat(Array.prototype.slice.call(arguments)))}});a.a.f=new function(){var b=0,c="__ko__"+(new Date).getTime(), -d={};return{get:function(b,c){var e=a.a.f.pa(b,!1);return e===q?q:e[c]},set:function(b,c,e){if(e!==q||a.a.f.pa(b,!1)!==q)a.a.f.pa(b,!0)[c]=e},pa:function(a,g){var e=a[c];if(!e||"null"===e||!d[e]){if(!g)return q;e=a[c]="ko"+b++;d[e]={}}return d[e]},clear:function(a){var b=a[c];return b?(delete d[b],a[c]=null,!0):!1}}};a.b("utils.domData",a.a.f);a.b("utils.domData.clear",a.a.f.clear);a.a.C=new function(){function b(b,c){var g=a.a.f.get(b,d);g===q&&c&&(g=[],a.a.f.set(b,d,g));return g}function c(e){var d= -b(e,!1);if(d)for(var d=d.slice(0),f=0;f<d.length;f++)d[f](e);a.a.f.clear(e);"function"==typeof t&&"function"==typeof t.cleanData&&t.cleanData([e]);if(g[e.nodeType])for(d=e.firstChild;e=d;)d=e.nextSibling,8===e.nodeType&&c(e)}var d="__ko_domNodeDisposal__"+(new Date).getTime(),f={1:!0,8:!0,9:!0},g={1:!0,9:!0};return{ia:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},cb:function(e,c){var g=b(e,!1);g&&(a.a.ka(g,c),0==g.length&&a.a.f.set(e,d,q))},H:function(b){if(f[b.nodeType]&& -(c(b),g[b.nodeType])){var d=[];a.a.R(d,b.getElementsByTagName("*"));for(var h=0,k=d.length;h<k;h++)c(d[h])}return b},removeNode:function(b){a.H(b);b.parentNode&&b.parentNode.removeChild(b)}}};a.H=a.a.C.H;a.removeNode=a.a.C.removeNode;a.b("cleanNode",a.H);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.C);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.C.ia);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.C.cb);(function(){a.a.xa=function(b){var c;if("undefined"!=typeof t)if(t.parseHTML)c= -t.parseHTML(b)||[];else{if((c=t.clean([b]))&&c[0]){for(b=c[0];b.parentNode&&11!==b.parentNode.nodeType;)b=b.parentNode;b.parentNode&&b.parentNode.removeChild(b)}}else{var d=a.a.F(b).toLowerCase();c=s.createElement("div");d=d.match(/^<(thead|tbody|tfoot)/)&&[1,"<table>","</table>"]||!d.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!d.indexOf("<td")||!d.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||[0,"",""];b="ignored<div>"+d[1]+b+d[2]+"</div>";for("function"==typeof w.innerShiv? -c.appendChild(w.innerShiv(b)):c.innerHTML=b;d[0]--;)c=c.lastChild;c=a.a.N(c.lastChild.childNodes)}return c};a.a.fa=function(b,c){a.a.oa(b);c=a.a.c(c);if(null!==c&&c!==q)if("string"!=typeof c&&(c=c.toString()),"undefined"!=typeof t)t(b).html(c);else for(var d=a.a.xa(c),f=0;f<d.length;f++)b.appendChild(d[f])}})();a.b("utils.parseHtmlFragment",a.a.xa);a.b("utils.setHtml",a.a.fa);a.s=function(){function b(c,f){if(c)if(8==c.nodeType){var g=a.s.$a(c.nodeValue);null!=g&&f.push({xb:c,Kb:g})}else if(1==c.nodeType)for(var g= -0,e=c.childNodes,m=e.length;g<m;g++)b(e[g],f)}var c={};return{va:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},mb:function(a,b){var g=c[a];if(g===q)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return g.apply(null,b||[]), -!0}finally{delete c[a]}},nb:function(c,f){var g=[];b(c,g);for(var e=0,m=g.length;e<m;e++){var h=g[e].xb,k=[h];f&&a.a.R(k,f);a.s.mb(g[e].Kb,k);h.nodeValue="";h.parentNode&&h.parentNode.removeChild(h)}},$a:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.s);a.b("memoization.memoize",a.s.va);a.b("memoization.unmemoize",a.s.mb);a.b("memoization.parseMemoText",a.s.$a);a.b("memoization.unmemoizeDomNodeAndDescendants",a.s.nb);a.Sa={throttle:function(b,c){b.throttleEvaluation= -c;var d=null;return a.j({read:b,write:function(a){clearTimeout(d);d=setTimeout(function(){b(a)},c)}})},notify:function(b,c){b.equalityComparer="always"==c?F(!1):a.m.fn.equalityComparer;return b}};a.b("extenders",a.Sa);a.kb=function(b,c,d){this.target=b;this.la=c;this.wb=d;a.r(this,"dispose",this.B)};a.kb.prototype.B=function(){this.Hb=!0;this.wb()};a.V=function(){this.G={};a.a.extend(this,a.V.fn);a.r(this,"subscribe",this.Da);a.r(this,"extend",this.extend);a.r(this,"getSubscriptionsCount",this.Db)}; -a.V.fn={Da:function(b,c,d){d=d||"change";var f=new a.kb(this,c?b.bind(c):b,function(){a.a.ka(this.G[d],f)}.bind(this));this.G[d]||(this.G[d]=[]);this.G[d].push(f);return f},notifySubscribers:function(b,c){c=c||"change";this.G[c]&&a.q.I(function(){a.a.p(this.G[c].slice(0),function(a){a&&!0!==a.Hb&&a.la(b)})},this)},Db:function(){var b=0;a.a.w(this.G,function(a,d){b+=d.length});return b},extend:function(b){var c=this;b&&a.a.w(b,function(b,f){var g=a.Sa[b];"function"==typeof g&&(c=g(c,f))});return c}}; -a.Wa=function(a){return null!=a&&"function"==typeof a.Da&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.V);a.b("isSubscribable",a.Wa);a.q=function(){var b=[];return{rb:function(a){b.push({la:a,Ra:[]})},end:function(){b.pop()},bb:function(c){if(!a.Wa(c))throw Error("Only subscribable things can act as dependencies");if(0<b.length){var d=b[b.length-1];!d||0<=a.a.k(d.Ra,c)||(d.Ra.push(c),d.la(c))}},I:function(a,d,f){try{return b.push(null),a.apply(d,f||[])}finally{b.pop()}}}}();var L= -{undefined:!0,"boolean":!0,number:!0,string:!0};a.m=function(b){function c(){if(0<arguments.length)return c.equalityComparer&&c.equalityComparer(d,arguments[0])||(c.K(),d=arguments[0],c.J()),this;a.q.bb(c);return d}var d=b;a.V.call(c);c.t=function(){return d};c.J=function(){c.notifySubscribers(d)};c.K=function(){c.notifySubscribers(d,"beforeChange")};a.a.extend(c,a.m.fn);a.r(c,"peek",c.t);a.r(c,"valueHasMutated",c.J);a.r(c,"valueWillMutate",c.K);return c};a.m.fn={equalityComparer:function(a,c){return null=== -a||typeof a in L?a===c:!1}};var A=a.m.Pb="__ko_proto__";a.m.fn[A]=a.m;a.qa=function(b,c){return null===b||b===q||b[A]===q?!1:b[A]===c?!0:a.qa(b[A],c)};a.T=function(b){return a.qa(b,a.m)};a.Xa=function(b){return"function"==typeof b&&b[A]===a.m||"function"==typeof b&&b[A]===a.j&&b.Eb?!0:!1};a.b("observable",a.m);a.b("isObservable",a.T);a.b("isWriteableObservable",a.Xa);a.U=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined."); -b=a.m(b);a.a.extend(b,a.U.fn);return b};a.U.fn={remove:function(a){for(var c=this.t(),d=[],f="function"==typeof a?a:function(e){return e===a},g=0;g<c.length;g++){var e=c[g];f(e)&&(0===d.length&&this.K(),d.push(e),c.splice(g,1),g--)}d.length&&this.J();return d},removeAll:function(b){if(b===q){var c=this.t(),d=c.slice(0);this.K();c.splice(0,c.length);this.J();return d}return b?this.remove(function(c){return 0<=a.a.k(b,c)}):[]},destroy:function(a){var c=this.t(),d="function"==typeof a?a:function(c){return c=== -a};this.K();for(var f=c.length-1;0<=f;f--)d(c[f])&&(c[f]._destroy=!0);this.J()},destroyAll:function(b){return b===q?this.destroy(F(!0)):b?this.destroy(function(c){return 0<=a.a.k(b,c)}):[]},indexOf:function(b){var c=this();return a.a.k(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.K(),this.t()[d]=c,this.J())}};a.a.p("pop push reverse shift sort splice unshift".split(" "),function(b){a.U.fn[b]=function(){var a=this.t();this.K();a=a[b].apply(a,arguments);this.J();return a}});a.a.p(["slice"], -function(b){a.U.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.U);a.j=function(b,c,d){function f(){a.a.p(v,function(a){a.B()});v=[]}function g(){var a=m.throttleEvaluation;a&&0<=a?(clearTimeout(t),t=setTimeout(e,a)):e()}function e(){if(!n)if(l&&D())x();else{n=!0;try{var b=a.a.Z(v,function(a){return a.target});a.q.rb(function(e){var c;0<=(c=a.a.k(b,e))?b[c]=q:v.push(e.Da(g))});for(var e=p.call(c),d=b.length-1;0<=d;d--)b[d]&&v.splice(d,1)[0].B();l=!0;m.notifySubscribers(k, -"beforeChange");k=e;m.notifySubscribers(k)}finally{a.q.end(),n=!1}v.length||x()}}function m(){if(0<arguments.length){if("function"===typeof r)r.apply(c,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");return this}l||e();a.q.bb(m);return k}function h(){return!l||0<v.length}var k,l=!1,n=!1,p=b;p&&"object"==typeof p?(d=p,p=d.read):(d=d||{},p||(p=d.read));if("function"!=typeof p)throw Error("Pass a function that returns the value of the ko.computed"); -var r=d.write,z=d.disposeWhenNodeIsRemoved||d.$||null,D=d.disposeWhen||d.Qa||F(!1),x=f,v=[],t=null;c||(c=d.owner);m.t=function(){l||e();return k};m.Cb=function(){return v.length};m.Eb="function"===typeof d.write;m.B=function(){x()};m.ta=h;a.V.call(m);a.a.extend(m,a.j.fn);a.r(m,"peek",m.t);a.r(m,"dispose",m.B);a.r(m,"isActive",m.ta);a.r(m,"getDependenciesCount",m.Cb);!0!==d.deferEvaluation&&e();if(z&&h()){x=function(){a.a.C.cb(z,x);f()};a.a.C.ia(z,x);var s=D,D=function(){return!a.a.aa(z)||s()}}return m}; -a.Gb=function(b){return a.qa(b,a.j)};C=a.m.Pb;a.j[C]=a.m;a.j.fn={};a.j.fn[C]=a.j;a.b("dependentObservable",a.j);a.b("computed",a.j);a.b("isComputed",a.Gb);(function(){function b(a,g,e){e=e||new d;a=g(a);if("object"!=typeof a||null===a||a===q||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var m=a instanceof Array?[]:{};e.save(a,m);c(a,function(c){var d=g(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":m[c]=d;break;case "object":case "undefined":var l= -e.get(d);m[c]=l!==q?l:b(d,g,e)}});return m}function c(a,b){if(a instanceof Array){for(var e=0;e<a.length;e++)b(e);"function"==typeof a.toJSON&&b("toJSON")}else for(e in a)b(e)}function d(){this.keys=[];this.Ha=[]}a.lb=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var e=0;a.T(b)&&10>e;e++)b=b();return b})};a.toJSON=function(b,c,e){b=a.lb(b);return a.a.Ca(b,c,e)};d.prototype={save:function(b,c){var e=a.a.k(this.keys, -b);0<=e?this.Ha[e]=c:(this.keys.push(b),this.Ha.push(c))},get:function(b){b=a.a.k(this.keys,b);return 0<=b?this.Ha[b]:q}}})();a.b("toJS",a.lb);a.b("toJSON",a.toJSON);(function(){a.h={n:function(b){switch(a.a.u(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.f.get(b,a.d.options.wa):7>=a.a.ca?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.h.n(b.options[b.selectedIndex]):q;default:return b.value}},W:function(b, -c){switch(a.a.u(b)){case "option":switch(typeof c){case "string":a.a.f.set(b,a.d.options.wa,q);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.f.set(b,a.d.options.wa,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":""===c&&(c=q);if(null===c||c===q)b.selectedIndex=-1;for(var d=b.options.length-1;0<=d;d--)if(a.h.n(b.options[d])==c){b.selectedIndex=d;break}1<b.size||-1!==b.selectedIndex||(b.selectedIndex= -0);break;default:if(null===c||c===q)c="";b.value=c}}}})();a.b("selectExtensions",a.h);a.b("selectExtensions.readValue",a.h.n);a.b("selectExtensions.writeValue",a.h.W);a.g=function(){function b(a,b){for(var d=null;a!=d;)d=a,a=a.replace(c,function(a,c){return b[c]});return a}var c=/\@ko_token_(\d+)\@/g,d=["true","false","null","undefined"],f=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;return{S:[],da:function(c){var e=a.a.F(c);if(3>e.length)return[];"{"===e.charAt(0)&&(e=e.substring(1,e.length- -1));c=[];for(var d=null,f,k=0;k<e.length;k++){var l=e.charAt(k);if(null===d)switch(l){case '"':case "'":case "/":d=k,f=l}else if(l==f&&"\\"!==e.charAt(k-1)){l=e.substring(d,k+1);c.push(l);var n="@ko_token_"+(c.length-1)+"@",e=e.substring(0,d)+n+e.substring(k+1),k=k-(l.length-n.length),d=null}}f=d=null;for(var p=0,r=null,k=0;k<e.length;k++){l=e.charAt(k);if(null===d)switch(l){case "{":d=k;r=l;f="}";break;case "(":d=k;r=l;f=")";break;case "[":d=k,r=l,f="]"}l===r?p++:l===f&&(p--,0===p&&(l=e.substring(d, -k+1),c.push(l),n="@ko_token_"+(c.length-1)+"@",e=e.substring(0,d)+n+e.substring(k+1),k-=l.length-n.length,d=null))}f=[];e=e.split(",");d=0;for(k=e.length;d<k;d++)p=e[d],r=p.indexOf(":"),0<r&&r<p.length-1?(l=p.substring(r+1),f.push({key:b(p.substring(0,r),c),value:b(l,c)})):f.push({unknown:b(p,c)});return f},ea:function(b){var e="string"===typeof b?a.g.da(b):b,c=[];b=[];for(var h,k=0;h=e[k];k++)if(0<c.length&&c.push(","),h.key){var l;a:{l=h.key;var n=a.a.F(l);switch(n.length&&n.charAt(0)){case "'":case '"':break a; -default:l="'"+n+"'"}}h=h.value;c.push(l);c.push(":");c.push(h);h=a.a.F(h);0<=a.a.k(d,a.a.F(h).toLowerCase())?h=!1:(n=h.match(f),h=null===n?!1:n[1]?"Object("+n[1]+")"+n[2]:h);h&&(0<b.length&&b.push(", "),b.push(l+" : function(__ko_value) { "+h+" = __ko_value; }"))}else h.unknown&&c.push(h.unknown);e=c.join("");0<b.length&&(e=e+", '_ko_property_writers' : { "+b.join("")+" } ");return e},Jb:function(b,c){for(var d=0;d<b.length;d++)if(a.a.F(b[d].key)==c)return!0;return!1},ha:function(b,c,d,f,k){if(b&& -a.T(b))!a.Xa(b)||k&&b.t()===f||b(f);else if((b=c()._ko_property_writers)&&b[d])b[d](f)}}}();a.b("expressionRewriting",a.g);a.b("expressionRewriting.bindingRewriteValidators",a.g.S);a.b("expressionRewriting.parseObjectLiteral",a.g.da);a.b("expressionRewriting.preProcessBindings",a.g.ea);a.b("jsonExpressionRewriting",a.g);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.g.ea);(function(){function b(a){return 8==a.nodeType&&(g?a.text:a.nodeValue).match(e)}function c(a){return 8==a.nodeType&& -(g?a.text:a.nodeValue).match(m)}function d(a,e){for(var d=a,g=1,f=[];d=d.nextSibling;){if(c(d)&&(g--,0===g))return f;f.push(d);b(d)&&g++}if(!e)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function f(a,b){var c=d(a,b);return c?0<c.length?c[c.length-1].nextSibling:a.nextSibling:null}var g=s&&"\x3c!--test--\x3e"===s.createComment("test").text,e=g?/^\x3c!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*--\x3e$/:/^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/,m=g?/^\x3c!--\s*\/ko\s*--\x3e$/: -/^\s*\/ko\s*$/,h={ul:!0,ol:!0};a.e={L:{},childNodes:function(a){return b(a)?d(a):a.childNodes},ba:function(c){if(b(c)){c=a.e.childNodes(c);for(var e=0,d=c.length;e<d;e++)a.removeNode(c[e])}else a.a.oa(c)},P:function(c,e){if(b(c)){a.e.ba(c);for(var d=c.nextSibling,g=0,f=e.length;g<f;g++)d.parentNode.insertBefore(e[g],d)}else a.a.P(c,e)},ab:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},Va:function(c,e,d){d?b(c)?c.parentNode.insertBefore(e, -d.nextSibling):d.nextSibling?c.insertBefore(e,d.nextSibling):c.appendChild(e):a.e.ab(c,e)},firstChild:function(a){return b(a)?!a.nextSibling||c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=f(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},ob:function(a){return(a=b(a))?a[1]:null},Za:function(e){if(h[a.a.u(e)]){var d=e.firstChild;if(d){do if(1===d.nodeType){var g;g=d.firstChild;var m=null;if(g){do if(m)m.push(g);else if(b(g)){var r=f(g,!0);r?g=r:m= -[g]}else c(g)&&(m=[g]);while(g=g.nextSibling)}if(g=m)for(m=d.nextSibling,r=0;r<g.length;r++)m?e.insertBefore(g[r],m):e.appendChild(g[r])}while(d=d.nextSibling)}}}}})();a.b("virtualElements",a.e);a.b("virtualElements.allowedBindings",a.e.L);a.b("virtualElements.emptyNode",a.e.ba);a.b("virtualElements.insertAfter",a.e.Va);a.b("virtualElements.prepend",a.e.ab);a.b("virtualElements.setDomNodeChildren",a.e.P);(function(){a.M=function(){this.Na={}};a.a.extend(a.M.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!= -b.getAttribute("data-bind");case 8:return null!=a.e.ob(b);default:return!1}},getBindings:function(a,c){var d=this.getBindingsString(a,c);return d?this.parseBindingsString(d,c,a):null},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.e.ob(b);default:return null}},parseBindingsString:function(b,c,d){try{var f;if(!(f=this.Na[b])){var g=this.Na,e,m="with($context){with($data||{}){return{"+a.g.ea(b)+"}}}";e=new Function("$context","$element",m); -f=g[b]=e}return f(c,d)}catch(h){throw h.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+h.message,h;}}});a.M.instance=new a.M})();a.b("bindingProvider",a.M);(function(){function b(b,e,d){for(var f=a.e.firstChild(e);e=f;)f=a.e.nextSibling(e),c(b,e,d)}function c(c,e,f){var h=!0,k=1===e.nodeType;k&&a.e.Za(e);if(k&&f||a.M.instance.nodeHasBindings(e))h=d(e,null,c,f).Sb;h&&b(c,e,!k)}function d(b,c,d,h){function k(a){return function(){return p[a]}}function l(){return p}var n=0,p,r, -z=a.a.f.get(b,f);if(!c){if(z)throw Error("You cannot apply bindings multiple times to the same element.");a.a.f.set(b,f,!0)}a.j(function(){var f=d&&d instanceof a.A?d:new a.A(a.a.c(d)),x=f.$data;!z&&h&&a.jb(b,f);if(p=("function"==typeof c?c(f,b):c)||a.M.instance.getBindings(b,f))0===n&&(n=1,a.a.w(p,function(c){var e=a.d[c];if(e&&8===b.nodeType&&!a.e.L[c])throw Error("The binding '"+c+"' cannot be used with virtual elements");if(e&&"function"==typeof e.init&&(e=(0,e.init)(b,k(c),l,x,f))&&e.controlsDescendantBindings){if(r!== -q)throw Error("Multiple bindings ("+r+" and "+c+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");r=c}}),n=2),2===n&&a.a.w(p,function(c){var e=a.d[c];e&&"function"==typeof e.update&&(0,e.update)(b,k(c),l,x,f)})},null,{$:b});return{Sb:r===q}}a.d={};a.A=function(b,c,d){c?(a.a.extend(this,c),this.$parentContext=c,this.$parent=c.$data,this.$parents=(c.$parents||[]).slice(0),this.$parents.unshift(this.$parent)):(this.$parents= -[],this.$root=b,this.ko=a);this.$data=b;d&&(this[d]=b)};a.A.prototype.createChildContext=function(b,c){return new a.A(b,this,c)};a.A.prototype.extend=function(b){var c=a.a.extend(new a.A,this);return a.a.extend(c,b)};var f="__ko_boundElement";a.jb=function(b,c){if(2==arguments.length)a.a.f.set(b,"__ko_bindingContext__",c);else return a.a.f.get(b,"__ko_bindingContext__")};a.Ka=function(b,c,f){1===b.nodeType&&a.e.Za(b);return d(b,c,f,!0)};a.Ja=function(a,c){1!==c.nodeType&&8!==c.nodeType||b(a,c,!0)}; -a.Ia=function(a,b){if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||w.document.body;c(a,b,!0)};a.na=function(b){switch(b.nodeType){case 1:case 8:var c=a.jb(b);if(c)return c;if(b.parentNode)return a.na(b.parentNode)}return q};a.ub=function(b){return(b=a.na(b))?b.$data:q};a.b("bindingHandlers",a.d);a.b("applyBindings",a.Ia);a.b("applyBindingsToDescendants",a.Ja);a.b("applyBindingsToNode",a.Ka); -a.b("contextFor",a.na);a.b("dataFor",a.ub)})();var K={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.w(d,function(c,d){d=a.a.c(d);var e=!1===d||null===d||d===q;e&&b.removeAttribute(c);8>=a.a.ca&&c in K?(c=K[c],e?b.removeAttribute(c):b[c]=d):e||b.setAttribute(c,d.toString());"name"===c&&a.a.gb(b,e?"":d.toString())})}};a.d.checked={init:function(b,c,d){a.a.o(b,"click",function(){var f;if("checkbox"==b.type)f=b.checked;else if("radio"==b.type&&b.checked)f= -b.value;else return;var g=c(),e=a.a.c(g);"checkbox"==b.type&&e instanceof Array?a.a.ja(g,b.value,b.checked):a.g.ha(g,d,"checked",f,!0)});"radio"!=b.type||b.name||a.d.uniqueName.init(b,F(!0))},update:function(b,c){var d=a.a.c(c());"checkbox"==b.type?b.checked=d instanceof Array?0<=a.a.k(d,b.value):d:"radio"==b.type&&(b.checked=b.value==d)}};a.d.css={update:function(b,c){var d=a.a.c(c());"object"==typeof d?a.a.w(d,function(c,d){d=a.a.c(d);a.a.ga(b,c,d)}):(d=String(d||""),a.a.ga(b,b.__ko__cssValue,!1), -b.__ko__cssValue=d,a.a.ga(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,f){var g=c()||{};a.a.w(g,function(e){"string"==typeof e&&a.a.o(b,e,function(b){var g,k=c()[e];if(k){var l=d();try{var n=a.a.N(arguments);n.unshift(f);g=k.apply(f,n)}finally{!0!==g&&(b.preventDefault?b.preventDefault():b.returnValue= -!1)}!1===l[e+"Bubble"]&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={Ya:function(b){return function(){var c=b(),d=a.a.ya(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.D.sa};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.D.sa}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.Ya(c))}, -update:function(b,c,d,f,g){return a.d.template.update(b,a.d.foreach.Ya(c),d,f,g)}};a.g.S.foreach=!1;a.e.L.foreach=!0;a.d.hasfocus={init:function(b,c,d){function f(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(l){g=f.body}e=g===b}f=c();a.g.ha(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var g=f.bind(null,!0),e=f.bind(null,!1);a.a.o(b,"focus",g);a.a.o(b,"focusin",g);a.a.o(b,"blur",e);a.a.o(b,"focusout",e)}, -update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===d||(d?b.focus():b.blur(),a.q.I(a.a.Ga,null,[b,d?"focusin":"focusout"]))}};a.d.hasFocus=a.d.hasfocus;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.fa(b,c())}};var I="__ko_withIfBindingData";G("if");G("ifnot",!1,!0);G("with",!0,!1,function(a,c){return a.createChildContext(c)});a.d.options={init:function(b){if("select"!==a.a.u(b))throw Error("options binding applies only to SELECT elements"); -for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(b,c){if(p){var d=0<=a.a.k(p,a.h.n(c[0]));a.a.hb(c[0],d)}}var e=0==b.length,m=!e&&b.multiple?b.scrollTop:null;c=a.a.c(c());var h=d(),k=h.optionsIncludeDestroyed,l={},n,p;b.multiple?p=a.a.Z(b.selectedOptions||a.a.Y(b.childNodes,function(b){return b.tagName&&"option"===a.a.u(b)&&b.selected}),function(b){return a.h.n(b)}):0<= -b.selectedIndex&&(p=[a.h.n(b.options[b.selectedIndex])]);if(c){"undefined"==typeof c.length&&(c=[c]);var r=a.a.Y(c,function(b){return k||b===q||null===b||!a.a.c(b._destroy)});"optionsCaption"in h&&(n=a.a.c(h.optionsCaption),null!==n&&n!==q&&r.unshift(l))}else c=[];d=g;h.optionsAfterRender&&(d=function(b,c){g(0,c);a.q.I(h.optionsAfterRender,null,[c[0],b!==l?b:q])});a.a.Aa(b,r,function(b,c,d){d.length&&(p=d[0].selected&&[a.h.n(d[0])]);c=s.createElement("option");b===l?(a.a.fa(c,n),a.h.W(c,q)):(d=f(b, -h.optionsValue,b),a.h.W(c,a.a.c(d)),b=f(b,h.optionsText,d),a.a.ib(c,b));return[c]},null,d);p=null;e&&"value"in h&&J(b,a.a.ya(h.value),!0);a.a.zb(b);m&&20<Math.abs(m-b.scrollTop)&&(b.scrollTop=m)}};a.d.options.wa="__ko.optionValueDomData__";a.d.selectedOptions={init:function(b,c,d){a.a.o(b,"change",function(){var f=c(),g=[];a.a.p(b.getElementsByTagName("option"),function(b){b.selected&&g.push(a.h.n(b))});a.g.ha(f,d,"selectedOptions",g)})},update:function(b,c){if("select"!=a.a.u(b))throw Error("values binding applies only to SELECT elements"); -var d=a.a.c(c());d&&"number"==typeof d.length&&a.a.p(b.getElementsByTagName("option"),function(b){var c=0<=a.a.k(d,a.h.n(b));a.a.hb(b,c)})}};a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.w(d,function(c,d){d=a.a.c(d);b.style[c]=d||""})}};a.d.submit={init:function(b,c,d,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");a.a.o(b,"submit",function(a){var d,m=c();try{d=m.call(f,b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}}; -a.d.text={update:function(b,c){a.a.ib(b,c())}};a.e.L.text=!0;a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.d.uniqueName.tb;a.a.gb(b,d)}}};a.d.uniqueName.tb=0;a.d.value={init:function(b,c,d){function f(){m=!1;var e=c(),f=a.h.n(b);a.g.ha(e,d,"value",f)}var g=["change"],e=d().valueUpdate,m=!1;e&&("string"==typeof e&&(e=[e]),a.a.R(g,e),g=a.a.Ma(g));!a.a.ca||("input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete)||-1!=a.a.k(g,"propertychange")|| -(a.a.o(b,"propertychange",function(){m=!0}),a.a.o(b,"blur",function(){m&&f()}));a.a.p(g,function(c){var d=f;a.a.Tb(c,"after")&&(d=function(){setTimeout(f,0)},c=c.substring(5));a.a.o(b,c,d)})},update:function(b,c){var d="select"===a.a.u(b),f=a.a.c(c()),g=a.h.n(b);f!==g&&(g=function(){a.h.W(b,f)},g(),d&&setTimeout(g,0));d&&0<b.length&&J(b,f,!1)}};a.d.visible={update:function(b,c){var d=a.a.c(c()),f="none"!=b.style.display;d&&!f?b.style.display="":!d&&f&&(b.style.display="none")}};(function(b){a.d[b]= -{init:function(c,d,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},f,g)}}})("click");a.v=function(){};a.v.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.v.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.v.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||s;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.l.i(d)}if(1== -b.nodeType||8==b.nodeType)return new a.l.Q(b);throw Error("Unknown template type: "+b);};a.v.prototype.renderTemplate=function(a,c,d,f){a=this.makeTemplateSource(a,f);return this.renderTemplateSource(a,c,d)};a.v.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.v.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.v); -a.Ea=function(){function b(b,c,d,m){b=a.g.da(b);for(var h=a.g.S,k=0;k<b.length;k++){var l=b[k].key;if(h.hasOwnProperty(l)){var n=h[l];if("function"===typeof n){if(l=n(b[k].value))throw Error(l);}else if(!n)throw Error("This template engine does not support the '"+l+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.g.ea(b)+" } })()},'"+d.toLowerCase()+"')";return m.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi, -d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Ab:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Ea.Lb(b,c)},d)},Lb:function(a,g){return a.replace(c,function(a,c,d,f,l){return b(l,c,d,g)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",g)})},qb:function(b,c){return a.s.va(function(d,m){var h=d.nextSibling;h&&h.nodeName.toLowerCase()===c&&a.Ka(h,b,m)})}}}();a.b("__tr_ambtns",a.Ea.qb);(function(){a.l={};a.l.i=function(a){this.i=a};a.l.i.prototype.text= -function(){var b=a.a.u(this.i),b="script"===b?"text":"textarea"===b?"value":"innerHTML";if(0==arguments.length)return this.i[b];var c=arguments[0];"innerHTML"===b?a.a.fa(this.i,c):this.i[b]=c};a.l.i.prototype.data=function(b){if(1===arguments.length)return a.a.f.get(this.i,"templateSourceData_"+b);a.a.f.set(this.i,"templateSourceData_"+b,arguments[1])};a.l.Q=function(a){this.i=a};a.l.Q.prototype=new a.l.i;a.l.Q.prototype.text=function(){if(0==arguments.length){var b=a.a.f.get(this.i,"__ko_anon_template__")|| -{};b.Fa===q&&b.ma&&(b.Fa=b.ma.innerHTML);return b.Fa}a.a.f.set(this.i,"__ko_anon_template__",{Fa:arguments[0]})};a.l.i.prototype.nodes=function(){if(0==arguments.length)return(a.a.f.get(this.i,"__ko_anon_template__")||{}).ma;a.a.f.set(this.i,"__ko_anon_template__",{ma:arguments[0]})};a.b("templateSources",a.l);a.b("templateSources.domElement",a.l.i);a.b("templateSources.anonymousTemplate",a.l.Q)})();(function(){function b(b,c,d){var f;for(c=a.e.nextSibling(c);b&&(f=b)!==c;)b=a.e.nextSibling(f),1!== -f.nodeType&&8!==f.nodeType||d(f)}function c(c,d){if(c.length){var f=c[0],g=c[c.length-1];b(f,g,function(b){a.Ia(d,b)});b(f,g,function(b){a.s.nb(b,[d])})}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function f(b,f,h,k,l){l=l||{};var n=b&&d(b),n=n&&n.ownerDocument,p=l.templateEngine||g;a.Ea.Ab(h,p,n);h=p.renderTemplate(h,k,l,n);if("number"!=typeof h.length||0<h.length&&"number"!=typeof h[0].nodeType)throw Error("Template engine must return an array of DOM nodes");n=!1;switch(f){case "replaceChildren":a.e.P(b, -h);n=!0;break;case "replaceNode":a.a.eb(b,h);n=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+f);}n&&(c(h,k),l.afterRender&&a.q.I(l.afterRender,null,[h,k.$data]));return h}var g;a.Ba=function(b){if(b!=q&&!(b instanceof a.v))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.za=function(b,c,h,k,l){h=h||{};if((h.templateEngine||g)==q)throw Error("Set a template engine before calling renderTemplate");l=l||"replaceChildren";if(k){var n=d(k);return a.j(function(){var g= -c&&c instanceof a.A?c:new a.A(a.a.c(c)),r="function"==typeof b?b(g.$data,g):b,g=f(k,l,r,g,h);"replaceNode"==l&&(k=g,n=d(k))},null,{Qa:function(){return!n||!a.a.aa(n)},$:n&&"replaceNode"==l?n.parentNode:n})}return a.s.va(function(d){a.za(b,c,h,d,"replaceNode")})};a.Rb=function(b,d,g,k,l){function n(a,b){c(b,r);g.afterRender&&g.afterRender(b,a)}function p(c,d){r=l.createChildContext(a.a.c(c),g.as);r.$index=d;var k="function"==typeof b?b(c,r):b;return f(null,"ignoreTargetNode",k,r,g)}var r;return a.j(function(){var b= -a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Y(b,function(b){return g.includeDestroyed||b===q||null===b||!a.a.c(b._destroy)});a.q.I(a.a.Aa,null,[k,b,p,g,n])},null,{$:k})};a.d.template={init:function(b,c){var d=a.a.c(c());"string"==typeof d||(d.name||1!=b.nodeType&&8!=b.nodeType)||(d=1==b.nodeType?b.childNodes:a.e.childNodes(b),d=a.a.Mb(d),(new a.l.Q(b)).nodes(d));return{controlsDescendantBindings:!0}},update:function(b,c,d,f,g){c=a.a.c(c());d={};f=!0;var n,p=null;"string"!=typeof c&&(d= -c,c=a.a.c(d.name),"if"in d&&(f=a.a.c(d["if"])),f&&"ifnot"in d&&(f=!a.a.c(d.ifnot)),n=a.a.c(d.data));"foreach"in d?p=a.Rb(c||b,f&&d.foreach||[],d,b,g):f?(g="data"in d?g.createChildContext(n,d.as):g,p=a.za(c||b,g,d,b)):a.e.ba(b);g=p;(n=a.a.f.get(b,"__ko__templateComputedDomDataKey__"))&&"function"==typeof n.B&&n.B();a.a.f.set(b,"__ko__templateComputedDomDataKey__",g&&g.ta()?g:q)}};a.g.S.template=function(b){b=a.g.da(b);return 1==b.length&&b[0].unknown||a.g.Jb(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"}; -a.e.L.template=!0})();a.b("setTemplateEngine",a.Ba);a.b("renderTemplate",a.za);a.a.Pa=function(){function a(b,d,f,g,e){var m=Math.min,h=Math.max,k=[],l,n=b.length,p,r=d.length,q=r-n||1,t=n+r+1,s,v,w;for(l=0;l<=n;l++)for(v=s,k.push(s=[]),w=m(r,l+q),p=h(0,l-1);p<=w;p++)s[p]=p?l?b[l-1]===d[p-1]?v[p-1]:m(v[p]||t,s[p-1]||t)+1:p+1:l+1;m=[];h=[];q=[];l=n;for(p=r;l||p;)r=k[l][p]-1,p&&r===k[l][p-1]?h.push(m[m.length]={status:f,value:d[--p],index:p}):l&&r===k[l-1][p]?q.push(m[m.length]={status:g,value:b[--l], -index:l}):(m.push({status:"retained",value:d[--p]}),--l);if(h.length&&q.length){b=10*n;var E;for(d=f=0;(e||d<b)&&(E=h[f]);f++){for(g=0;k=q[g];g++)if(E.value===k.value){E.moved=k.index;k.moved=E.index;q.splice(g,1);d=g=0;break}d+=g}}return m.reverse()}return function(c,d,f){c=c||[];d=d||[];return c.length<=d.length?a(c,d,"added","deleted",f):a(d,c,"deleted","added",f)}}();a.b("utils.compareArrays",a.a.Pa);(function(){function b(b){for(;b.length&&!a.a.aa(b[0]);)b.splice(0,1);if(1<b.length){for(var c= -b[0],g=b[b.length-1],e=[c];c!==g;){c=c.nextSibling;if(!c)return;e.push(c)}Array.prototype.splice.apply(b,[0,b.length].concat(e))}return b}function c(c,f,g,e,m){var h=[];c=a.j(function(){var c=f(g,m,b(h))||[];0<h.length&&(a.a.eb(h,c),e&&a.q.I(e,null,[g,c,m]));h.splice(0,h.length);a.a.R(h,c)},null,{$:c,Qa:function(){return!a.a.pb(h)}});return{O:h,j:c.ta()?c:q}}a.a.Aa=function(d,f,g,e,m){function h(a,c){u=n[c];x!==c&&(E[a]=u);u.ra(x++);b(u.O);t.push(u);w.push(u)}function k(b,c){if(b)for(var d=0,e=c.length;d< -e;d++)c[d]&&a.a.p(c[d].O,function(a){b(a,d,c[d].X)})}f=f||[];e=e||{};var l=a.a.f.get(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult")===q,n=a.a.f.get(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult")||[],p=a.a.Z(n,function(a){return a.X}),r=a.a.Pa(p,f,e.dontLimitMoves),t=[],s=0,x=0,v=[],w=[];f=[];for(var E=[],p=[],u,B=0,y,A;y=r[B];B++)switch(A=y.moved,y.status){case "deleted":A===q&&(u=n[s],u.j&&u.j.B(),v.push.apply(v,b(u.O)),e.beforeRemove&&(f[B]=u,w.push(u)));s++;break;case "retained":h(B, -s++);break;case "added":A!==q?h(B,A):(u={X:y.value,ra:a.m(x++)},t.push(u),w.push(u),l||(p[B]=u))}k(e.beforeMove,E);a.a.p(v,e.beforeRemove?a.H:a.removeNode);for(var B=0,l=a.e.firstChild(d),C;u=w[B];B++){u.O||a.a.extend(u,c(d,g,u.X,m,u.ra));for(s=0;r=u.O[s];l=r.nextSibling,C=r,s++)r!==l&&a.e.Va(d,r,C);!u.Fb&&m&&(m(u.X,u.O,u.ra),u.Fb=!0)}k(e.beforeRemove,f);k(e.afterMove,E);k(e.afterAdd,p);a.a.f.set(d,"setDomNodeChildrenFromArrayMapping_lastMappingResult",t)}})();a.b("utils.setDomNodeChildrenFromArrayMapping", -a.a.Aa);a.D=function(){this.allowTemplateRewriting=!1};a.D.prototype=new a.v;a.D.prototype.renderTemplateSource=function(b){var c=(9>a.a.ca?0:b.nodes)?b.nodes():null;if(c)return a.a.N(c.cloneNode(!0).childNodes);b=b.text();return a.a.xa(b)};a.D.sa=new a.D;a.Ba(a.D.sa);a.b("nativeTemplateEngine",a.D);(function(){a.ua=function(){var a=this.Ib=function(){if("undefined"==typeof t||!t.tmpl)return 0;try{if(0<=t.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource= -function(b,f,g){g=g||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var e=b.data("precompiled");e||(e=b.text()||"",e=t.template(null,"{{ko_with $item.koBindingContext}}"+e+"{{/ko_with}}"),b.data("precompiled",e));b=[f.$data];f=t.extend({koBindingContext:f},g.templateOptions);f=t.tmpl(e,b,f);f.appendTo(s.createElement("div"));t.fragments={};return f};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+ -a+" })()) }}"};this.addTemplate=function(a,b){s.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(t.tmpl.tag.ko_code={open:"__.push($1 || '');"},t.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.ua.prototype=new a.v;var b=new a.ua;0<b.Ib&&a.Ba(b);a.b("jqueryTmplTemplateEngine",a.ua)})()})})(); -})(); \ No newline at end of file diff --git a/samples/javaconfig/hellojs/src/main/resources/resources/js/message.js b/samples/javaconfig/hellojs/src/main/resources/resources/js/message.js deleted file mode 100644 index 01bb6ed9ca0..00000000000 --- a/samples/javaconfig/hellojs/src/main/resources/resources/js/message.js +++ /dev/null @@ -1,75 +0,0 @@ -function Message(data) { - this.id = ko.observable(data.id) - this.text = ko.observable(data.text) - this.summary = ko.observable(data.summary) - this.created = ko.observable(new Date(data.created)) -} - -function MessageListViewModel() { - var self = this; - self.messages = ko.observableArray([]); - self.chosenMessageData = ko.observable(); - self.inbox = ko.observable(); - self.compose = ko.observable(); - self.errors = ko.observableArray([]); - - self.goToMessage = function(message) { - self.inbox(null); - $.getJSON("./" + message.id(), function(data) { - self.chosenMessageData(new Message(data)); - }); - }; - - self.goToCompose = function(data) { - self.inbox(null); - self.chosenMessageData(null); - self.compose(new Message([])); - }; - - self.goToInbox = function() { - $.getJSON("./", function(allData) { - var mappedMessages = $.map(allData, function(item) { return new Message(item) }); - self.messages(mappedMessages); - self.inbox(mappedMessages); - self.chosenMessageData(null); - self.compose(null); - }); - } - - self.save = function() { - $.ajax("./", { - data: ko.toJSON(self.compose), - type: "post", contentType: "application/json", - success: function() { - self.goToInbox(); - } - }); - }; - - self.goToInbox(); -} - -$(function () { - var messageModel = new MessageListViewModel(); - var token = $("meta[name='_csrf']").attr("content"); - var header = $("meta[name='_csrf_header']").attr("content"); - $(document).ajaxSend(function(e, xhr, options) { - messageModel.errors.removeAll(); - xhr.setRequestHeader( "Content-type", "application/json" ); - xhr.setRequestHeader(header, token); - }); - $(document).ajaxError(function( event, jqxhr, settings, exception ) { - if (jqxhr.status == 401 ) { - window.location = "./login"; - } else if(jqxhr.status == 400) { - var errors = $.parseJSON(jqxhr.responseText); - for (var i = 0; i < errors.length; i++) { - messageModel.errors.push(errors[i]); - } - } else { - alert("Error processing "+ settings.url); - } - }); - ko.applyBindings(messageModel) -}); - diff --git a/samples/javaconfig/hellojs/src/main/resources/views/messages/inbox.html b/samples/javaconfig/hellojs/src/main/resources/views/messages/inbox.html deleted file mode 100644 index 320645f38ba..00000000000 --- a/samples/javaconfig/hellojs/src/main/resources/views/messages/inbox.html +++ /dev/null @@ -1,158 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head> - <title>SecureMail</title> - <link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}"/> - <link th:href="@{/resources/css/bootstrap.css}" rel="stylesheet"></link> - <style type="text/css"> - /* Sticky footer styles - -------------------------------------------------- */ - - html, - body { - height: 100%; - /* The html and body elements cannot have any padding or margin. */ - } - - /* Wrapper for page content to push down footer */ - #wrap { - min-height: 100%; - height: auto !important; - height: 100%; - /* Negative indent footer by it's height */ - margin: 0 auto -60px; - } - - /* Set the fixed height of the footer here */ - #push, - #footer { - height: 60px; - } - #footer { - background-color: #f5f5f5; - } - - /* Lastly, apply responsive CSS fixes as necessary */ - @media (max-width: 767px) { - #footer { - margin-left: -20px; - margin-right: -20px; - padding-left: 20px; - padding-right: 20px; - } - } - - - - /* Custom page CSS - -------------------------------------------------- */ - /* Not required for template or sticky footer method. */ - - .container { - width: auto; - max-width: 680px; - } - .container .credit { - margin: 20px 0; - text-align: center; - } - a { - color: green; - } - .navbar-form { - margin-left: 1em; - } - </style> - <link th:href="@{/resources/css/bootstrap-responsive.css}" rel="stylesheet"></link> - - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - <meta name="_csrf" th:content="${_csrf.token}"/> - <meta name="_csrf_header" th:content="${_csrf.headerName}"/> - </head> - - - <body> - <div id="wrap"> - <div class="navbar navbar-inverse navbar-static-top"> - <div class="navbar-inner"> - <div class="container"> - <a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a> - <div class="nav-collapse collapse"> - <div th:if="${#httpServletRequest.remoteUser != null}"> - <form class="navbar-form pull-right" th:action="@{/logout}" method="post"> - <input type="submit" value="Log out" /> - </form> - <p class="navbar-text pull-right" th:text="${#httpServletRequest.remoteUser}"> - sample_user - </p> - </div> - <ul class="nav"> - <li><a data-bind="click: $root.goToInbox" th:href="@{/}">Inbox</a></li> - <li><a data-bind="click: $root.goToCompose" th:href="@{/(form)}">Compose</a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Begin page content --> - <div class="container"> - <div data-bind="with: inbox"> - <h1>Inbox</h1> - <table class="table"> - <thead> - <tr> - <th>Created</th> - <th>Summary</th> - </tr> - </thead> - <tbody data-bind="foreach: $root.messages"> - <tr data-bind="click: $root.goToMessage"> - <td data-bind="text: created"></td> - <td><a data-bind="text: summary, attr: { href: id}"></a></td> - </tr> - </tbody> - </table> - </div> - - <div class="container" data-bind="with: chosenMessageData"> - <h1>Message : <span data-bind="text: summary"></span></h1> - <dl> - <dt>Created</dt> - <dd data-bind="text: created"></dd> - <dt>Message</dt> - <dd data-bind="html: text"></dd> - </dl> - </div> - - <div class="container" data-bind="with: compose"> - <h1>Messages : Create</h1> - <div class="alert alert-error" data-bind="foreach: $root.errors, visible: $root.errors().length"> - <p data-bind="text: $data"></p> - </div> - <form action="./" method="post"> - <label for="summary">Summary</label> - <input type="text" id="summary" data-bind="value: summary" name="summary" class="input-xxlarge" /> - <label for="text">Message</label> - <textarea name="text" id="text" data-bind="value: text" class="input-xxlarge"><!-- --></textarea> - <div class="form-actions"> - <input type="button" data-bind="click: $root.save" value="Create" /> - </div> - </form> - </div> - </div> - - <div id="push"><!-- --></div> - </div> - <div id="footer"> - <div class="container"> - <p class="muted credit">Visit the <a href="#">Spring Security</a> site for more <a href="#">samples</a>.</p> - </div> - </div> - - <script type="text/javascript" th:src="@{/resources/js/jquery-3.5.1.min.js}"><!-- --></script> - <script type="text/javascript" th:src="@{/resources/js/knockout-2.3.0.js}"><!-- --></script> - <script type="text/javascript" th:src="@{/resources/js/message.js}"><!-- --></script> - </body> -</html> diff --git a/samples/javaconfig/hellojs/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/hellojs/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/hellojs/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/hellojs/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/hellojs/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/hellojs/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle b/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle deleted file mode 100644 index 3074d45935e..00000000000 --- a/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'javax.xml.bind:jaxb-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api' - - runtime 'opensymphony:sitemesh' - - testCompile project(':spring-security-test') -} diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index 45f8ae8d784..00000000000 --- a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.core.annotation.Order; -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -@Order(2) -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index c0719e88f8a..00000000000 --- a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.userdetails.User; - -@EnableWebSecurity -public class SecurityConfig { - - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")); - } - // @formatter:on -} diff --git a/samples/javaconfig/hellomvc/src/main/resources/views/README.adoc b/samples/javaconfig/hellomvc/src/main/resources/views/README.adoc deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/samples/javaconfig/hellomvc/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/hellomvc/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/hellomvc/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index f7117bf46bf..00000000000 --- a/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.security.samples.mvc.config.WebMvcConfiguration; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class }) -@WebAppConfiguration -public class SecurityConfigTests { - private MockMvc mvc; - - @Autowired - private WebApplicationContext context; - - - @Before - public void setup() { - mvc = MockMvcBuilders.webAppContextSetup(context) - .apply(springSecurity()) - .defaultRequest(get("/").accept(MediaType.TEXT_HTML)).build(); - } - - @Test - public void requestProtectedResourceRequiresAuthentication() throws Exception { - mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); - } - - @Test - public void loginSuccess() throws Exception { - mvc.perform(formLogin()).andExpect(redirectedUrl("/")); - } - - @Test - public void loginFailure() throws Exception { - mvc.perform(formLogin().password("invalid")).andExpect( - redirectedUrl("/login?error")); - } - - @Test - @WithMockUser - public void requestProtectedResourceWithUser() throws Exception { - mvc.perform(get("/")).andExpect(status().isOk()); - } - - @Test - @WithMockUser - public void composeMessageRequiresCsrfToken() throws Exception { - MockHttpServletRequestBuilder composeMessage = post("/").param("summary", - "New Message").param("text", "This is a new message"); - - mvc.perform(composeMessage).andExpect(status().isForbidden()); - } - - @Test - @WithMockUser - public void composeMessage() throws Exception { - MockHttpServletRequestBuilder composeMessage = post("/") - .param("summary", "New Message").param("text", "This is a new message") - .with(csrf()); - - mvc.perform(composeMessage).andExpect(redirectedUrlPattern("/*")); - } - - @Test - @WithMockUser - public void logoutRequiresCsrfToken() throws Exception { - mvc.perform(post("/logout")).andExpect(status().isForbidden()); - } - - @Test - @WithMockUser - public void logoutSuccess() throws Exception { - mvc.perform(logout()).andExpect(redirectedUrl("/login?logout")) - .andExpect(unauthenticated()); - } -} diff --git a/samples/javaconfig/helloworld/spring-security-samples-javaconfig-helloworld.gradle b/samples/javaconfig/helloworld/spring-security-samples-javaconfig-helloworld.gradle deleted file mode 100644 index 6d3e8436247..00000000000 --- a/samples/javaconfig/helloworld/spring-security-samples-javaconfig-helloworld.gradle +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-web') - compile jstlDependencies - compile slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java deleted file mode 100644 index d2f43eb8bd3..00000000000 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; - -/** - * @author Michael Simons - */ -public class HelloWorldJcTests { - - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit(); - homePage - .assertAt() - .andTheUserNameIsDisplayed(); - } - - @Test - public void authenticatedUserLogsOut() { - LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit() - .logout(); - loginPage.assertAt(); - - loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index ed527c39577..00000000000 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - @FindBy(css = "p") - private WebElement message; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Hello Security"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andTheUserNameIsDisplayed() { - assertThat(message.getText()).isEqualTo("Hello user"); - return this; - } - } -} diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 30822ea2a94..00000000000 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public HomePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - } -} diff --git a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 0d80bfac166..00000000000 --- a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; - -@EnableWebSecurity -public class SecurityConfig { - - // @formatter:off - @Bean - public UserDetailsService userDetailsService() { - UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(); - return new InMemoryUserDetailsManager(user); - } - // @formatter:on -} diff --git a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java deleted file mode 100644 index 008b87b870f..00000000000 --- a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class SecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { - - public SecurityWebApplicationInitializer() { - super(SecurityConfig.class); - } -} diff --git a/samples/javaconfig/helloworld/src/main/resources/logback.xml b/samples/javaconfig/helloworld/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/helloworld/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/helloworld/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/helloworld/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/helloworld/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/helloworld/src/main/webapp/index.jsp b/samples/javaconfig/helloworld/src/main/webapp/index.jsp deleted file mode 100644 index 8b4d7a39830..00000000000 --- a/samples/javaconfig/helloworld/src/main/webapp/index.jsp +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.0"> - - <jsp:directive.page contentType="text/html" pageEncoding="UTF-8" /> - <jsp:output omit-xml-declaration="true" /> - <jsp:output doctype-root-element="HTML" - doctype-system="about:legacy-compat" /> -<html lang="en"> - <head> - <title>Hello Security</title> - <c:url var="faviconUrl" value="/resources/img/favicon.ico"/> - <link rel="icon" type="image/x-icon" href="${faviconUrl}"/> - <c:url var="bootstrapUrl" value="/resources/css/bootstrap.css"/> - <link href="${bootstrapUrl}" rel="stylesheet"></link> - <c:url var="bootstrapResponsiveUrl" value="/resources/css/bootstrap-responsive.css"/> - <link href="${bootstrapResponsiveUrl}" rel="stylesheet"></link> - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - </head> - - <body> - <div class="container"> - <h1>This is secured!</h1> - <p> - Hello <b><c:out value="${pageContext.request.remoteUser}"/></b> - </p> - <c:url var="logoutUrl" value="/logout"/> - <form class="form-inline" action="${logoutUrl}" method="post"> - <input type="submit" value="Log out" /> - <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> - </form> - </div> - </body> -</html> -</jsp:root> diff --git a/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css b/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css deleted file mode 100644 index ba09bc8777b..00000000000 --- a/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css +++ /dev/null @@ -1,1092 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -@-ms-viewport { - width: device-width; -} - -.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; -} - -.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; - } -} - -@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 .dropdown-menu a:hover { - 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 .dropdown-menu a:hover { - 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/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap.css b/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap.css deleted file mode 100644 index 22aa0c17a90..00000000000 --- a/samples/javaconfig/helloworld/src/main/webapp/resources/css/bootstrap.css +++ /dev/null @@ -1,6039 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -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; - } -} - -.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; -} - -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 { - 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 { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover { - color: #356635; -} - -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; - padding-right: 5px; - padding-left: 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; - 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: 16px; - font-weight: 300; - line-height: 25px; -} - -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 { - margin-bottom: 5px; - 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 { - 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 { - -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 { - -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 tfoot:last-child tr:last-child > td: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 tfoot:last-child tr:last-child > td: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/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:hover > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:hover > 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 { - 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 { - 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 { - 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 { - color: #999999; -} - -.dropdown-menu .disabled > a:hover { - 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; -} - -.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 { - 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 #bbbbbb; - *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: #a2a2a2; - -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: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 { - 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 { - border-color: #c5c5c5; - border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); -} - -.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: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: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: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: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: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: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 { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover { - 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-mini .caret, -.btn-small .caret, -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.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 { - 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 { - 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 { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover { - 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 { - 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 { - 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 { - 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 { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover { - 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 { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover { - 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 { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover { - 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 { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover { - 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 { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover { - 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 { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover { - 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 { - 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: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 { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.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 { - 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 { - 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 { - 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: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 > .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 { - 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 { - 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 > 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; - padding: 5px; - font-size: 11px; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - margin-top: -3px; -} - -.tooltip.right { - margin-left: 3px; -} - -.tooltip.bottom { - margin-top: 3px; -} - -.tooltip.left { - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 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; - width: 236px; - 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-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 { - 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.badge:hover { - 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 { - 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 { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.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/samples/javaconfig/helloworld/src/main/webapp/resources/img/favicon.ico b/samples/javaconfig/helloworld/src/main/webapp/resources/img/favicon.ico deleted file mode 100644 index bfb9974019d..00000000000 Binary files a/samples/javaconfig/helloworld/src/main/webapp/resources/img/favicon.ico and /dev/null differ diff --git a/samples/javaconfig/helloworld/src/main/webapp/resources/img/logo.png b/samples/javaconfig/helloworld/src/main/webapp/resources/img/logo.png deleted file mode 100644 index 393230883fb..00000000000 Binary files a/samples/javaconfig/helloworld/src/main/webapp/resources/img/logo.png and /dev/null differ diff --git a/samples/javaconfig/inmemory/spring-security-samples-javaconfig-inmemory.gradle b/samples/javaconfig/inmemory/spring-security-samples-javaconfig-inmemory.gradle deleted file mode 100644 index 3074d45935e..00000000000 --- a/samples/javaconfig/inmemory/spring-security-samples-javaconfig-inmemory.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'javax.xml.bind:jaxb-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api' - - runtime 'opensymphony:sitemesh' - - testCompile project(':spring-security-test') -} diff --git a/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index ad65d6ec9b2..00000000000 --- a/samples/javaconfig/inmemory/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; - -@EnableWebSecurity -public class SecurityConfig { - - // @formatter:off - @Bean - public UserDetailsService userDetailsService() { - User.UserBuilder builder = User.withDefaultPasswordEncoder(); - UserDetails user = builder.username("user").password("password").roles("USER").build(); - UserDetails admin = builder.username("admin").password("password").roles("USER", "ADMIN").build(); - return new InMemoryUserDetailsManager(user, admin); - } - // @formatter:on -} diff --git a/samples/javaconfig/inmemory/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/inmemory/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/inmemory/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/inmemory/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/inmemory/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index 507cc875da7..00000000000 --- a/samples/javaconfig/inmemory/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.security.samples.mvc.config.WebMvcConfiguration; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class }) -@WebAppConfiguration -public class SecurityConfigTests { - private MockMvc mvc; - - @Autowired - private WebApplicationContext context; - - @Before - public void setup() { - mvc = MockMvcBuilders.webAppContextSetup(context) - .apply(springSecurity()) - .defaultRequest(get("/").accept(MediaType.TEXT_HTML)).build(); - } - - @Test - public void requestProtectedResourceRequiresAuthentication() throws Exception { - mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); - } - - @Test - public void loginSuccess() throws Exception { - mvc.perform(formLogin()).andExpect(redirectedUrl("/")); - } - - @Test - public void loginFailure() throws Exception { - mvc.perform(formLogin().password("invalid")).andExpect( - redirectedUrl("/login?error")); - } - - @Test - @WithMockUser - public void requestProtectedResourceWithUser() throws Exception { - mvc.perform(get("/")).andExpect(status().isOk()); - } - - @Test - @WithMockUser - public void logoutSuccess() throws Exception { - mvc.perform(logout()) - .andExpect(redirectedUrl("/login?logout")) - .andExpect(unauthenticated()); - } -} diff --git a/samples/javaconfig/jdbc/spring-security-samples-javaconfig-jdbc.gradle b/samples/javaconfig/jdbc/spring-security-samples-javaconfig-jdbc.gradle deleted file mode 100644 index e3ff16ce6ae..00000000000 --- a/samples/javaconfig/jdbc/spring-security-samples-javaconfig-jdbc.gradle +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'org.springframework:spring-webmvc' - compile 'org.springframework:spring-jdbc' - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile slf4jDependencies - - compile 'javax.xml.bind:jaxb-api' - compile 'com.sun.xml.bind:jaxb-core' - compile 'com.sun.xml.bind:jaxb-impl' - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api' - - runtime 'opensymphony:sitemesh' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/JdbcJcTests.java b/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/JdbcJcTests.java deleted file mode 100644 index dad6a0052ca..00000000000 --- a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/JdbcJcTests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Michael Simons - */ -@Transactional -public class JdbcJcTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final String userName = "user"; - final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() - .username(userName) - .password("password") - .submit(); - homePage - .assertAt() - .andTheUserNameDisplayedIs(userName); - } - - @Test - public void authenticatedUserLogsOut() { - LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit() - .logout(); - loginPage.assertAt(); - - loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 71033bf9416..00000000000 --- a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).endsWith("View All"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p.navbar-text") - private WebElement message; - - public Content andTheUserNameDisplayedIs(final String userName) { - assertThat(message.getText()).isEqualTo(userName); - return this; - } - } -} diff --git a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 30822ea2a94..00000000000 --- a/samples/javaconfig/jdbc/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public HomePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - } -} diff --git a/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index e3dbb1cf514..00000000000 --- a/samples/javaconfig/jdbc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import javax.sql.DataSource; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.core.userdetails.User; - -@EnableWebSecurity -public class SecurityConfig { - @Autowired - private DataSource dataSource; - - // @formatter:off - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .jdbcAuthentication() - .dataSource(dataSource) - .withDefaultSchema() - .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")); - } - // @formatter:on -} diff --git a/samples/javaconfig/jdbc/src/main/resources/logback.xml b/samples/javaconfig/jdbc/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/jdbc/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/jdbc/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/jdbc/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/jdbc/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/ldap/spring-security-samples-javaconfig-ldap.gradle b/samples/javaconfig/ldap/spring-security-samples-javaconfig-ldap.gradle deleted file mode 100644 index 1fc70cb879d..00000000000 --- a/samples/javaconfig/ldap/spring-security-samples-javaconfig-ldap.gradle +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-ldap') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'javax.xml.bind:jaxb-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-webmvc' - compile apachedsDependencies - compile slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'opensymphony:sitemesh' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/LdapJcTests.java b/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/LdapJcTests.java deleted file mode 100644 index 99df0ac9599..00000000000 --- a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/LdapJcTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; - -/** - * @author Michael Simons - */ -public class LdapJcTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final String userName = "user"; - // @formatter:off - final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() - .username(userName) - .password("password") - .submit(); - // @formatter:on - homePage - .assertAt() - .andTheUserNameDisplayedIs(userName); - } - - @Test - public void authenticatedUserLogsOut() { - // @formatter:off - LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit() - .logout(); - // @formatter:on - loginPage.assertAt(); - - loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 22ddd1c6391..00000000000 --- a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("SecureMail: View All"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p.navbar-text") - private WebElement message; - - public Content andTheUserNameDisplayedIs(final String userName) { - assertThat(message.getText()).isEqualTo(userName); - return this; - } - } -} diff --git a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 30822ea2a94..00000000000 --- a/samples/javaconfig/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public HomePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - } -} diff --git a/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index da5de6aa672..00000000000 --- a/samples/javaconfig/ldap/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - -@EnableWebSecurity -public class SecurityConfig { - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .ldapAuthentication() - .userDnPatterns("uid={0},ou=people") - .groupSearchBase("ou=groups") - .contextSource().port(0); - } - // @formatter:on -} diff --git a/samples/javaconfig/ldap/src/main/resources/logback.xml b/samples/javaconfig/ldap/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/ldap/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/ldap/src/main/resources/users.ldif b/samples/javaconfig/ldap/src/main/resources/users.ldif deleted file mode 100644 index fde2456d46f..00000000000 --- a/samples/javaconfig/ldap/src/main/resources/users.ldif +++ /dev/null @@ -1,42 +0,0 @@ -dn: ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: groups - -dn: ou=people,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: people - -dn: uid=admin,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Rod Johnson -sn: Johnson -uid: admin -userPassword: password - -dn: uid=user,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Dianne Emu -sn: Emu -uid: user -userPassword: password - -dn: cn=user,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: user -uniqueMember: uid=admin,ou=people,dc=springframework,dc=org -uniqueMember: uid=user,ou=people,dc=springframework,dc=org - -dn: cn=admin,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: admin -uniqueMember: uid=admin,ou=people,dc=springframework,dc=org \ No newline at end of file diff --git a/samples/javaconfig/ldap/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/ldap/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/ldap/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/messages/spring-security-samples-javaconfig-messages.gradle b/samples/javaconfig/messages/spring-security-samples-javaconfig-messages.gradle deleted file mode 100644 index 43c2f49ac26..00000000000 --- a/samples/javaconfig/messages/spring-security-samples-javaconfig-messages.gradle +++ /dev/null @@ -1,31 +0,0 @@ - - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-web') - compile 'javax.validation:validation-api' - compile 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' - compile 'org.eclipse.persistence:javax.persistence' - compile('org.hibernate:hibernate-entitymanager') { - exclude group:'javassist', module: 'javassist' - } - compile 'org.hibernate:hibernate-validator' - compile 'org.hsqldb:hsqldb' - compile('org.springframework.data:spring-data-jpa') { - exclude group:'org.aspectj', module:'aspectjrt' - } - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-aspects' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-instrument' - compile 'org.springframework:spring-orm' - compile 'org.springframework:spring-tx' - compile 'org.springframework:spring-webmvc' - compile 'org.thymeleaf:thymeleaf-spring5' - - providedCompile 'javax.servlet:javax.servlet-api' -} diff --git a/samples/javaconfig/messages/src/main/java/META-INF/MANIFEST.MF b/samples/javaconfig/messages/src/main/java/META-INF/MANIFEST.MF deleted file mode 100644 index 5e9495128c0..00000000000 --- a/samples/javaconfig/messages/src/main/java/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/DataConfiguration.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/DataConfiguration.java deleted file mode 100644 index 9e174051d89..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/DataConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import javax.sql.DataSource; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.Database; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import org.springframework.security.samples.data.Message; -import org.springframework.transaction.PlatformTransactionManager; - -@Configuration -@EnableJpaRepositories("org.springframework.security.samples.data") -public class DataConfiguration { - - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.HSQL).build(); - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - vendorAdapter.setDatabase(Database.HSQL); - vendorAdapter.setGenerateDdl(true); - - LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); - factory.setJpaVendorAdapter(vendorAdapter); - factory.setPackagesToScan(Message.class.getPackage().getName()); - factory.setDataSource(dataSource()); - - return factory; - } - - @Bean - @DependsOn("entityManagerFactory") - public ResourceDatabasePopulator initDatabase(DataSource dataSource) throws Exception { - ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); - populator.addScript(new ClassPathResource("data.sql")); - populator.populate(dataSource.getConnection()); - return populator; - } - - @Bean - public PlatformTransactionManager transactionManager() { - JpaTransactionManager txManager = new JpaTransactionManager(); - txManager.setEntityManagerFactory(entityManagerFactory().getObject()); - return txManager; - } -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/MessageWebApplicationInitializer.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/MessageWebApplicationInitializer.java deleted file mode 100644 index 428042060fe..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/MessageWebApplicationInitializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import javax.servlet.Filter; - -import org.springframework.security.samples.mvc.config.WebMvcConfiguration; -import org.springframework.web.filter.HiddenHttpMethodFilter; -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -public class MessageWebApplicationInitializer extends - AbstractAnnotationConfigDispatcherServletInitializer { - - @Override - protected Class<?>[] getRootConfigClasses() { - return new Class[] { RootConfiguration.class }; - } - - @Override - protected Class<?>[] getServletConfigClasses() { - return new Class[] { WebMvcConfiguration.class }; - } - - @Override - protected String[] getServletMappings() { - return new String[] { "/" }; - } - - @Override - protected Filter[] getServletFilters() { - return new Filter[] { new HiddenHttpMethodFilter() }; - } -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/RootConfiguration.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/RootConfiguration.java deleted file mode 100644 index ce3d1738484..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/config/RootConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan -public class RootConfiguration { - -} \ No newline at end of file diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/Message.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/Message.java deleted file mode 100644 index 23e038f702a..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/Message.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.data; - -import java.util.Calendar; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -import org.hibernate.validator.constraints.NotEmpty; - -@Entity -public class Message { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotEmpty(message = "Message is required.") - private String text; - - @NotEmpty(message = "Summary is required.") - private String summary; - - private Calendar created = Calendar.getInstance(); - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Calendar getCreated() { - return created; - } - - public void setCreated(Calendar created) { - this.created = created; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/MessageRepository.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/MessageRepository.java deleted file mode 100644 index 22a417facf9..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/data/MessageRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.data; - -import org.springframework.data.repository.CrudRepository; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; - -public interface MessageRepository extends CrudRepository<Message, Long> { - - @Transactional - @PreAuthorize("hasRole('ROLE_ADMIN')") - <S extends Message> S save(S entity); -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/DefaultController.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/DefaultController.java deleted file mode 100644 index 62b42b489fc..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/DefaultController.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.mvc; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -public class DefaultController { - - @RequestMapping("/**") - public String notFound() { - return "errors/404"; - } -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/MessageController.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/MessageController.java deleted file mode 100644 index 8c909eb0a6b..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/MessageController.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.mvc; - -import javax.validation.Valid; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.samples.data.Message; -import org.springframework.security.samples.data.MessageRepository; -import org.springframework.stereotype.Controller; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.support.RedirectAttributes; - -@Controller -@RequestMapping("/") -public class MessageController { - private MessageRepository messageRepository; - - @Autowired - public MessageController(MessageRepository messageRepository) { - this.messageRepository = messageRepository; - } - - @RequestMapping - public ModelAndView list() { - Iterable<Message> messages = messageRepository.findAll(); - return new ModelAndView("messages/inbox", "messages", messages); - } - - @RequestMapping("{id}") - public ModelAndView view(@PathVariable("id") Message message) { - return new ModelAndView("messages/show", "message", message); - } - - @RequestMapping(value = "{id}", method = RequestMethod.DELETE) - public String delete(@PathVariable("id") Message message, RedirectAttributes redirect) { - messageRepository.delete(message); - redirect.addFlashAttribute("globalMessage", "Message removed successfully"); - return "redirect:/"; - } - - @RequestMapping(params = "form", method = RequestMethod.GET) - public String createForm(@ModelAttribute Message message) { - return "messages/compose"; - } - - @RequestMapping(method = RequestMethod.POST) - public ModelAndView create(@Valid Message message, BindingResult result, - RedirectAttributes redirect) { - if (result.hasErrors()) { - return new ModelAndView("messages/compose"); - } - message = messageRepository.save(message); - redirect.addFlashAttribute("globalMessage", "Successfully created a new message"); - return new ModelAndView("redirect:/{message.id}", "message.id", message.getId()); - } -} diff --git a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/config/WebMvcConfiguration.java b/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/config/WebMvcConfiguration.java deleted file mode 100644 index dfbef33d6cc..00000000000 --- a/samples/javaconfig/messages/src/main/java/org/springframework/security/samples/mvc/config/WebMvcConfiguration.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.mvc.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.core.Ordered; -import org.springframework.data.repository.support.DomainClassConverter; -import org.springframework.format.support.FormattingConversionService; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; -import org.thymeleaf.spring5.view.ThymeleafViewResolver; -import org.thymeleaf.templatemode.TemplateMode; - -@EnableWebMvc -@ComponentScan("org.springframework.security.samples.mvc") -public class WebMvcConfiguration implements WebMvcConfigurer { - - @Autowired - private ApplicationContext applicationContext; - - @Autowired - private FormattingConversionService mvcConversionService; - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/login").setViewName("login"); - registry.setOrder(Ordered.HIGHEST_PRECEDENCE); - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/resources/") - .setCachePeriod(31556926); - registry.setOrder(Ordered.HIGHEST_PRECEDENCE); - } - - @Bean - public ViewResolver viewResolver() { - ThymeleafViewResolver resolver = new ThymeleafViewResolver(); - resolver.setTemplateEngine(templateEngine()); - resolver.setCharacterEncoding("UTF-8"); - return resolver; - } - - @Bean - public SpringTemplateEngine templateEngine() { - SpringTemplateEngine engine = new SpringTemplateEngine(); - engine.setEnableSpringELCompiler(true); - engine.setTemplateResolver(templateResolver()); - return engine; - } - - private SpringResourceTemplateResolver templateResolver() { - SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); - resolver.setPrefix("classpath:/views/"); - resolver.setSuffix(".html"); - resolver.setTemplateMode(TemplateMode.HTML); - resolver.setApplicationContext(applicationContext); - return resolver; - } - - @Bean - public DomainClassConverter<?> domainClassConverter() { - return new DomainClassConverter<>(mvcConversionService); - } -} diff --git a/samples/javaconfig/messages/src/main/resources/data.sql b/samples/javaconfig/messages/src/main/resources/data.sql deleted file mode 100644 index 7ca2b297ed2..00000000000 --- a/samples/javaconfig/messages/src/main/resources/data.sql +++ /dev/null @@ -1,2 +0,0 @@ -insert into message(created,summary,text) values ('2013-10-04 10:00:00','Hello Rob','This message is for Rob'); -insert into message(created,summary,text) values ('2013-10-04 10:00:00','Hello Luke','This message is for Luke'); \ No newline at end of file diff --git a/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap-responsive.css b/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap-responsive.css deleted file mode 100644 index ba09bc8777b..00000000000 --- a/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap-responsive.css +++ /dev/null @@ -1,1092 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -@-ms-viewport { - width: device-width; -} - -.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; -} - -.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; - } -} - -@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 .dropdown-menu a:hover { - 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 .dropdown-menu a:hover { - 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/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap.css b/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap.css deleted file mode 100644 index 22aa0c17a90..00000000000 --- a/samples/javaconfig/messages/src/main/resources/resources/css/bootstrap.css +++ /dev/null @@ -1,6039 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -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; - } -} - -.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; -} - -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 { - 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 { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover { - color: #356635; -} - -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; - padding-right: 5px; - padding-left: 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; - 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: 16px; - font-weight: 300; - line-height: 25px; -} - -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 { - margin-bottom: 5px; - 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 { - 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 { - -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 { - -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 tfoot:last-child tr:last-child > td: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 tfoot:last-child tr:last-child > td: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/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:hover > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:hover > 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 { - 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 { - 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 { - 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 { - color: #999999; -} - -.dropdown-menu .disabled > a:hover { - 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; -} - -.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 { - 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 #bbbbbb; - *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: #a2a2a2; - -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: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 { - 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 { - border-color: #c5c5c5; - border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); -} - -.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: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: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: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: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: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: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 { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover { - 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-mini .caret, -.btn-small .caret, -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.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 { - 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 { - 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 { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover { - 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 { - 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 { - 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 { - 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 { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover { - 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 { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover { - 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 { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover { - 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 { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover { - 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 { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover { - 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 { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover { - 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 { - 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: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 { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.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 { - 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 { - 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 { - 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: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 > .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 { - 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 { - 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 > 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; - padding: 5px; - font-size: 11px; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - margin-top: -3px; -} - -.tooltip.right { - margin-left: 3px; -} - -.tooltip.bottom { - margin-top: 3px; -} - -.tooltip.left { - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 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; - width: 236px; - 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-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 { - 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.badge:hover { - 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 { - 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 { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.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/samples/javaconfig/messages/src/main/resources/resources/img/favicon.ico b/samples/javaconfig/messages/src/main/resources/resources/img/favicon.ico deleted file mode 100644 index bfb9974019d..00000000000 Binary files a/samples/javaconfig/messages/src/main/resources/resources/img/favicon.ico and /dev/null differ diff --git a/samples/javaconfig/messages/src/main/resources/resources/img/logo.png b/samples/javaconfig/messages/src/main/resources/resources/img/logo.png deleted file mode 100644 index 393230883fb..00000000000 Binary files a/samples/javaconfig/messages/src/main/resources/resources/img/logo.png and /dev/null differ diff --git a/samples/javaconfig/messages/src/main/resources/views/layout.html b/samples/javaconfig/messages/src/main/resources/views/layout.html deleted file mode 100644 index 19ca32e2838..00000000000 --- a/samples/javaconfig/messages/src/main/resources/views/layout.html +++ /dev/null @@ -1,121 +0,0 @@ -<!DOCTYPE html SYSTEM "https://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-3.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" - xmlns:th="https://www.thymeleaf.org"> - <head th:fragment="head(title,links)"> - <title>SecureMail: <th:block th:include="${title}"></th:block></title> - <link rel="icon" type="image/x-icon" th:href="@{/resources/img/favicon.ico}" href="../resources/img/favicon.ico"/> - <link th:href="@{/resources/css/bootstrap.css}" href="../resources/css/bootstrap.css" rel="stylesheet"></link> - <style type="text/css"> - /* Sticky footer styles - -------------------------------------------------- */ - - html, - body { - height: 100%; - /* The html and body elements cannot have any padding or margin. */ - } - - /* Wrapper for page content to push down footer */ - #wrap { - min-height: 100%; - height: auto !important; - height: 100%; - /* Negative indent footer by it's height */ - margin: 0 auto -60px; - } - - /* Set the fixed height of the footer here */ - #push, - #footer { - height: 60px; - } - #footer { - background-color: #f5f5f5; - } - - /* Lastly, apply responsive CSS fixes as necessary */ - @media (max-width: 767px) { - #footer { - margin-left: -20px; - margin-right: -20px; - padding-left: 20px; - padding-right: 20px; - } - } - - - - /* Custom page CSS - -------------------------------------------------- */ - /* Not required for template or sticky footer method. */ - - .container { - width: auto; - max-width: 680px; - } - .container .credit { - margin: 20px 0; - text-align: center; - } - a { - color: green; - } - .navbar-form { - margin-left: 1em; - } - </style> - <link th:href="@{resources/css/bootstrap-responsive.css}" href="/resources/css/bootstrap-responsive.css" rel="stylesheet"></link> - - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - <th:block th:replace="${links}"/> - </head> - - - <body th:fragment="body"> - <div id="wrap"> - <div class="navbar navbar-inverse navbar-static-top"> - <div class="navbar-inner"> - <div class="container"> - <a class="brand" th:href="@{/}"><img th:src="@{/resources/img/logo.png}" alt="Spring Security Sample"/></a> - <div class="nav-collapse collapse"> - <div th:if="${#httpServletRequest.remoteUser != null}"> - <form class="navbar-form pull-right" th:action="@{/logout}" method="post"> - <input type="submit" value="Log out" /> - </form> - <p class="navbar-text pull-right" th:text="${#httpServletRequest.remoteUser}"> - sample_user - </p> - </div> - <ul class="nav"> - <li><a th:href="@{/}">Inbox</a></li> - <li><a th:href="@{/(form)}">Compose</a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Begin page content --> - <div class="container"> - <div class="alert alert-success" - th:if="${globalMessage}" - th:text="${globalMessage}"> - Some Success message - </div> - <div th:replace="${content}"> - Fake content - </div> - </div> - - <div id="push"><!-- --></div> - </div> - - <div id="footer"> - <div class="container"> - <p class="muted credit">Visit the <a href="https://spring.io/spring-security">Spring Security</a> site for more <a href="https://github.com/spring-projects/spring-security/blob/master/samples/">samples</a>.</p> - </div> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/messages/src/main/resources/views/messages/compose.html b/samples/javaconfig/messages/src/main/resources/views/messages/compose.html deleted file mode 100644 index 4e098d306f7..00000000000 --- a/samples/javaconfig/messages/src/main/resources/views/messages/compose.html +++ /dev/null @@ -1,40 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Create</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <div class="container"> - <h1>Messages : Create</h1> - <form id="messageForm" - th:action="@{/}" - th:object="${message}" - action="view.html" - method="post"> - <div th:if="${#fields.hasErrors('*')}" - class="alert alert-error"> - <p th:each="error : ${#fields.errors('*')}" - th:text="${error}"> - Validation error - </p> - </div> - <label for="summary"> - Summary - </label> - <input type="text" - th:field="*{summary}" - th:class="${#fields.hasErrors('summary')} ? 'field-error'"/> - <label for="text"> - Message - </label> - <textarea th:field="*{text}" - th:class="${#fields.hasErrors('text')} ? 'field-error'"> - </textarea> - <div class="form-actions"> - <input type="submit" value="Create"/> - </div> - </form> - </div> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/messages/src/main/resources/views/messages/inbox.html b/samples/javaconfig/messages/src/main/resources/views/messages/inbox.html deleted file mode 100644 index 2b759bd90f7..00000000000 --- a/samples/javaconfig/messages/src/main/resources/views/messages/inbox.html +++ /dev/null @@ -1,29 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>View All</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <h1>Inbox</h1> - <table class="table table-bordered table-striped"> - <thead> - <tr> - <th>Created</th> - <th>Summary</th> - <th>Delete</th> - </tr> - </thead> - <tbody> - <tr th:if="${messages.empty}"> - <td colspan="2">No messages</td> - </tr> - <tr th:each="message : ${messages}"> - <td th:text="${#calendars.format(message.created)}">July 11, 2012 2:17:16 PM CDT</td> - <td><a href="view.html" th:href="@{'/' + ${message.id}}" th:text="${message.summary}">The summary</a></td> - <td><form class="form-inline" th:action="@{'/' + ${message.id}}" th:method="delete"><input type="submit" value="Delete"/></form></td> - </tr> - </tbody> - </table> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/messages/src/main/resources/views/messages/show.html b/samples/javaconfig/messages/src/main/resources/views/messages/show.html deleted file mode 100644 index 9c04cc7ae96..00000000000 --- a/samples/javaconfig/messages/src/main/resources/views/messages/show.html +++ /dev/null @@ -1,20 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Create</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <div class="container"> - <h1>Message : <span th:text="${message.summary}">A short summary...</span></h1> - <dl> - <dt>ID</dt> - <dd id="id" th:text="${message.id}">123</dd> - <dt>Date</dt> - <dd id="created" th:text="${#calendars.format(message.created)}">July 11, 2012 2:17:16 PM CDT</dd> - <dt>Message</dt> - <dd id="text" th:text="${message.text}">A detailed message that is longer than the summary.</dd> - </dl> - </div> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/openid/spring-security-samples-javaconfig-openid.gradle b/samples/javaconfig/openid/spring-security-samples-javaconfig-openid.gradle deleted file mode 100644 index a62026e8dbe..00000000000 --- a/samples/javaconfig/openid/spring-security-samples-javaconfig-openid.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-openid') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'javax.xml.bind:jaxb-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'net.sourceforge.nekohtml:nekohtml' - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index c194eba0957..00000000000 --- a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 9645aac23bd..00000000000 --- a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.samples.security.CustomUserDetailsService; - -/** - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - */ -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/resources/**").permitAll() - .anyRequest().authenticated() - ) - .openidLogin((openidLogin) -> - openidLogin - .loginPage("/login") - .permitAll() - .authenticationUserDetailsService(new CustomUserDetailsService()) - .attributeExchange((googleExchange) -> - googleExchange - .identifierPattern("https://www.google.com/.*") - .attribute((emailAttribute) -> - emailAttribute - .name("email") - .type("https://axschema.org/contact/email") - .required(true) - ) - .attribute((firstnameAttribute) -> - firstnameAttribute - .name("firstname") - .type("https://axschema.org/namePerson/first") - .required(true) - ) - .attribute((lastnameAttribute) -> - lastnameAttribute - .name("lastname") - .type("https://axschema.org/namePerson/last") - .required(true) - ) - ) - .attributeExchange((yahooExchange) -> - yahooExchange - .identifierPattern(".*yahoo.com.*") - .attribute((emailAttribute) -> - emailAttribute - .name("email") - .type("https://axschema.org/contact/email") - .required(true) - ) - .attribute((fullnameAttribute) -> - fullnameAttribute - .name("fullname") - .type("https://axschema.org/namePerson") - .required(true) - ) - ) - .attributeExchange((myopenidExchange) -> - myopenidExchange - .identifierPattern(".*myopenid.com.*") - .attribute((emailAttribute) -> - emailAttribute - .name("email") - .type("https://schema.openid.net/contact/email") - .required(true) - ) - .attribute((fullnameAttribute) -> - fullnameAttribute - .name("fullname") - .type("https://schema.openid.net/namePerson") - .required(true) - ) - ) - ); - } - // @formatter:on -} diff --git a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/mvc/UserController.java b/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/mvc/UserController.java deleted file mode 100644 index 7b20b9d788b..00000000000 --- a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/mvc/UserController.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.mvc; - -import org.springframework.security.openid.OpenIDAuthenticationToken; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - */ -@Controller -@RequestMapping("/user/") -public class UserController { - - @RequestMapping(method = RequestMethod.GET) - public String show(Model model, OpenIDAuthenticationToken authentication) { - model.addAttribute("authentication", authentication); - return "user/show"; - } -} diff --git a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/security/CustomUserDetailsService.java b/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/security/CustomUserDetailsService.java deleted file mode 100644 index 935a1efcf59..00000000000 --- a/samples/javaconfig/openid/src/main/java/org/springframework/security/samples/security/CustomUserDetailsService.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.security; - -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.openid.OpenIDAuthenticationToken; - -/** - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - */ -public class CustomUserDetailsService implements - AuthenticationUserDetailsService<OpenIDAuthenticationToken> { - public UserDetails loadUserDetails(OpenIDAuthenticationToken token) - throws UsernameNotFoundException { - return new User(token.getName(), "", - AuthorityUtils.createAuthorityList("ROLE_USER")); - } -} diff --git a/samples/javaconfig/openid/src/main/resources/logback.xml b/samples/javaconfig/openid/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/openid/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/openid/src/main/resources/resources/css/openid.css b/samples/javaconfig/openid/src/main/resources/resources/css/openid.css deleted file mode 100644 index 8929ff07aba..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/css/openid.css +++ /dev/null @@ -1,45 +0,0 @@ -#openid_form { - width: 580px; -} - #openid_form legend { - font-weight: bold; - } -#openid_choice { - display: none; -} -#openid_input_area { - clear: both; - padding: 10px; -} -#openid_btns, #openid_btns br { - clear: both; -} - #openid_highlight { - padding: 3px; - background-color: #FFFCC9; - float: left; - } - .openid_large_btn { - width: 100px; - height: 60px; - border: 1px solid #DDD; - margin: 3px; - float: left; - } - .openid_small_btn { - width: 24px; - height: 24px; - border: 1px solid #DDD; - margin: 3px; - float: left; - } - a.openid_large_btn:focus { - outline: none; - } - a.openid_large_btn:focus - { - -moz-outline-style: none; - } - .openid_selected { - border: 4px solid #DDD; - } \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/aol.gif b/samples/javaconfig/openid/src/main/resources/resources/img/aol.gif deleted file mode 100644 index decc4f12362..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/aol.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/blogger.ico b/samples/javaconfig/openid/src/main/resources/resources/img/blogger.ico deleted file mode 100644 index 1b9730b01c3..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/blogger.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/claimid.ico b/samples/javaconfig/openid/src/main/resources/resources/img/claimid.ico deleted file mode 100644 index 2b80f49183c..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/claimid.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/facebook.gif b/samples/javaconfig/openid/src/main/resources/resources/img/facebook.gif deleted file mode 100644 index b997b358f78..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/facebook.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/favicon.ico b/samples/javaconfig/openid/src/main/resources/resources/img/favicon.ico deleted file mode 100644 index bfb9974019d..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/favicon.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/flickr.ico b/samples/javaconfig/openid/src/main/resources/resources/img/flickr.ico deleted file mode 100644 index 11f6e07f684..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/flickr.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/google.gif b/samples/javaconfig/openid/src/main/resources/resources/img/google.gif deleted file mode 100644 index 1b6cd07bd8b..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/google.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/livejournal.ico b/samples/javaconfig/openid/src/main/resources/resources/img/livejournal.ico deleted file mode 100644 index f3d21ec5e8f..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/livejournal.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/logo.png b/samples/javaconfig/openid/src/main/resources/resources/img/logo.png deleted file mode 100644 index 393230883fb..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/logo.png and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/myopenid.ico b/samples/javaconfig/openid/src/main/resources/resources/img/myopenid.ico deleted file mode 100644 index ceb06e6a3f0..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/myopenid.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/openid-inputicon.gif b/samples/javaconfig/openid/src/main/resources/resources/img/openid-inputicon.gif deleted file mode 100644 index cde836c893f..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/openid-inputicon.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/openid.gif b/samples/javaconfig/openid/src/main/resources/resources/img/openid.gif deleted file mode 100644 index c718b0e6f37..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/openid.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/technorati.ico b/samples/javaconfig/openid/src/main/resources/resources/img/technorati.ico deleted file mode 100644 index fa1083c1165..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/technorati.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/verisign.gif b/samples/javaconfig/openid/src/main/resources/resources/img/verisign.gif deleted file mode 100644 index faa6aaafbda..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/verisign.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/verisign.ico b/samples/javaconfig/openid/src/main/resources/resources/img/verisign.ico deleted file mode 100644 index 3953af93198..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/verisign.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/vidoop.ico b/samples/javaconfig/openid/src/main/resources/resources/img/vidoop.ico deleted file mode 100644 index bbd9a0d50f8..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/vidoop.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/wordpress.ico b/samples/javaconfig/openid/src/main/resources/resources/img/wordpress.ico deleted file mode 100644 index 31b7d2c2b77..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/wordpress.ico and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/img/yahoo.gif b/samples/javaconfig/openid/src/main/resources/resources/img/yahoo.gif deleted file mode 100644 index 42adbfa57f8..00000000000 Binary files a/samples/javaconfig/openid/src/main/resources/resources/img/yahoo.gif and /dev/null differ diff --git a/samples/javaconfig/openid/src/main/resources/resources/js/jquery-1.2.6.min.js b/samples/javaconfig/openid/src/main/resources/resources/js/jquery-1.2.6.min.js deleted file mode 100644 index 82b98e1d766..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/js/jquery-1.2.6.min.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * jQuery 1.2.6 - New Wave Javascript - * - * Copyright (c) 2008 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ - * $Rev: 5685 $ - */ -(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else -return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else -return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else -selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else -return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else -this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else -return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else -jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&©&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else -script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else -for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else -for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else -jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else -ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else -while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else -while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},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.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else -for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else -jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else -xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else -jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else -for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else -s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else -e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/jquery.query-2.1.3.js b/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/jquery.query-2.1.3.js deleted file mode 100644 index e4320a76222..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/jquery.query-2.1.3.js +++ /dev/null @@ -1,220 +0,0 @@ -/** - * jQuery.query - Query String Modification and Creation for jQuery - * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com) - * Licensed under the WTFPL (https://www.wtfpl.net/). - * Date: 2009/02/08 - * - * @author Blair Mitchelmore - * @version 2.1.3 - * - **/ -new function(settings) { - // Various Settings - var $separator = settings.separator || '&'; - var $spaces = settings.spaces === false ? false : true; - var $suffix = settings.suffix === false ? '' : '[]'; - var $prefix = settings.prefix === false ? false : true; - var $hash = $prefix ? settings.hash === true ? "#" : "?" : ""; - var $numbers = settings.numbers === false ? false : true; - - jQuery.query = new function() { - var is = function(o, t) { - return o != undefined && o !== null && (!!t ? o.constructor == t : true); - }; - var parse = function(path) { - var m, rx = /\[([^[]*)\]/g, match = /^(\S+?)(\[\S*\])?$/.exec(path), base = match[1], tokens = []; - while (m = rx.exec(match[2])) tokens.push(m[1]); - return [base, tokens]; - }; - var set = function(target, tokens, value) { - var o, token = tokens.shift(); - if (typeof target != 'object') target = null; - if (token === "") { - if (!target) target = []; - if (is(target, Array)) { - target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value)); - } else if (is(target, Object)) { - var i = 0; - while (target[i++] != null); - target[--i] = tokens.length == 0 ? value : set(target[i], tokens.slice(0), value); - } else { - target = []; - target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value)); - } - } else if (token && token.match(/^\s*[0-9]+\s*$/)) { - var index = parseInt(token, 10); - if (!target) target = []; - target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value); - } else if (token) { - var index = token.replace(/^\s*|\s*$/g, ""); - if (!target) target = {}; - if (is(target, Array)) { - var temp = {}; - for (var i = 0; i < target.length; ++i) { - temp[i] = target[i]; - } - target = temp; - } - target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value); - } else { - return value; - } - return target; - }; - - var queryObject = function(a) { - var self = this; - self.keys = {}; - - if (a.queryObject) { - jQuery.each(a.get(), function(key, val) { - self.SET(key, val); - }); - } else { - jQuery.each(arguments, function() { - var q = "" + this; - q = decodeURIComponent(q); - q = q.replace(/^[?#]/,''); // remove any leading ? || # - q = q.replace(/[;&]$/,''); // remove any trailing & || ; - if ($spaces) q = q.replace(/[+]/g,' '); // replace +'s with spaces - - jQuery.each(q.split(/[&;]/), function(){ - var key = this.split('=')[0]; - var val = this.split('=')[1]; - - if (!key) return; - - if ($numbers) { - if (/^[+-]?[0-9]+\.[0-9]*$/.test(val)) // simple float regex - val = parseFloat(val); - else if (/^[+-]?[0-9]+$/.test(val)) // simple int regex - val = parseInt(val, 10); - } - - val = (!val && val !== 0) ? true : val; - - if (val !== false && val !== true && typeof val != 'number') - val = val; - - self.SET(key, val); - }); - }); - } - return self; - }; - - queryObject.prototype = { - queryObject: true, - has: function(key, type) { - var value = this.get(key); - return is(value, type); - }, - GET: function(key) { - if (!is(key)) return this.keys; - var parsed = parse(key), base = parsed[0], tokens = parsed[1]; - var target = this.keys[base]; - while (target != null && tokens.length != 0) { - target = target[tokens.shift()]; - } - return typeof target == 'number' ? target : target || ""; - }, - get: function(key) { - var target = this.GET(key); - if (is(target, Object)) - return jQuery.extend(true, {}, target); - else if (is(target, Array)) - return target.slice(0); - return target; - }, - SET: function(key, val) { - var value = !is(val) ? null : val; - var parsed = parse(key), base = parsed[0], tokens = parsed[1]; - var target = this.keys[base]; - this.keys[base] = set(target, tokens.slice(0), value); - return this; - }, - set: function(key, val) { - return this.copy().SET(key, val); - }, - REMOVE: function(key) { - return this.SET(key, null).COMPACT(); - }, - remove: function(key) { - return this.copy().REMOVE(key); - }, - EMPTY: function() { - var self = this; - jQuery.each(self.keys, function(key, value) { - delete self.keys[key]; - }); - return self; - }, - load: function(url) { - var hash = url.replace(/^.*?[#](.+?)(?:\?.+)?$/, "$1"); - var search = url.replace(/^.*?[?](.+?)(?:#.+)?$/, "$1"); - return new queryObject(url.length == search.length ? '' : search, url.length == hash.length ? '' : hash); - }, - empty: function() { - return this.copy().EMPTY(); - }, - copy: function() { - return new queryObject(this); - }, - COMPACT: function() { - function build(orig) { - var obj = typeof orig == "object" ? is(orig, Array) ? [] : {} : orig; - if (typeof orig == 'object') { - function add(o, key, value) { - if (is(o, Array)) - o.push(value); - else - o[key] = value; - } - jQuery.each(orig, function(key, value) { - if (!is(value)) return true; - add(obj, key, build(value)); - }); - } - return obj; - } - this.keys = build(this.keys); - return this; - }, - compact: function() { - return this.copy().COMPACT(); - }, - toString: function() { - var i = 0, queryString = [], chunks = [], self = this; - var addFields = function(arr, key, value) { - if (!is(value) || value === false) return; - var o = [encodeURIComponent(key)]; - if (value !== true) { - o.push("="); - o.push(encodeURIComponent(value)); - } - arr.push(o.join("")); - }; - var build = function(obj, base) { - var newKey = function(key) { - return !base || base == "" ? [key].join("") : [base, "[", key, "]"].join(""); - }; - jQuery.each(obj, function(key, value) { - if (typeof value == 'object') - build(value, newKey(key)); - else - addFields(chunks, newKey(key), value); - }); - }; - - build(this.keys); - - if (chunks.length > 0) queryString.push($hash); - queryString.push(chunks.join($separator)); - - return queryString.join(""); - } - }; - - return new queryObject(location.search, location.hash); - }; -}(jQuery.query || {}); // Pass in jQuery.query as settings object diff --git a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client-config.js b/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client-config.js deleted file mode 100644 index 4cb0920c8e4..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client-config.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -Defines the base of where the OpenID Provider redirects its response to. - */ -var server_root = "https://openid-selector.googlecode.com/svn/trunk/" - -/* -On the server-side you'd accept an OpenID URL and perform discovery -on it to find out the actual OpenID endpoint to send the authentication -request to. On the client side it isn't possible to lookup the endpoint -from the target server due to XSS restrictions. The endpoint for each -provider is therefore cached in this static file. If an endpoint isn't -specified for a provider then authentication on the client side cannot -proceed. -*/ -var providers_endpoint = { - google: 'https://www.google.com/accounts/o8/ud', - yahoo: 'https://open.login.yahooapis.com/openid/op/auth', - aol: 'https://api.screenname.aol.com/auth/openidServer', - verisign: 'https://pip.verisignlabs.com/server' -} \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client.js b/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client.js deleted file mode 100644 index 63f4f75bf6b..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/js/openid-client/openid-client.js +++ /dev/null @@ -1,63 +0,0 @@ -var claimedID; -var providerID; -var openIDPopup; - -function OpenID_iframe_then_popup_handler(provider, claimed) { - providerID = provider; - claimedID = claimed; - var immediateiframe = document.getElementById("openid_immediate_iframe"); - - var iframeurl = getBaseOpenIDProviderURL(providerID, claimedID, true); - - immediateiframe.innerHTML = "<iframe frameborder='1' src='" + iframeurl + "'></iframe>"; -} - -function processOpenIDImmediateResponse(responseURL) { - var immediateiframe = document.getElementById("openid_immediate_iframe"); - immediateiframe.innerHTML = responseURL; - - var failure = new RegExp("openid.mode=setup_needed"); - if(failure.test(responseURL)) { - var popupurl = getBaseOpenIDProviderURL(providerID, claimedID, false); - openIDPopup = window.open(popupurl, "OpenIDPopup"); - } else { - alert("Success without popup!"); - } -} - -function processOpenIDSetupResponse(responseURL) { - openIDPopup.close(); - - var results = ""; - var responseQuery = $.query.load(responseURL); - $.each(responseQuery.get(), function(key, value) { - results += "<br/>" + key + "=" + value; - }); - - document.getElementById("openid_status").innerHTML = "<br/>Result of authentication is: " + results; -} - -function getBaseOpenIDProviderURL(provider, claimed, immediate) { - var providerEndpoint = providers_endpoint[provider]; - var providerURL = providerEndpoint; //From previous discovery - providerURL += "?"; - providerURL += "openid.ns=" + encodeURIComponent("https://specs.openid.net/auth/2.0"); - if(providers[provider].label) { - providerURL += "&openid.claimed_id=" + encodeURIComponent(claimed); - providerURL += "&openid.identity=" + encodeURIComponent(claimed); - } - else { - providerURL += "&openid.claimed_id=" + encodeURIComponent("https://specs.openid.net/auth/2.0/identifier_select"); - providerURL += "&openid.identity=" + encodeURIComponent("https://specs.openid.net/auth/2.0/identifier_select"); - } - if(immediate) { - providerURL += "&openid.return_to=" + encodeURIComponent(server_root + "openid-client/checkid_immediate_response.html"); - providerURL += "&openid.realm=" + encodeURIComponent(server_root + "openid-client/checkid_immediate_response.html"); - providerURL += "&openid.mode=checkid_immediate"; - } else { - providerURL += "&openid.return_to=" + encodeURIComponent(server_root + "openid-client/checkid_setup_response.html"); - providerURL += "&openid.realm=" + encodeURIComponent(server_root + "openid-client/checkid_setup_response.html"); - providerURL += "&openid.mode=checkid_setup"; - } - return providerURL; -} \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/resources/js/openid-jquery.js b/samples/javaconfig/openid/src/main/resources/resources/js/openid-jquery.js deleted file mode 100644 index e46d233e331..00000000000 --- a/samples/javaconfig/openid/src/main/resources/resources/js/openid-jquery.js +++ /dev/null @@ -1,240 +0,0 @@ -/* -Simple OpenID Plugin -https://code.google.com/p/openid-selector/ - -This code is licenced under the New BSD License. -*/ - -var providers_large = { - google: { - name: 'Google', - url: 'https://www.google.com/accounts/o8/id' - }, - yahoo: { - name: 'Yahoo', - url: 'https://me.yahoo.com/' - }, - aol: { - name: 'AOL', - label: 'Enter your AOL screenname.', - url: 'https://openid.aol.com/{username}' - }, - verisign: { - name: 'Verisign', - label: 'Your Verisign username', - url: 'http://{username}.pip.verisignlabs.com/' - }, - openid: { - name: 'OpenID', - label: 'Enter your OpenID.', - url: null - } -}; -var providers_small = { - myopenid: { - name: 'MyOpenID', - label: 'Enter your MyOpenID username.', - url: 'http://{username}.myopenid.com/' - }, - livejournal: { - name: 'LiveJournal', - label: 'Enter your Livejournal username.', - url: 'http://{username}.livejournal.com/' - }, - flickr: { - name: 'Flickr', - label: 'Enter your Flickr username.', - url: 'https://flickr.com/{username}/' - }, - technorati: { - name: 'Technorati', - label: 'Enter your Technorati username.', - url: 'https://technorati.com/people/technorati/{username}/' - }, - wordpress: { - name: 'Wordpress', - label: 'Enter your Wordpress.com username.', - url: 'http://{username}.wordpress.com/' - }, - blogger: { - name: 'Blogger', - label: 'Your Blogger account', - url: 'http://{username}.blogspot.com/' - }, - vidoop: { - name: 'Vidoop', - label: 'Your Vidoop username', - url: 'http://{username}.myvidoop.com/' - }, - claimid: { - name: 'ClaimID', - label: 'Your ClaimID username', - url: 'https://claimid.com/{username}' - } -}; -var providers = $.extend({}, providers_large, providers_small); - -var openid = { - - demo: false, - ajaxHandler: null, - cookie_expires: 6*30, // 6 months. - cookie_name: 'openid_provider', - cookie_path: '/', - - img_path: 'resources/img/', - - input_id: null, - provider_url: null, - provider_id: null, - - init: function(input_id) { - - var openid_btns = $('#openid_btns'); - - this.input_id = input_id; - - $('#openid_choice').show(); - $('#openid_input_area').empty(); - - // add box for each provider - for (id in providers_large) { - - openid_btns.append(this.getBoxHTML(providers_large[id], 'large', '.gif')); - } - if (providers_small) { - openid_btns.append('<br/>'); - - for (id in providers_small) { - - openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.ico')); - } - } - - $('#openid_form').submit(this.submit); - - var box_id = this.readCookie(); - if (box_id) { - this.signin(box_id, true); - } - }, - getBoxHTML: function(provider, box_size, image_ext) { - - var box_id = provider["name"].toLowerCase(); - return '<a title="'+provider["name"]+'" href="javascript: openid.signin(\''+ box_id +'\');"' + - ' style="background: #FFF url(' + this.img_path + box_id + image_ext+') no-repeat center center" ' + - 'class="' + box_id + ' openid_' + box_size + '_btn"></a>'; - - }, - /* Provider image click */ - signin: function(box_id, onload) { - - var provider = providers[box_id]; - if (! provider) { - return; - } - - this.highlight(box_id); - this.setCookie(box_id); - - this.provider_id = box_id; - this.provider_url = provider['url']; - - // prompt user for input? - if (provider['label']) { - this.useInputBox(provider); - } else { - $('#openid_input_area').empty(); - if (! onload) { - $('#openid_form').submit(); - } - } - }, - /* Sign-in button click */ - submit: function() { - - var url = openid.provider_url; - if (url) { - url = url.replace('{username}', $('#openid_username').val()); - openid.setOpenIdUrl(url); - } - if(openid.ajaxHandler) { - openid.ajaxHandler(openid.provider_id, document.getElementById(openid.input_id).value); - return false; - } - if(openid.demo) { - alert("In client demo mode. Normally would have submitted OpenID:\r\n" + document.getElementById(openid.input_id).value); - return false; - } - return true; - }, - setOpenIdUrl: function (url) { - - var hidden = document.getElementById(this.input_id); - if (hidden != null) { - hidden.value = url; - } else { - $('#openid_form').append('<input type="hidden" id="' + this.input_id + '" name="' + this.input_id + '" value="'+url+'"/>'); - } - }, - highlight: function (box_id) { - - // remove previous highlight. - var highlight = $('#openid_highlight'); - if (highlight) { - highlight.replaceWith($('#openid_highlight a')[0]); - } - // add new highlight. - $('.'+box_id).wrap('<div id="openid_highlight"></div>'); - }, - setCookie: function (value) { - - var date = new Date(); - date.setTime(date.getTime()+(this.cookie_expires*24*60*60*1000)); - var expires = "; expires="+date.toGMTString(); - - document.cookie = this.cookie_name+"="+value+expires+"; path=" + this.cookie_path; - }, - readCookie: function () { - var nameEQ = this.cookie_name + "="; - var ca = document.cookie.split(';'); - for(var i=0;i < ca.length;i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1,c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); - } - return null; - }, - useInputBox: function (provider) { - - var input_area = $('#openid_input_area'); - - var html = ''; - var id = 'openid_username'; - var value = ''; - var label = provider['label']; - var style = ''; - - if (label) { - html = '<p>' + label + '</p>'; - } - if (provider['name'] == 'OpenID') { - id = this.input_id; - value = 'http://'; - style = 'background:#FFF url('+this.img_path+'openid-inputicon.gif) no-repeat scroll 0 50%; padding-left:18px;'; - } - html += '<input id="'+id+'" type="text" style="'+style+'" name="'+id+'" value="'+value+'" />' + - '<input id="openid_submit" type="submit" value="Sign-In"/>'; - - input_area.empty(); - input_area.append(html); - - $('#'+id).focus(); - }, - setDemoMode: function (demoMode) { - this.demo = demoMode; - }, - setAjaxHandler: function (ajaxFunction) { - this.ajaxHandler = ajaxFunction; - } -}; diff --git a/samples/javaconfig/openid/src/main/resources/users.ldif b/samples/javaconfig/openid/src/main/resources/users.ldif deleted file mode 100644 index ae43438b1c4..00000000000 --- a/samples/javaconfig/openid/src/main/resources/users.ldif +++ /dev/null @@ -1,42 +0,0 @@ -dn: ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: groups - -dn: ou=people,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: people - -dn: uid=admin,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Rod Johnson -sn: Johnson -uid: admin -userPassword: password - -dn: uid=user,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Dianne Emu -sn: Emu -uid: user -userPassword: password - -dn: cn=user,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: user -member: uid=admin,ou=people,dc=springframework,dc=org -member: uid=user,ou=people,dc=springframework,dc=org - -dn: cn=admin,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: admin -member: uid=admin,ou=people,dc=springframework,dc=org \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/views/login.html b/samples/javaconfig/openid/src/main/resources/views/login.html deleted file mode 100644 index 4c6f86c51a2..00000000000 --- a/samples/javaconfig/openid/src/main/resources/views/login.html +++ /dev/null @@ -1,51 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{::link})"> - <title>Messages : Login</title> - <!-- /Simple OpenID Selector --> - <link rel="stylesheet" th:href="@{/resources/css/openid.css}" /> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <form name="f" th:action="@{/login/openid}" method="post" id="openid_form"> - <p><strong> - NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are - <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - </strong></p> - <input type="hidden" name="action" value="verify" /> - <fieldset> - <legend>Sign-in or Create New Account</legend> - <div th:if="${param.error}" class="alert alert-error"> - Invalid username and password. - </div> - <div th:if="${param.logout}" class="alert alert-success"> - You have been logged out. - </div> - <div id="openid_choice"> - <p>Please click your account provider:</p> - <div id="openid_btns"></div> - - </div> - - <div id="openid_input_area"> - <input id="openid_identifier" name="openid_identifier" type="text" value="http://" /> - <input id="openid_submit" type="submit" value="Sign-In"/> - </div> - <noscript> - <p>OpenID is a service that allows you to log-on to many different websites using a single identity. - Find out <a href="https://openid.net/what/">more about OpenID</a> and <a href="https://openid.net/get/">how to get an OpenID enabled account</a>.</p> - </noscript> - </fieldset> - </form> - - <script type="text/javascript" th:src="@{/resources/js/jquery-1.2.6.min.js}"><!-- --></script> - <script type="text/javascript" th:src="@{/resources/js/openid-jquery.js}"><!-- --></script> - <script type="text/javascript"> - $(document).ready(function() { - openid.init('openid_identifier'); - // openid.setDemoMode(true); Stops form submission for client javascript-only test purposes - }); - </script> - </div> - </body> -</html> diff --git a/samples/javaconfig/openid/src/main/resources/views/user/show.html b/samples/javaconfig/openid/src/main/resources/views/user/show.html deleted file mode 100644 index d2a14f7ad4d..00000000000 --- a/samples/javaconfig/openid/src/main/resources/views/user/show.html +++ /dev/null @@ -1,34 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Messages : Login</title> - <!-- /Simple OpenID Selector --> - <link rel="stylesheet" th:href="@{/resources/css/openid.css}" /> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <h1>User Attributes</h1> - <table class="table table-border"> - <thead> - <tr> - <th>Attribute Name</th> - <th>Attribute Value</th> - </tr> - </thead> - <tbody> - <tr> - <td>ID</td> - <td th:text="${authentication.identityUrl}">https://example.com/user/id</td> - </tr> - <tr th:each="attribute : ${authentication.attributes}"> - <td th:text="${attribute.name}">Attribute Name</td> - <td> - <dl th:each="value : ${attribute.values}"> - <dd th:text="${value}">Attribute Value</dd> - </dl> - </td> - </tr> - </tbody> - </table> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/resources/views/user/show.jspx b/samples/javaconfig/openid/src/main/resources/views/user/show.jspx deleted file mode 100644 index c72fcfc3cff..00000000000 --- a/samples/javaconfig/openid/src/main/resources/views/user/show.jspx +++ /dev/null @@ -1,42 +0,0 @@ -<jsp:root - xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" - xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" - version="2.0"> - <jsp:directive.page language="java" contentType="text/html"/> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title>User</title> - </head> - <body> - <h1>User Attributes</h1> - <table class="table"> - <thead> - <tr> - <th>Attribute Name</th> - <th>Attribute Value</th> - </tr> - </thead> - <tbody> - <tr> - <td>ID</td> - <td><c:out value="${authentication.identityUrl}"/></td> - </tr> - <c:forEach items="${authentication.attributes}" var="attribute"> - <tr> - <td><c:out value="${attribute.name}"/></td> - <td> - <dl> - <c:forEach items="${attribute.values}" var="value"> - <dd><c:out value="${value}"/></dd> - </c:forEach> - </dl> - </td> - </tr> - </c:forEach> - </tbody> - </table> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/openid/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/openid/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/openid/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/openid/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/openid/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index 776fa09e04d..00000000000 --- a/samples/javaconfig/openid/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.web.FilterChainProxy; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -@WebAppConfiguration -public class SecurityConfigTests { - @Configuration - @ComponentScan(basePackages = "org.springframework.security.samples.config") - public static class Config { - } - - @Autowired - private FilterChainProxy springSecurityFilterChain; - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/preauth/spring-security-samples-javaconfig-preauth.gradle b/samples/javaconfig/preauth/spring-security-samples-javaconfig-preauth.gradle deleted file mode 100644 index 646b6878d06..00000000000 --- a/samples/javaconfig/preauth/spring-security-samples-javaconfig-preauth.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 512365bcf0c..00000000000 --- a/samples/javaconfig/preauth/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/login", "/resources/**").permitAll() - .anyRequest().authenticated() - ) - .jee((jee) -> - jee - .mappableRoles("USER", "ADMIN") - ); - } - // @formatter:on -} diff --git a/samples/javaconfig/preauth/src/main/resources/logback.xml b/samples/javaconfig/preauth/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/preauth/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/preauth/src/main/resources/views/login.html b/samples/javaconfig/preauth/src/main/resources/views/login.html deleted file mode 100644 index 225e08832b2..00000000000 --- a/samples/javaconfig/preauth/src/main/resources/views/login.html +++ /dev/null @@ -1,24 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Please Login</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <form name="f" th:action="@{/j_security_check}" method="post"> - <fieldset> - <legend>Please Login</legend> - <div th:if="${param.error}" class="alert alert-error">Invalid - username and password.</div> - <div th:if="${param.logout}" class="alert alert-success">You - have been logged out.</div> - <label for="username">Username</label> <input type="text" - id="username" name="username" /> <label for="password">Password</label> - <input type="password" id="password" name="password" /> - <div class="form-actions"> - <button type="submit" class="btn">Log in</button> - </div> - </fieldset> - </form> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/preauth/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/preauth/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/preauth/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/preauth/src/main/webapp/WEB-INF/web.xml b/samples/javaconfig/preauth/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index c2fd6603b9d..00000000000 --- a/samples/javaconfig/preauth/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <login-config> - <auth-method>FORM</auth-method> - <form-login-config> - <form-login-page>/login</form-login-page> - <form-error-page>/login?error</form-error-page> - </form-login-config> - </login-config> - - <security-role> - <role-name>ROLE_USER</role-name> - </security-role> - <security-constraint> - <web-resource-collection> - <web-resource-name>Public</web-resource-name> - <description>Matches unconstrained pages</description> - <url-pattern>/login</url-pattern> - <url-pattern>/logout</url-pattern> - <url-pattern>/resources/*</url-pattern> - </web-resource-collection> - </security-constraint> - <security-constraint> - <web-resource-collection> - <web-resource-name>Secured Areas</web-resource-name> - <url-pattern>/*</url-pattern> - </web-resource-collection> - <auth-constraint> - <role-name>ROLE_USER</role-name> - </auth-constraint> - </security-constraint> -</web-app> diff --git a/samples/javaconfig/preauth/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/preauth/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/preauth/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/rememberme/spring-security-samples-javaconfig-rememberme.gradle b/samples/javaconfig/rememberme/spring-security-samples-javaconfig-rememberme.gradle deleted file mode 100644 index 4d52c8ec1e9..00000000000 --- a/samples/javaconfig/rememberme/spring-security-samples-javaconfig-rememberme.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - compile slf4jDependencies - - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - providedCompile 'javax.servlet:javax.servlet-api' - - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 2570717394f..00000000000 --- a/samples/javaconfig/rememberme/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -import static org.springframework.security.config.Customizer.withDefaults; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER").and() - .withUser("admin").password("password").roles("USER", "ADMIN"); - } - // @formatter:on - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .antMatchers("/resources/**").permitAll() - .anyRequest().authenticated() - ) - .formLogin((formLogin) -> - formLogin - .loginPage("/login") - .permitAll() - ) - .rememberMe(withDefaults()); - } - // @formatter:on -} diff --git a/samples/javaconfig/rememberme/src/main/resources/logback.xml b/samples/javaconfig/rememberme/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/rememberme/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/rememberme/src/main/resources/views/login.html b/samples/javaconfig/rememberme/src/main/resources/views/login.html deleted file mode 100644 index 55b40468e36..00000000000 --- a/samples/javaconfig/rememberme/src/main/resources/views/login.html +++ /dev/null @@ -1,26 +0,0 @@ -<html xmlns:th="https://www.thymeleaf.org"> - <head th:include="layout :: head(title=~{::title},links=~{})"> - <title>Please Login</title> - </head> - <body th:include="layout :: body" th:with="content=~{::content}"> - <div th:fragment="content"> - <form name="f" th:action="@{/login}" method="post"> - <fieldset> - <legend>Please Login</legend> - <div th:if="${param.error}" class="alert alert-error">Invalid - username and password.</div> - <div th:if="${param.logout}" class="alert alert-success">You - have been logged out.</div> - <label for="username">Username</label> <input type="text" - id="username" name="username" /> <label for="password">Password</label> - <input type="password" id="password" name="password" /> <label - for="remember-me">Remember Me?</label> <input type="checkbox" - id="remember-me" name="remember-me" /> - <div class="form-actions"> - <button type="submit" class="btn">Log in</button> - </div> - </fieldset> - </form> - </div> - </body> -</html> \ No newline at end of file diff --git a/samples/javaconfig/rememberme/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/rememberme/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/rememberme/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/rememberme/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/rememberme/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/rememberme/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/saml2login/spring-security-samples-javaconfig-saml2login.gradle b/samples/javaconfig/saml2login/spring-security-samples-javaconfig-saml2login.gradle deleted file mode 100644 index e1d82499dc9..00000000000 --- a/samples/javaconfig/saml2login/spring-security-samples-javaconfig-saml2login.gradle +++ /dev/null @@ -1,9 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-saml2-service-provider') - compile project(':spring-security-config') - compile slf4jDependencies - - testCompile 'org.springframework:spring-test' -} diff --git a/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index c7044c51929..00000000000 --- a/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; -import org.springframework.security.web.session.HttpSessionEventPublisher; - -/** - * We customize {@link AbstractSecurityWebApplicationInitializer} to enable the - * {@link HttpSessionEventPublisher}. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { - - public MessageSecurityWebApplicationInitializer() { - super(SecurityConfig.class); - } - - @Override - protected boolean enableHttpSessionEventPublisher() { - return true; - } -} diff --git a/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index bc9172d65a4..00000000000 --- a/samples/javaconfig/saml2login/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import java.security.cert.X509Certificate; - -import org.opensaml.security.x509.X509Support; - -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Bean - RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { - RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("one") - .assertingPartyDetails((party) -> party - .entityId("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php") - .verificationX509Credentials((c) -> c.add(assertingPartyVerifyingCredential())) - .singleSignOnServiceLocation("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php") - .wantAuthnRequestsSigned(false) - ) - .build(); - return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeRequests((authz) -> authz - .anyRequest().authenticated() - ) - .saml2Login(Customizer.withDefaults()); - // @formatter:on - } - - Saml2X509Credential assertingPartyVerifyingCredential() { - String bits = - "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD\n" + - "VQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYD\n" + - "VQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwX\n" + - "c2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0Bw\n" + - "aXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJ\n" + - "BgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAa\n" + - "BgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQD\n" + - "DBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlr\n" + - "QHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62\n" + - "E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz\n" + - "2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWW\n" + - "RDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQ\n" + - "nX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5\n" + - "cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gph\n" + - "iJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5\n" + - "ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTAD\n" + - "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduO\n" + - "nRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+v\n" + - "ZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLu\n" + - "xbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6z\n" + - "V9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3\n" + - "lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk"; - try { - X509Certificate certificate = X509Support.decodeCertificate(bits); - return Saml2X509Credential.verification(certificate); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } -} diff --git a/samples/javaconfig/saml2login/src/main/resources/logback.xml b/samples/javaconfig/saml2login/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/saml2login/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/saml2login/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/saml2login/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index 0e2bdba1d5b..00000000000 --- a/samples/javaconfig/saml2login/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/javaconfig/x509/spring-security-samples-javaconfig-x509.gradle b/samples/javaconfig/x509/spring-security-samples-javaconfig-x509.gradle deleted file mode 100644 index 7150dd32912..00000000000 --- a/samples/javaconfig/x509/spring-security-samples-javaconfig-x509.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'ch.qos.logback:logback-classic' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'opensymphony:sitemesh' -} diff --git a/samples/javaconfig/x509/src/etc/ca.pem b/samples/javaconfig/x509/src/etc/ca.pem deleted file mode 100644 index a5b52ca9d7e..00000000000 --- a/samples/javaconfig/x509/src/etc/ca.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDojCCAoqgAwIBAgIEMKX1dzANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMC -R0IxETAPBgNVBAgTCFNjb3RsYW5kMRAwDgYDVQQHEwdHbGFzZ293MRkwFwYDVQQK -ExBTcHJpbmcgRnJhbWV3b3JrMRgwFgYDVQQLEw9TcHJpbmcgU2VjdXJpdHkxIDAe -BgNVBAMTF1NwcmluZyBTZWN1cml0eSBUZXN0IENBMB4XDTA4MDEyNTExMTIyMVoX -DTE4MDIyNTAwMDAwMFowgYkxCzAJBgNVBAYTAkdCMREwDwYDVQQIEwhTY290bGFu -ZDEQMA4GA1UEBxMHR2xhc2dvdzEZMBcGA1UEChMQU3ByaW5nIEZyYW1ld29yazEY -MBYGA1UECxMPU3ByaW5nIFNlY3VyaXR5MSAwHgYDVQQDExdTcHJpbmcgU2VjdXJp -dHkgVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzl/wEe -snYrwqaGZuB8hmwACtptazh1+eXCfd66FkioxlLF7yTnjCC7DT+vmMgSuThIEIsN -xlxLpEgyU3bU8GIuR8wyYIyvuSMcptdFJLV7NKYuRycxpDuqimTM7Br0nfNgKVEv -1QwguGWr6YN3aZ68/xe/D5xyPhakKu++7VFXIXw9f0+nqojdrFTqQ6l9GAVRgfX6 -h4JOaV1VFx83y2pnFj0iFneVxRcvXyWnyXlcOvJDIyVuyS/hYxb+E5rtBvp5XQ0o -5CP4OMwCZGx/jEqlL8oO7BwEgu9aEBxKvoIKJmHDTHgWIxgawTrKabmong4utnMI -yNrhsI77bmh2U7UCAwEAAaMQMA4wDAYDVR0PBAUDAwcGADANBgkqhkiG9w0BAQUF -AAOCAQEAuD8W9Ukkfyi0y65mwguFVAqBC3RSTMRXcjbLQV4rMDM/Q9kjA6acY4Ta -WgxGTwNCydqaqwDVsmn+6Je8Lp2xm9KLDLypVdNopGs+Mlfo55dhwqymXkQw1oJI -CPhR3nBmGEnSWW0UY9bPlpxRF2D5GDVwpuxDtXvWa4baPwRRI9MxwPWHA3ITl+fc -s9QVKy+pRAnuP9MSIp755cJ1CODOn2ElNCqnxxsZmcWcmI3LkHAwTmegl3PVvhrk -MKMEA/neshh/M/hWGNTFt77Hoa7pU9dv5RCWFvZPqsUgPrwGrmUvcmSDir3lSWQm -SuSED2LKVo+BFqwWS+jp49AR9b8B/Q== ------END CERTIFICATE----- diff --git a/samples/javaconfig/x509/src/etc/dianne.p12 b/samples/javaconfig/x509/src/etc/dianne.p12 deleted file mode 100644 index 6e5ba218db7..00000000000 Binary files a/samples/javaconfig/x509/src/etc/dianne.p12 and /dev/null differ diff --git a/samples/javaconfig/x509/src/etc/rod.p12 b/samples/javaconfig/x509/src/etc/rod.p12 deleted file mode 100644 index 4cd05644305..00000000000 Binary files a/samples/javaconfig/x509/src/etc/rod.p12 and /dev/null differ diff --git a/samples/javaconfig/x509/src/etc/scott.p12 b/samples/javaconfig/x509/src/etc/scott.p12 deleted file mode 100644 index f0a6357e730..00000000000 Binary files a/samples/javaconfig/x509/src/etc/scott.p12 and /dev/null differ diff --git a/samples/javaconfig/x509/src/etc/server.jks b/samples/javaconfig/x509/src/etc/server.jks deleted file mode 100644 index aaa1119fff4..00000000000 Binary files a/samples/javaconfig/x509/src/etc/server.jks and /dev/null differ diff --git a/samples/javaconfig/x509/src/etc/server.xml b/samples/javaconfig/x509/src/etc/server.xml deleted file mode 100644 index 0a38efa0fd0..00000000000 --- a/samples/javaconfig/x509/src/etc/server.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - https://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. ---><Server port="8006" shutdown="SHUTDOWN"> - <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/> - <Listener className="org.apache.catalina.core.JasperListener"/> - <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/> - <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/> - <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/> - - <GlobalNamingResources> - <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/> - </GlobalNamingResources> - - <Service name="Catalina"> - <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> - - <Connector SSLEnabled="true" clientAuth="true" keystoreFile="${basedir}/samples/javaconfig/x509/src/etc/server.jks" keystorePass="password" maxThreads="150" port="8443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS" trustStorePass="password" truststoreFile="${basedir}/samples/javaconfig/x509/src/etc/server.jks"/> - - <Connector port="8010" protocol="AJP/1.3" redirectPort="8443"/> - - - <Engine defaultHost="localhost" name="Catalina"> - <Realm className="org.apache.catalina.realm.LockOutRealm"> - <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> - </Realm> - - <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"> - <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log." suffix=".txt"/> - <Context docBase="spring-security-samples-javaconfig-x509-3.2.x" path="/sample" reloadable="true" source="org.eclipse.jst.j2ee.server:spring-security-samples-javaconfig-x509-3.2.x"/></Host> - </Engine> - </Service> -</Server> \ No newline at end of file diff --git a/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java deleted file mode 100644 index f851f82de06..00000000000 --- a/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; - -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -public class MessageSecurityWebApplicationInitializer extends - AbstractSecurityWebApplicationInitializer { -} diff --git a/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/SecurityConfig.java deleted file mode 100644 index 040dd757629..00000000000 --- a/samples/javaconfig/x509/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2019 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -import static org.springframework.security.config.Customizer.withDefaults; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - // @formatter:off - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) - throws Exception { - auth. - inMemoryAuthentication() - .withUser("dianne").password("password").roles("USER").and() - .withUser("rod").password("password").roles("USER", "ADMIN").and() - .withUser("scott").password("password").roles("USER"); - } - // @formatter:on - - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests((authorizeRequests) -> - authorizeRequests - .anyRequest().authenticated() - ) - .x509(withDefaults()); - } - // @formatter:on -} diff --git a/samples/javaconfig/x509/src/main/resources/logback.xml b/samples/javaconfig/x509/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/javaconfig/x509/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/javaconfig/x509/src/main/webapp/META-INF/MANIFEST.MF b/samples/javaconfig/x509/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators.xml b/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators.xml deleted file mode 100644 index 7df83f1950f..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators.xml +++ /dev/null @@ -1,5 +0,0 @@ -<decorators defaultdir="/WEB-INF/decorators"> - <decorator name="main" page="main.jsp"> - <pattern>/*</pattern> - </decorator> -</decorators> diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators/main.jsp b/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators/main.jsp deleted file mode 100644 index 20726cb81e2..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/decorators/main.jsp +++ /dev/null @@ -1,138 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:c="http://java.sun.com/jsp/jstl/core" - xmlns:fn="http://java.sun.com/jsp/jstl/functions" - xmlns:decorator="https://www.opensymphony.com/sitemesh/decorator" - xmlns:page="https://www.opensymphony.com/sitemesh/page" - xmlns:form="http://www.springframework.org/tags/form" - xmlns:spring="http://www.springframework.org/tags" - xmlns:sec="http://www.springframework.org/security/tags" - xmlns:tags="urn:jsptagdir:/WEB-INF/tags" version="2.0"> - - <jsp:directive.page contentType="text/html" pageEncoding="UTF-8" /> - <jsp:output omit-xml-declaration="true" /> - <jsp:output doctype-root-element="HTML" - doctype-system="about:legacy-compat" /> -<html lang="en"> - <head> - <title>SecureMail: <decorator:title/></title> - <c:url var="faviconUrl" value="/resources/img/favicon.ico"/> - <link rel="icon" type="image/x-icon" href="${faviconUrl}"/> - <c:url var="bootstrapUrl" value="/resources/css/bootstrap.css"/> - <link href="${bootstrapUrl}" rel="stylesheet"></link> - <style type="text/css"> - /* Sticky footer styles - -------------------------------------------------- */ - - html, - body { - height: 100%; - /* The html and body elements cannot have any padding or margin. */ - } - - /* Wrapper for page content to push down footer */ - #wrap { - min-height: 100%; - height: auto !important; - height: 100%; - /* Negative indent footer by it's height */ - margin: 0 auto -60px; - } - - /* Set the fixed height of the footer here */ - #push, - #footer { - height: 60px; - } - #footer { - background-color: #f5f5f5; - } - - /* Lastly, apply responsive CSS fixes as necessary */ - @media (max-width: 767px) { - #footer { - margin-left: -20px; - margin-right: -20px; - padding-left: 20px; - padding-right: 20px; - } - } - - - - /* Custom page CSS - -------------------------------------------------- */ - /* Not required for template or sticky footer method. */ - - .container { - width: auto; - max-width: 680px; - } - .container .credit { - margin: 20px 0; - text-align: center; - } - a { - color: green; - } - .navbar-form { - margin-left: 1em; - } - </style> - <c:url var="bootstrapResponsiveUrl" value="/resources/css/bootstrap-responsive.css"/> - <link href="${bootstrapResponsiveUrl}" rel="stylesheet"></link> - - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - </head> - - - <body> - <div id="wrap"> - <div class="navbar navbar-inverse navbar-static-top"> - <div class="navbar-inner"> - <div class="container"> - <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </a> - <c:url var="homeUrl" value="/"/> - <c:url var="logoUrl" value="/resources/img/logo.png"/> - <a class="brand" href="${homeUrl}"><img src="${logoUrl}" alt="Spring Security Sample"/></a> - <div class="nav-collapse collapse"> - <c:if test="${pageContext.request.remoteUser != null}"> - <c:url var="logoutUrl" value="/logout"/> - <form:form class="navbar-form pull-right" action="${logoutUrl}" method="post"><input type="submit" value="Log out" /></form:form> - <p class="navbar-text pull-right"> - <c:out value="${pageContext.request.remoteUser}"/> - </p> - </c:if> - <ul class="nav"> - <c:url var="inboxUrl" value="/"/> - <li><a href="${inboxUrl}">Inbox</a></li> - <c:url var="composeUrl" value="/?form"/> - <li><a href="${composeUrl}">Compose</a></li> - </ul> - </div> - </div> - </div> - </div> - <!-- Begin page content --> - <div class="container"> - <decorator:body/> - </div> - - <div id="push"><!-- --></div> - </div> - - <div id="footer"> - <div class="container"> - <p class="muted credit">Visit the <a href="#">Spring Security</a> site for more <a href="#">samples</a>.</p> - </div> - </div> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/login.jspx b/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/login.jspx deleted file mode 100644 index 5b56e9b8cca..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/login.jspx +++ /dev/null @@ -1,36 +0,0 @@ -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" - xmlns:form="http://www.springframework.org/tags/form" version="2.0"> - <jsp:directive.page language="java" contentType="text/html" /> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> -<head> -<title>Please Login</title> -</head> -<body> - <c:url value="/j_security_check" var="loginUrl"/> - <form name="f" action="${loginUrl}" method="post"> - <fieldset> - <legend>Please Login</legend> - <c:if test="${param.error != null}"> - <div class="alert alert-error"> - Failed to login. - </div> - </c:if> - <c:if test="${param.logout != null}"> - <div class="alert alert-success"> - You have been logged out. - </div> - </c:if> - <label for="username">Username</label> - <input type="text" id="username" name="username" value="${username}"/> - <label for="password">Password</label> - <input type="password" id="password" name="password"/> - <div class="form-actions"> - <button type="submit" class="btn">Log in</button> - </div> - </fieldset> - </form> -</body> -</html> -</jsp:root> diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/compose.jspx b/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/compose.jspx deleted file mode 100644 index a92f7e2d6cf..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/compose.jspx +++ /dev/null @@ -1,26 +0,0 @@ -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" - xmlns:form="http://www.springframework.org/tags/form" version="2.0"> - <jsp:directive.page language="java" contentType="text/html" /> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title>Compose</title> - </head> - <body> - <div class="container"> - <h1>Messages : Create</h1> - <form:form action="./" method="post" modelAttribute="message"> - <form:errors path="*" element="div" cssClass="alert alert-error" /> - <label for="summary">Summary</label> - <form:input type="text" path="summary" class="input-xxlarge" /> - <label for="text">Message</label> - <form:textarea path="text" class="input-xxlarge"></form:textarea> - <div class="form-actions"> - <input type="submit" value="Create" /> - </div> - </form:form> - </div> - </body> - </html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/inbox.jspx b/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/inbox.jspx deleted file mode 100644 index ed02d313c6f..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/inbox.jspx +++ /dev/null @@ -1,40 +0,0 @@ -<jsp:root - xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" - xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" - version="2.0"> - <jsp:directive.page language="java" contentType="text/html"/> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title>Inbox</title> - </head> - <body> - <h1>Inbox</h1> - <table class="table"> - <thead> - <tr> - <th>Created</th> - <th>Summary</th> - </tr> - </thead> - <tbody> - <c:if test="${empty messages}"> - <tr> - <td colspan="2" class="msg">You have not received any mail yet.</td> - </tr> - </c:if> - <c:forEach items="${messages}" var="message"> - <tr> - <td><fmt:formatDate value="${message.created.time}"/></td> - <spring:url var="messageUrl" value="/{id}"> - <spring:param name="id" value="${message.id}"/> - </spring:url> - <td><a href="${messageUrl}"><c:out value="${message.summary}"/></a></td> - </tr> - </c:forEach> - </tbody> - </table> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/show.jspx b/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/show.jspx deleted file mode 100644 index 82007c267b0..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/views/messages/show.jspx +++ /dev/null @@ -1,24 +0,0 @@ -<jsp:root - xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:spring="http://www.springframework.org/tags" - xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" - xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:form="http://www.springframework.org/tags/form" - version="2.0"> - <jsp:directive.page language="java" contentType="text/html"/> -<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> - <head> - <title><c:out value="${message.summary}"/></title> - </head> - <body> - <div class="container"> - <h1>Message : <c:out value="${message.summary}"/></h1> - <dl> - <dt>Created</dt> - <dd><fmt:formatDate value="${message.created.time}"/></dd> - <dt>Message</dt> - <dd><c:out value="${message.text}"/></dd> - </dl> - </div> - </body> -</html> -</jsp:root> \ No newline at end of file diff --git a/samples/javaconfig/x509/src/main/webapp/WEB-INF/web.xml- b/samples/javaconfig/x509/src/main/webapp/WEB-INF/web.xml- deleted file mode 100644 index b87ff75318c..00000000000 --- a/samples/javaconfig/x509/src/main/webapp/WEB-INF/web.xml- +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <login-config> - <auth-method>FORM</auth-method> - <form-login-config> - <form-login-page>/login</form-login-page> - <form-error-page>/login?error</form-error-page> - </form-login-config> - </login-config> - - <security-role> - <role-name>ROLE_USER</role-name> - </security-role> - <security-constraint> - <web-resource-collection> - <web-resource-name>Public</web-resource-name> - <description>Matches unconstrained pages</description> - <url-pattern>/login</url-pattern> - <url-pattern>/logout</url-pattern> - <url-pattern>/resources/*</url-pattern> - </web-resource-collection> - </security-constraint> - <security-constraint> - <web-resource-collection> - <web-resource-name>Secured Areas</web-resource-name> - <url-pattern>/*</url-pattern> - </web-resource-collection> - <auth-constraint> - <role-name>ROLE_USER</role-name> - </auth-constraint> - </security-constraint> -</web-app> diff --git a/samples/javaconfig/x509/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/x509/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index dd807f16d14..00000000000 --- a/samples/javaconfig/x509/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = SecurityConfig.class) -public class SecurityConfigTests { - - @Test - public void securityConfigurationLoads() { - } -} diff --git a/samples/xml/aspectj/spring-security-samples-xml-aspectj.gradle b/samples/xml/aspectj/spring-security-samples-xml-aspectj.gradle deleted file mode 100644 index dfc2b98418a..00000000000 --- a/samples/xml/aspectj/spring-security-samples-xml-aspectj.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' -apply plugin: 'io.freefair.aspectj.post-compile-weaving' - -dependencies { - compile project(':spring-security-core') - - aspect project(':spring-security-aspects') - - runtime project(':spring-security-aspects') - runtime project(':spring-security-config') -} - -aspectj { - version = aspectjVersion -} - diff --git a/samples/xml/aspectj/src/main/java/sample/aspectj/SecuredService.java b/samples/xml/aspectj/src/main/java/sample/aspectj/SecuredService.java deleted file mode 100644 index 9f81cf17339..00000000000 --- a/samples/xml/aspectj/src/main/java/sample/aspectj/SecuredService.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.springframework.security.access.annotation.Secured; - -/** - * Service which is secured on the class level - * - * @author Mike Wiesner - * @since 3.0 - */ -@Secured("ROLE_USER") -public class SecuredService { - - public void secureMethod() { - // nothing - } - -} diff --git a/samples/xml/aspectj/src/main/java/sample/aspectj/Service.java b/samples/xml/aspectj/src/main/java/sample/aspectj/Service.java deleted file mode 100644 index 70b8c0c71f9..00000000000 --- a/samples/xml/aspectj/src/main/java/sample/aspectj/Service.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.springframework.security.access.annotation.Secured; - -/** - * Service which is secured on method level - * - * @author Mike Wiesner - * @since 1.0 - */ -public class Service { - - @Secured("ROLE_USER") - public void secureMethod() { - // nothing - } - - public void publicMethod() { - // nothing - } - -} diff --git a/samples/xml/aspectj/src/main/resources/aspectj-context.xml b/samples/xml/aspectj/src/main/resources/aspectj-context.xml deleted file mode 100644 index 31ee9234945..00000000000 --- a/samples/xml/aspectj/src/main/resources/aspectj-context.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:sec="http://www.springframework.org/schema/security" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <sec:global-method-security secured-annotations="enabled" mode="aspectj" /> -<!-- - <bean id="aspectJSecurityInterceptor" - class="org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor"> - <property name="authenticationManager" ref="authenticationManager" /> - <property name="accessDecisionManager" ref="accessDecisionManager" /> - <property name="securityMetadataSource"> - <bean - class="org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource" /> - </property> - </bean> - - <bean id="authenticationManager" - class="org.springframework.security.authentication.ProviderManager"> - <property name="providers"> - <bean - class="org.springframework.security.authentication.TestingAuthenticationProvider" /> - </property> - </bean> - - <bean id="accessDecisionManager" - class="org.springframework.security.access.vote.AffirmativeBased"> - <property name="decisionVoters"> - <list> - <bean - class="org.springframework.security.access.vote.RoleVoter" /> - </list> - </property> - </bean> - - <bean - class="org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect" - factory-method="aspectOf"> - <property name="securityInterceptor" ref="aspectJSecurityInterceptor" /> - </bean> - --> - <bean class="sample.aspectj.Service" /> - - <bean class="sample.aspectj.SecuredService" /> - -</beans> diff --git a/samples/xml/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java b/samples/xml/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java deleted file mode 100644 index 5e78a92108e..00000000000 --- a/samples/xml/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.aspectj; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = "classpath:aspectj-context.xml") -public class AspectJInterceptorTests { - private Authentication admin = new UsernamePasswordAuthenticationToken("test", "xxx", - AuthorityUtils.createAuthorityList("ROLE_ADMIN")); - private Authentication user = new UsernamePasswordAuthenticationToken("test", "xxx", - AuthorityUtils.createAuthorityList("ROLE_USER")); - - @Autowired - private Service service; - - @Autowired - private SecuredService securedService; - - @Test - public void testPublicMethod() { - service.publicMethod(); - } - - @Test(expected = AuthenticationCredentialsNotFoundException.class) - public void testSecuredMethodNotAuthenticated() { - service.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void testSecuredMethodWrongRole() { - SecurityContextHolder.getContext().setAuthentication(admin); - service.secureMethod(); - } - - @Test - public void testSecuredMethodEverythingOk() { - SecurityContextHolder.getContext().setAuthentication(user); - service.secureMethod(); - } - - @Test(expected = AuthenticationCredentialsNotFoundException.class) - public void testSecuredClassNotAuthenticated() { - securedService.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void testSecuredClassWrongRole() { - SecurityContextHolder.getContext().setAuthentication(admin); - securedService.secureMethod(); - } - - @Test(expected = AccessDeniedException.class) - public void testSecuredClassWrongRoleOnNewedInstance() { - SecurityContextHolder.getContext().setAuthentication(admin); - new SecuredService().secureMethod(); - } - - @Test - public void testSecuredClassEverythingOk() { - SecurityContextHolder.getContext().setAuthentication(user); - securedService.secureMethod(); - new SecuredService().secureMethod(); - } - - @After - public void tearDown() { - SecurityContextHolder.clearContext(); - } - -} diff --git a/samples/xml/aspectj/src/test/resources/logback-test.xml b/samples/xml/aspectj/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/aspectj/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/cas/Readme.txt b/samples/xml/cas/Readme.txt deleted file mode 100644 index cfa67e6ad7e..00000000000 --- a/samples/xml/cas/Readme.txt +++ /dev/null @@ -1,12 +0,0 @@ -To run a CAS server and client application, just execute the command - -./gradlew cas - -from the project root directory. You should then be able to point your browser at - -https://localhost:8443/cas-sample/ - -to view the sample application. On attempting to access a secure page, -you'll be redirected to the CAS server where you can log in with one of -the usernames from the sample application context (enter the username in the -password field too, to authenticate to CAS in testing mode). diff --git a/samples/xml/cas/cassample/spring-security-samples-xml-cassample.gradle b/samples/xml/cas/cassample/spring-security-samples-xml-cassample.gradle deleted file mode 100644 index 9fedcea3bd5..00000000000 --- a/samples/xml/cas/cassample/spring-security-samples-xml-cassample.gradle +++ /dev/null @@ -1,61 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -def keystore = "$rootDir/samples/certificates/server.jks" -def password = 'password' - -dependencies { - compile project(':spring-security-cas') - compile project(':spring-security-core') - compile 'org.jasig.cas.client:cas-client-core' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-web') - runtime 'ch.qos.logback:logback-classic' - runtime 'net.sf.ehcache:ehcache' - runtime 'org.slf4j:jcl-over-slf4j' - runtime 'org.springframework:spring-context-support' - - integrationTestCompile project(':spring-security-cas') - integrationTestCompile seleniumDependencies - integrationTestCompile 'org.eclipse.jetty:jetty-server' - integrationTestCompile 'org.eclipse.jetty:jetty-servlet' - integrationTestCompile 'org.slf4j:jcl-over-slf4j' -} - -integrationTest { - dependsOn { casServer().tasks.appBeforeIntegrationTest } - doFirst { - def casServiceHost = 'localhost:' + gretty.httpsPort - def casServerHost = 'localhost:' + casServer().gretty.httpsPort - systemProperties['cas.server.host'] = casServerHost - systemProperties['cas.service.host'] = casServiceHost - systemProperties['jar.path'] = jar.archivePath - systemProperties['javax.net.ssl.trustStore'] = keystore - systemProperties['javax.net.ssl.trustStorePassword'] = password - } - finalizedBy { casServer().tasks.appAfterIntegrationTest } -} - -prepareAppServerForIntegrationTests { - dependsOn { casServer().tasks.appBeforeIntegrationTest } - doLast { - def casServiceHost = 'localhost:' + project.gretty.httpsPort - def casServerHost = 'localhost:' + casServer().gretty.httpsPort - gretty.jvmArgs += ["-Dcas.server.host=${casServerHost}", "-Dcas.service.host=${casServiceHost}"] - } -} - -gretty { - contextPath = '/cas-sample/' - httpsEnabled = true - httpsPort = 8443 - sslKeyStorePath = keystore - sslKeyStorePassword = password - jvmArgs = ["-Djavax.net.ssl.trustStore=${keystore}", "-Djavax.net.ssl.trustStorePassword=${password}"] -} - -def casServer() { - project(':spring-security-samples-xml-casserver') -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleProxyTests.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleProxyTests.java deleted file mode 100644 index 906952cb63a..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleProxyTests.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas; - -import java.util.HashMap; -import java.util.Map; - -import org.jasig.cas.client.proxy.Cas20ProxyRetriever; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; - -import org.springframework.security.samples.cas.pages.AccessDeniedPage; -import org.springframework.security.samples.cas.pages.ExtremelySecurePage; -import org.springframework.security.samples.cas.pages.LoginPage; -import org.springframework.security.samples.cas.pages.ProxyTicketSamplePage; -import org.springframework.security.samples.cas.pages.SecurePage; -import org.springframework.security.samples.cas.pages.UnauthorizedPage; - -/** - * Tests authenticating to the CAS Sample application using Proxy Tickets. Geb is used to authenticate the {@link JettyCasService} - * to the CAS Server in order to obtain the Ticket Granting Ticket. Afterwards HttpClient is used for accessing the CAS Sample application - * using Proxy Tickets obtained using the Proxy Granting Ticket. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class CasSampleProxyTests { - private static String serverUrl; - private static String serviceUrl; - private static JettyCasService service; - private static Cas20ProxyRetriever retriever; - - private WebDriver driver = new HtmlUnitDriver(); - - private LoginPage login; - private SecurePage secure; - private ExtremelySecurePage extremelySecure; - private ProxyTicketSamplePage proxyTicketSample; - private AccessDeniedPage accessDenied; - private UnauthorizedPage unauthorized; - - @BeforeClass - public static void setupClass() { - String serverHost = System.getProperty("cas.server.host", "localhost:8443"); - serverUrl = "https://" + serverHost + "/cas"; - String serviceHost = System.getProperty("cas.service.host", "localhost:8443"); - serviceUrl = "https://" + serviceHost + "/cas-sample"; - service = new JettyCasService().init(serverUrl); - retriever = new Cas20ProxyRetriever(serverUrl, "UTF-8"); - } - - @AfterClass - public static void teardownClass() throws Exception { - service.stop(); - } - - @Before - public void setup() { - this.login = new LoginPage(this.driver, serverUrl); - this.secure = new SecurePage(this.driver, serviceUrl); - this.extremelySecure = new ExtremelySecurePage(this.driver, serviceUrl); - this.proxyTicketSample = new ProxyTicketSamplePage(this.driver, serviceUrl); - this.accessDenied = new AccessDeniedPage(this.driver); - this.unauthorized = new UnauthorizedPage(this.driver); - } - - @After - public void teardown() { - this.driver.close(); - } - - @Test - public void securePageWhenRoleUserThenDisplays() { - this.login.to(this::serviceParam).assertAt().login("scott"); - this.secure.to(this::ticketParam).assertAt(); - } - - @Test - public void proxyTicketSamplePageWhenRoleUserThenDisplays() { - this.login.to(this::serviceParam).assertAt().login("scott"); - this.proxyTicketSample.to(this::ticketParam).assertAt(); - } - - @Test - public void extremelySecurePageWhenRoleUserThenDenies() { - this.login.to(this::serviceParam).assertAt().login("scott"); - this.extremelySecure.to(this::ticketParam); - this.accessDenied.assertAt(); - } - - @Test - public void extremelySecurePageWhenRoleSupervisorThenDisplays() { - this.login.to(this::serviceParam).assertAt().login("rod"); - this.extremelySecure.to(this::ticketParam).assertAt(); - } - - @Test - public void extremelySecurePageWhenReusingTicketThenDisplays() { - this.login.to(this::serviceParam).assertAt().login("rod"); - Map<String, String> ptCache = new HashMap<>(); - this.extremelySecure.to((url) -> url + "?ticket=" + ptCache.computeIfAbsent(url, this::getPt)).assertAt(); - this.extremelySecure.to((url) -> url + "?ticket=" + ptCache.get(url)).assertAt(); - } - - @Test - public void securePageWhenInvalidTicketThenFails() { - this.login.to(this::serviceParam).assertAt().login("scott"); - this.secure.to((url) -> url + "?ticket=invalid"); - this.unauthorized.assertAt(); - } - - private String serviceParam(String url) { - return url + "?service=" + service.serviceUrl(); - } - - private String ticketParam(String url) { - return url + "?ticket=" + getPt(url); - } - - /** - * Obtains a proxy ticket using the pgt from the {@link #service}. - * @param targetService the targetService that the proxy ticket will be valid for - * @return a proxy ticket for targetService - */ - String getPt(String targetService) { - assert service.pgt != null; - return retriever.getProxyTicketIdFor(service.pgt, targetService); - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleTests.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleTests.java deleted file mode 100644 index e820531969c..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/CasSampleTests.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; - -import org.springframework.security.samples.cas.pages.AccessDeniedPage; -import org.springframework.security.samples.cas.pages.ExtremelySecurePage; -import org.springframework.security.samples.cas.pages.HomePage; -import org.springframework.security.samples.cas.pages.LocalLogoutPage; -import org.springframework.security.samples.cas.pages.LoginPage; -import org.springframework.security.samples.cas.pages.ProxyTicketSamplePage; -import org.springframework.security.samples.cas.pages.SecurePage; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests the CAS sample application using service tickets. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class CasSampleTests { - private WebDriver driver = new HtmlUnitDriver(); - - private String serviceUrl; - private String serverUrl; - - private LoginPage login; - - private HomePage home; - private SecurePage secure; - private ExtremelySecurePage extremelySecure; - private ProxyTicketSamplePage proxyTicketSample; - private LocalLogoutPage localLogout; - private AccessDeniedPage accessDenied; - - @Before - public void setup() { - String serverHost = System.getProperty("cas.server.host", "localhost:8443"); - this.serverUrl = "https://" + serverHost + "/cas"; - String serviceHost = System.getProperty("cas.service.host", "localhost:8443"); - this.serviceUrl = "https://" + serviceHost + "/cas-sample"; - this.login = new LoginPage(this.driver, this.serverUrl); - this.home = new HomePage(this.driver, this.serviceUrl); - this.secure = new SecurePage(this.driver, this.serviceUrl); - this.extremelySecure = new ExtremelySecurePage(this.driver, this.serviceUrl); - this.proxyTicketSample = new ProxyTicketSamplePage(this.driver, this.serviceUrl); - this.localLogout = new LocalLogoutPage(this.driver, this.serviceUrl); - this.accessDenied = new AccessDeniedPage(this.driver); - } - - @After - public void tearDown() { - this.driver.close(); - } - - @Test - public void homePageWhenUnauthenticatedUserThenSucceeds() { - this.home.to().assertAt(); - } - - @Test - public void extremelySecurePageWhenUnauthenticatedThenRequiresLogin() { - this.extremelySecure.to(); - this.login.assertAt(); - } - - @Test - public void authenticateWhenInvalidTicketThenFails() { - this.driver.get(this.serviceUrl + "/login/cas?ticket=invalid"); - assertThat(this.driver.findElement(By.tagName("h2")).getText()) - .isEqualTo("Login to CAS failed!"); - } - - @Test - public void securePageWhenUnauthenticatedThenRequiresLogin() { - this.secure.to(); - this.login.assertAt(); - } - - @Test - public void securePageWhenRoleUserThenDisplays() { - this.login.to().login("scott"); - this.secure.to().assertAt(); - } - - @Test - public void proxyTicketSamplePageWhenRoleUserThenDisplays() { - this.login.to().login("scott"); - this.proxyTicketSample.to().assertAt(); - } - - @Test - public void extremelySecurePageWhenRoleUserThenDenies() { - this.login.to().login("scott"); - this.extremelySecure.to(); - this.accessDenied.assertAt(); - } - - @Test - public void localLogoutLinkWhenClickedThenRedirectsToLocalLogoutPage() { - this.login.to().login("scott"); - this.secure.to().logout(); - this.localLogout.assertAt(); - } - - @Test - public void casLogoutWhenClickedThenPerformsCompleteLogout() { - this.login.to().login("scott"); - this.driver.get(this.serverUrl + "/logout"); - this.secure.to(); - this.login.assertAt(); - } - - @Test - public void extremelySecureWhenRoleSupervisorThenDisplays() { - this.login.to().login("rod"); - this.extremelySecure.to().assertAt(); - } - - @Test - public void casLogoutWhenClickedThenExtremelySecurePageRequiresLogin() { - this.login.to().login("scott"); - this.driver.get(this.serverUrl + "/logout"); - this.extremelySecure.to(); - this.login.assertAt(); - } - - @Test - public void casLogoutWhenVisitedThenLogsOutSample() { - this.secure.to(); - this.login.assertAt().login("rod"); - this.secure.assertAt(); - this.driver.get(this.serverUrl + "/logout"); - this.secure.to(); - this.login.assertAt(); - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/JettyCasService.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/JettyCasService.java deleted file mode 100644 index dc9454b7e05..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/JettyCasService.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas; - -import java.io.File; -import java.io.IOException; -import java.net.ServerSocket; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.SecureRequestCustomizer; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; -import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; -import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; -import org.jasig.cas.client.validation.TicketValidationException; - -import org.springframework.util.StringUtils; - -/** - * A CAS Service that allows a PGT to be obtained. This is useful for testing use of proxy tickets. - * - * @author Rob Winch - */ -public class JettyCasService extends Server { - private Cas20ProxyTicketValidator validator; - private int port = availablePort(); - - /** - * The Proxy Granting Ticket. To initialize pgt, authenticate to the CAS Server with the service parameter - * equal to {@link #serviceUrl()}. - */ - String pgt; - - /** - * Start the CAS Service which will be available at {@link #serviceUrl()}. - * - * @param casServerUrl - * @return - */ - JettyCasService init(String casServerUrl) { - System.out.println("Initializing to " + casServerUrl); - ProxyGrantingTicketStorage storage = new ProxyGrantingTicketStorageImpl(); - this.validator = new Cas20ProxyTicketValidator(casServerUrl); - this.validator.setAcceptAnyProxy(true); - this.validator.setProxyGrantingTicketStorage(storage); - this.validator.setProxyCallbackUrl(absoluteUrl("callback")); - - String password = System.getProperty("javax.net.ssl.trustStorePassword", "password"); - - SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(getTrustStore()); - sslContextFactory.setKeyStorePassword(password); - sslContextFactory.setKeyManagerPassword(password); - - HttpConfiguration http_config = new HttpConfiguration(); - http_config.setSecureScheme("https"); - http_config.setSecurePort(availablePort()); - http_config.setOutputBufferSize(32768); - - HttpConfiguration https_config = new HttpConfiguration(http_config); - SecureRequestCustomizer src = new SecureRequestCustomizer(); - src.setStsMaxAge(2000); - src.setStsIncludeSubDomains(true); - https_config.addCustomizer(src); - - ServerConnector https = new ServerConnector(this, - new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), - new HttpConnectionFactory(https_config)); - https.setPort(port); - https.setIdleTimeout(500000); - - addConnector(https); - setHandler(new AbstractHandler() { - public void handle(String target, Request baseRequest, - HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - String st = request.getParameter("ticket"); - if (StringUtils.hasText(st)) { - try { - JettyCasService.this.validator.validate(st, JettyCasService.this.serviceUrl()); - } catch (TicketValidationException e) { - throw new IllegalArgumentException(e); - } - } - String pgt = request.getParameter("pgtId"); - if (StringUtils.hasText(pgt)) { - JettyCasService.this.pgt = pgt; - } - response.setStatus(HttpServletResponse.SC_OK); - baseRequest.setHandled(true); - } - }); - - try { - start(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - return this; - } - - /** - * Returns the absolute URL that this CAS service is available at. - * @return - */ - String serviceUrl() { - return absoluteUrl("service"); - } - - /** - * Given a relative url, will provide an absolute url for this CAS Service. - * @param relativeUrl the relative url (i.e. service, callback, etc) - * @return - */ - private String absoluteUrl(String relativeUrl) { - return "https://localhost:" + port + "/" + relativeUrl; - } - - private static String getTrustStore() { - String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore"); - if (trustStoreLocation == null || !new File(trustStoreLocation).isFile()) { - throw new IllegalStateException("Could not find the trust store at path \"" + trustStoreLocation + - "\". Specify the location using the javax.net.ssl.trustStore system property."); - } - return trustStoreLocation; - } - - /** - * Obtains a random available port (i.e. one that is not in use) - * @return - */ - private static int availablePort() { - try (ServerSocket server = new ServerSocket(0)) { - return server.getLocalPort(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/AccessDeniedPage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/AccessDeniedPage.java deleted file mode 100644 index cb143f14ff5..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/AccessDeniedPage.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the access denied page - * - * @author Rob Winch - * @author Josh Cummings - */ -public class AccessDeniedPage { - - private final Content content; - - public AccessDeniedPage(WebDriver driver) { - this.content = PageFactory.initElements(driver, Content.class); - } - - public AccessDeniedPage assertAt() { - assertThat(this.content.header()).contains("403 - Access Denied"); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - public String header() { - return this.header.getText(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ExtremelySecurePage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ExtremelySecurePage.java deleted file mode 100644 index 15243965064..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ExtremelySecurePage.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the extremely secure page of the CAS Sample application. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class ExtremelySecurePage extends Page<ExtremelySecurePage> { - private final Content content; - - public ExtremelySecurePage(WebDriver driver, String baseUrl) { - super(driver, baseUrl + "/secure/extreme"); - this.content = PageFactory.initElements(driver, Content.class); - } - - @Override - public ExtremelySecurePage assertAt() { - assertThat(this.content.getText()).isEqualTo("VERY Secure Page"); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - public String getText() { - return this.header.getText(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/HomePage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/HomePage.java deleted file mode 100644 index 87317329d78..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/HomePage.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the Home page of the CAS sample application - * - * @author Rob Winch - * @author Josh Cummings - */ -public class HomePage extends Page<HomePage> { - private final Content content; - - public HomePage(WebDriver driver, String baseUrl) { - super(driver, baseUrl); - this.content = PageFactory.initElements(driver, Content.class); - } - - @Override - public HomePage assertAt() { - assertThat(this.content.header()).isEqualTo("Home Page"); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - public String header() { - return this.header.getText(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LocalLogoutPage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LocalLogoutPage.java deleted file mode 100644 index 7cac6aad53e..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LocalLogoutPage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; - -/** - * This represents the local logout page. This page is where the user is logged out of the CAS Sample application, but - * since the user is still logged into the CAS Server accessing a protected page within the CAS Sample application would result - * in SSO occurring again. To fully logout, the user should click the cas server logout url which logs out of the cas server and performs - * single logout on the other services. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class LocalLogoutPage extends Page<LocalLogoutPage> { - public LocalLogoutPage(WebDriver driver, String baseUrl) { - super(driver, baseUrl + "/cas-logout.jsp"); - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LoginPage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LoginPage.java deleted file mode 100644 index 436e39eb8be..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/LoginPage.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -/** - * The CAS login page. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class LoginPage extends Page<LoginPage> { - private final Content content; - - public LoginPage(WebDriver driver, String baseUrl) { - super(driver, baseUrl + "/login"); - this.content = PageFactory.initElements(driver, Content.class); - } - - public void login(String user) { - login(user, user); - } - - public void login(String user, String password) { - this.content.username(user).password(password).submit(); - } - - public static class Content { - private WebElement username; - private WebElement password; - @FindBy(css = "input[type=submit]") - private WebElement submit; - - public Content username(String username) { - this.username.sendKeys(username); - return this; - } - - public Content password(String password) { - this.password.sendKeys(password); - return this; - } - - public void submit() { - this.submit.click(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/Page.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/Page.java deleted file mode 100644 index 1e7d4e40c34..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/Page.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import java.util.function.Function; - -import org.openqa.selenium.WebDriver; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Josh Cummings - */ -public abstract class Page<T extends Page<T>> { - private final WebDriver driver; - private final String url; - - protected Page(WebDriver driver, String url) { - this.driver = driver; - this.url = url; - } - - public T assertAt() { - assertThat(this.driver.getCurrentUrl()).startsWith(this.url); - return (T) this; - } - - public T to() { - this.driver.get(this.url); - return (T) this; - } - - public T to(Function<String, String> urlPostProcessor) { - this.driver.get(urlPostProcessor.apply(this.url)); - return (T) this; - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.java deleted file mode 100644 index c570414f3ae..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/ProxyTicketSamplePage.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the proxy ticket sample page within the CAS Sample application. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class ProxyTicketSamplePage extends Page<ProxyTicketSamplePage> { - private final Content content; - - public ProxyTicketSamplePage(WebDriver driver, String baseUrl) { - super(driver, baseUrl + "/secure/ptSample"); - this.content = PageFactory.initElements(driver, Content.class); - } - - @Override - public ProxyTicketSamplePage assertAt() { - assertThat(this.content.getText()).isEqualTo("Secure Page using a Proxy Ticket"); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - public String getText() { - return this.header.getText(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/SecurePage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/SecurePage.java deleted file mode 100644 index 338ce55ccf3..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/SecurePage.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the secure page within the CAS Sample application. - * - * @author Rob Winch - * @author Josh Cummings - */ -public class SecurePage extends Page<SecurePage> { - private final Content content; - - public SecurePage(WebDriver driver, String baseUrl) { - super(driver, baseUrl + "/secure"); - this.content = PageFactory.initElements(driver, Content.class); - } - - @Override - public SecurePage assertAt() { - assertThat(this.content.header()).isEqualTo("Secure Page"); - return this; - } - - public SecurePage logout() { - this.content.logout(); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - WebElement logout; - - public String header() { - return this.header.getText(); - } - - public void logout() { - this.logout.click(); - } - } - - // had nav -} diff --git a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/UnauthorizedPage.java b/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/UnauthorizedPage.java deleted file mode 100644 index 90b967d5b67..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/java/org/springframework/security/samples/cas/pages/UnauthorizedPage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002-2011 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Represents the unauthorized page - * - * @author Josh Cummings - */ -public class UnauthorizedPage { - - private final Content content; - - public UnauthorizedPage(WebDriver driver) { - this.content = PageFactory.initElements(driver, Content.class); - } - - public UnauthorizedPage assertAt() { - assertThat(this.content.header()).contains("401 - Unauthorized"); - return this; - } - - public static class Content { - @FindBy(tagName="h1") - WebElement header; - - public String header() { - return this.header.getText(); - } - } -} diff --git a/samples/xml/cas/cassample/src/integration-test/resources/logback-test.xml b/samples/xml/cas/cassample/src/integration-test/resources/logback-test.xml deleted file mode 100644 index fc35a34be1e..00000000000 --- a/samples/xml/cas/cassample/src/integration-test/resources/logback-test.xml +++ /dev/null @@ -1,16 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - <logger name="org.apache.directory" level="ERROR"/> - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/cas/cassample/src/main/java/org/springframework/security/samples/cas/web/ProxyTicketSampleServlet.java b/samples/xml/cas/cassample/src/main/java/org/springframework/security/samples/cas/web/ProxyTicketSampleServlet.java deleted file mode 100644 index 6b66a0b8712..00000000000 --- a/samples/xml/cas/cassample/src/main/java/org/springframework/security/samples/cas/web/ProxyTicketSampleServlet.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2011-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.cas.web; - -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URLEncoder; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.jasig.cas.client.util.CommonUtils; -import org.springframework.security.cas.authentication.CasAuthenticationToken; - -/** - * <p> - * {@link ProxyTicketSampleServlet} demonstrates how to obtain a proxy ticket and then use - * it to make a remote call. To learn how proxy tickets work, see the <a - * href="https://wiki.jasig.org/display/CAS/Proxy+CAS+Walkthrough">Proxy CAS - * Walkthrough</a> - * </p> - * - * @author Rob Winch - */ -public final class ProxyTicketSampleServlet extends HttpServlet { - /** - * This is the URL that will be called and authenticate a proxy ticket. - */ - private String targetUrl; - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws IOException { - // NOTE: The CasAuthenticationToken can also be obtained using - // SecurityContextHolder.getContext().getAuthentication() - final CasAuthenticationToken token = (CasAuthenticationToken) request - .getUserPrincipal(); - // proxyTicket could be reused to make calls to the CAS service even if the - // target url differs - final String proxyTicket = token.getAssertion().getPrincipal() - .getProxyTicketFor(targetUrl); - - // Make a remote call to ourself. This is a bit silly, but it works well to - // demonstrate how to use proxy tickets. - final String serviceUrl = targetUrl + "?ticket=" - + URLEncoder.encode(proxyTicket, "UTF-8"); - String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8"); - - // modify the response and write it out to inform the user that it was obtained - // using a proxy ticket. - proxyResponse = proxyResponse.replaceFirst("Secure Page", - "Secure Page using a Proxy Ticket"); - proxyResponse = proxyResponse.replaceFirst("<p>", "<p>This page is rendered by " - + getClass().getSimpleName() - + " by making a remote call to the Secure Page using a proxy ticket (" - + proxyTicket + ") and inserts this message. "); - final PrintWriter writer = response.getWriter(); - writer.write(proxyResponse); - } - - /** - * Initialize the target URL. It allows for the host to change based upon the - * "cas.service.host" system property. If the property is not set, the default is - * "localhost:8443". - */ - @Override - public void init() throws ServletException { - super.init(); - String casServiceHost = System.getProperty("cas.service.host", "localhost:8443"); - targetUrl = "https://" + casServiceHost + "/cas-sample/secure/"; - } - - private static final long serialVersionUID = -7720161771819727775L; -} diff --git a/samples/xml/cas/cassample/src/main/webapp/401.jsp b/samples/xml/cas/cassample/src/main/webapp/401.jsp deleted file mode 100644 index db1a4a1f3e0..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/401.jsp +++ /dev/null @@ -1,8 +0,0 @@ -<html> -<head> -<title>401 - Unauthorized</title> -</head> -<body> -<h1>401 - Unauthorized</h1> -</body> -</html> diff --git a/samples/xml/cas/cassample/src/main/webapp/403.jsp b/samples/xml/cas/cassample/src/main/webapp/403.jsp deleted file mode 100644 index f893421dbd2..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/403.jsp +++ /dev/null @@ -1,8 +0,0 @@ -<html> -<head> -<title>403 - Access Denied</title> -</head> -<body> -<h1>403 - Access Denied</h1> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/cas/cassample/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/cas/cassample/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index bc8a2d85245..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<b:beans xmlns:b="http://www.springframework.org/schema/beans" - xmlns="http://www.springframework.org/schema/security" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" - xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> - - <http entry-point-ref="casEntryPoint"> - <intercept-url pattern="/" access="permitAll"/> - <intercept-url pattern="/index.jsp" access="permitAll"/> - <intercept-url pattern="/cas-logout.jsp" access="permitAll"/> - <intercept-url pattern="/casfailed.jsp" access="permitAll"/> - - <intercept-url pattern="/secure/extreme/**" - access="hasRole('ROLE_SUPERVISOR')" /> - <intercept-url pattern="/secure/**" access="hasRole('ROLE_USER')" /> - <intercept-url pattern="/**" access="hasRole('ROLE_USER')" /> - <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/> - <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/> - <custom-filter ref="casFilter" position="CAS_FILTER" /> - <logout logout-success-url="/cas-logout.jsp"/> - <csrf disabled="true"/> - </http> - - <authentication-manager alias="authManager"> - <authentication-provider ref="casAuthProvider" /> - </authentication-manager> - - <user-service id="userService"> - <user name="rod" password="rod" authorities="ROLE_SUPERVISOR,ROLE_USER" /> - <user name="dianne" password="dianne" authorities="ROLE_USER" /> - <user name="scott" password="scott" authorities="ROLE_USER" /> - </user-service> - - <!-- This filter handles a Single Logout Request from the CAS Server --> - <b:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/> - <!-- This filter redirects to the CAS Server to signal Single Logout should be performed --> - <b:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter" - p:filterProcessesUrl="/logout/cas"> - <b:constructor-arg value="https://${cas.server.host}/cas/logout"/> - <b:constructor-arg> - <b:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> - </b:constructor-arg> - </b:bean> - - <b:bean id="serviceProperties" - class="org.springframework.security.cas.ServiceProperties" - p:service="https://${cas.service.host}/cas-sample/login/cas" - p:authenticateAllArtifacts="true"/> - <b:bean id="casEntryPoint" - class="org.springframework.security.cas.web.CasAuthenticationEntryPoint" - p:serviceProperties-ref="serviceProperties" p:loginUrl="https://${cas.server.host}/cas/login" /> - <b:bean id="casFilter" - class="org.springframework.security.cas.web.CasAuthenticationFilter" - p:authenticationManager-ref="authManager" - p:serviceProperties-ref="serviceProperties" - p:proxyGrantingTicketStorage-ref="pgtStorage" - p:proxyReceptorUrl="/login/cas/proxyreceptor"> - <b:property name="authenticationDetailsSource"> - <b:bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"> - <b:constructor-arg ref="serviceProperties"/> - </b:bean> - </b:property> - <b:property name="authenticationFailureHandler"> - <b:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler" - p:defaultFailureUrl="/casfailed.jsp"/> - </b:property> - </b:bean> - <!-- - NOTE: In a real application you should not use an in memory implementation. You will also want - to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup() - --> - <b:bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/> - <b:bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider" - p:serviceProperties-ref="serviceProperties" - p:key="casAuthProviderKey"> - <b:property name="authenticationUserDetailsService"> - <b:bean - class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> - <b:constructor-arg ref="userService" /> - </b:bean> - </b:property> - <b:property name="ticketValidator"> - <b:bean - class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator" - p:acceptAnyProxy="true" - p:proxyCallbackUrl="https://${cas.service.host}/cas-sample/login/cas/proxyreceptor" - p:proxyGrantingTicketStorage-ref="pgtStorage"> - <b:constructor-arg value="https://${cas.server.host}/cas" /> - </b:bean> - </b:property> - <b:property name="statelessTicketCache"> - <b:bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache"> - <b:property name="cache"> - <b:bean id="ehcache" class="net.sf.ehcache.Cache" - init-method="initialise" - destroy-method="dispose"> - <b:constructor-arg value="casTickets"/> - <b:constructor-arg value="50"/> - <b:constructor-arg value="true"/> - <b:constructor-arg value="false"/> - <b:constructor-arg value="3600"/> - <b:constructor-arg value="900"/> - <b:property name="cacheManager"> - <b:bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/> - </b:property> - </b:bean> - </b:property> - </b:bean> - </b:property> - </b:bean> - - <!-- Configuration for the environment can be overriden by system properties --> - <context:property-placeholder system-properties-mode="OVERRIDE" properties-ref="environment"/> - <util:properties id="environment"> - <b:prop key="cas.service.host">localhost:8443</b:prop> - <b:prop key="cas.server.host">localhost:9443</b:prop> - </util:properties> -</b:beans> diff --git a/samples/xml/cas/cassample/src/main/webapp/WEB-INF/web.xml b/samples/xml/cas/cassample/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 0cf03709fc7..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Tutorial web application - - - --> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - <display-name>Spring Security CAS Demo Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>cas.root</param-value> - </context-param> - - <!-- - Include the character encoding Filter as per JASIG recommenation when doing Single Sign Out - https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out - --> - <filter> - <filter-name>characterEncodingFilter</filter-name> - <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> - <init-param> - <param-name>encoding</param-name> - <param-value>UTF-8</param-value> - </init-param> - </filter> - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>characterEncodingFilter</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - Included to support Single Logout. Note that the SingleSignOutFilter is included in the - springSecurityFilterChain. However, it could also be placed as the first filter-mapping - in the web.xml - --> - <listener> - <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> - </listener> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <servlet> - <servlet-name>ptSampleServlet</servlet-name> - <servlet-class>org.springframework.security.samples.cas.web.ProxyTicketSampleServlet</servlet-class> - </servlet> - - <servlet-mapping> - <servlet-name>ptSampleServlet</servlet-name> - <url-pattern>/secure/ptSample</url-pattern> - </servlet-mapping> - <error-page> - <error-code>401</error-code> - <location>/401.jsp</location> - </error-page> - <error-page> - <error-code>403</error-code> - <location>/403.jsp</location> - </error-page> -</web-app> diff --git a/samples/xml/cas/cassample/src/main/webapp/cas-logout.jsp b/samples/xml/cas/cassample/src/main/webapp/cas-logout.jsp deleted file mode 100644 index 5658e5d38bc..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/cas-logout.jsp +++ /dev/null @@ -1,15 +0,0 @@ - -<html> -<head> - <title>Single-sign out?</title> -</head> - -<body> -<h2>Do you want to log out of CAS?</h2> - -<p>You have logged out of this application, but may still have an active single-sign on session with CAS.</p> - -<p><a id="casLogout" href="logout/cas">Logout of CAS</a></p> - -</body> -</html> diff --git a/samples/xml/cas/cassample/src/main/webapp/casfailed.jsp b/samples/xml/cas/cassample/src/main/webapp/casfailed.jsp deleted file mode 100644 index 3dabc8c62bf..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/casfailed.jsp +++ /dev/null @@ -1,17 +0,0 @@ -<%@ page import="org.springframework.security.core.AuthenticationException" %> -<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %> - -<html> -<head> - <title>Login to CAS failed!</title> -</head> - -<body> -<h2>Login to CAS failed!</h2> - -<font color="red"> - Your CAS credentials were rejected.<br/> -</font> - -</body> -</html> diff --git a/samples/xml/cas/cassample/src/main/webapp/index.jsp b/samples/xml/cas/cassample/src/main/webapp/index.jsp deleted file mode 100644 index e7d6dc78028..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/index.jsp +++ /dev/null @@ -1,12 +0,0 @@ -<html> -<body> -<h1>Home Page</h1> -<p>Anyone can view this page.</p> - -<p>Your principal object is....: <%= request.getUserPrincipal() %></p> - -<p><a id="secure" href="secure/index.jsp">Secure page</a></p> -<p><a id="proxy" href="secure/ptSample">Proxy Ticket Sample page</a></p> -<p><a id="extremelySecure" href="secure/extreme/index.jsp">Extremely secure page</a></p> -</body> -</html> diff --git a/samples/xml/cas/cassample/src/main/webapp/secure/extreme/index.jsp b/samples/xml/cas/cassample/src/main/webapp/secure/extreme/index.jsp deleted file mode 100644 index 376cbe5fd9a..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/secure/extreme/index.jsp +++ /dev/null @@ -1,12 +0,0 @@ -<html> -<body> -<h1>VERY Secure Page</h1> -This is a protected page. You can only see me if you are a supervisor. - -<p><a id="home" href="../../">Home</a> -<p><a id="secure" href="../../secure/index.jsp">Secure page</a></p> -<p><a id="ptSample" href="../../secure/ptSample">Proxy Ticket Sample page</a></p> -<p><a id="logout" href="../../logout">Logout</a> - -</body> -</html> diff --git a/samples/xml/cas/cassample/src/main/webapp/secure/index.jsp b/samples/xml/cas/cassample/src/main/webapp/secure/index.jsp deleted file mode 100644 index ea4c7240d1d..00000000000 --- a/samples/xml/cas/cassample/src/main/webapp/secure/index.jsp +++ /dev/null @@ -1,15 +0,0 @@ -<html> -<body> -<h1>Secure Page</h1> -<p>This is a protected page. You can get to me if you've been remembered, -or if you've authenticated this session.</p> - -<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %> - <p>You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.</p> -<% } %> - -<p><a id="home" href="../">Home</a> -<p><a id="proxy" href="ptSample">Proxy Ticket Sample page</a></p> -<p><a id="logout" href="../logout">Logout</a> -</body> -</html> diff --git a/samples/xml/cas/casserver/spring-security-samples-xml-casserver.gradle b/samples/xml/cas/casserver/spring-security-samples-xml-casserver.gradle deleted file mode 100644 index ad7c2f96ffe..00000000000 --- a/samples/xml/cas/casserver/spring-security-samples-xml-casserver.gradle +++ /dev/null @@ -1,56 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -def keystore = "$rootDir/samples/certificates/server.jks" -def password = 'password' - -dependencies { - compile "org.jasig.cas:cas-server-webapp:4.0.7@war" - compile slf4jDependencies - runtime 'org.aspectj:aspectjrt' - runtime 'org.aspectj:aspectjtools' - runtime 'org.aspectj:aspectjweaver' -} - -project.tasks.withType(org.gradle.api.tasks.bundling.War) { war -> - war.duplicatesStrategy = DuplicatesStrategy.EXCLUDE - project.tasks.war.doFirst { - war.classpath = war.classpath.filter { !it.name.endsWith(".war") } - - war.project.configurations.runtime.each { - if (it.name.endsWith(".war")) { - def fileList = war.project.zipTree(it) - war.from fileList - } - } - } -} - -project.tasks.withType(org.akhikhl.gretty.StartBaseTask).all { task -> - task.doFirst { - def destinationDir = project.file("$buildDir/inplaceWebapp") - project.configurations.runtime.each { dependency -> - if (dependency.name.endsWith(".war")) { - def warTree = project.zipTree(dependency) - project.copy { - from warTree - into destinationDir - eachFile { - if (it.relativePath.getFile(destinationDir).exists()) { - it.exclude() - } - } - } - } - } - } -} - -gretty { - contextPath = '/cas' - httpsEnabled = true - httpPort = 9090 - httpsPort = 9443 - sslKeyStorePath = keystore - sslKeyStorePassword = password - jvmArgs = ["-Djavax.net.ssl.trustStore=${keystore}", "-Djavax.net.ssl.trustStorePassword=${password}"] -} diff --git a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/classes/log4j.xml b/samples/xml/cas/casserver/src/main/webapp/WEB-INF/classes/log4j.xml deleted file mode 100644 index bb38a82ef08..00000000000 --- a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/classes/log4j.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> - -<log4j:configuration debug="false" xmlns:log4j="http://jakarta.apache.org/log4j/"> - <appender name="console" class="org.apache.log4j.ConsoleAppender"> - <layout class="org.apache.log4j.PatternLayout"> - <param name="ConversionPattern" value="%d %p [%c] - <%m>%n"/> - </layout> - </appender> - <logger name="org.jasig" additivity="true"> - <level value="@logLevel@" /> - </logger> - <root> - <level value="ERROR"/> - <appender-ref ref="console"/> - </root> -</log4j:configuration> diff --git a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/deployerConfigContext.xml b/samples/xml/cas/casserver/src/main/webapp/WEB-INF/deployerConfigContext.xml deleted file mode 100644 index ffb983051b9..00000000000 --- a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/deployerConfigContext.xml +++ /dev/null @@ -1,192 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you under the Apache License, - Version 2.0 (the "License"); you may not use this file - except in compliance with the License. You may obtain a - copy of the License at the following location: - - https://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. - ---> -<!-- -| deployerConfigContext.xml centralizes into one file some of the declarative configuration that -| all CAS deployers will need to modify. -| -| This file declares some of the Spring-managed JavaBeans that make up a CAS deployment. -| The beans declared in this file are instantiated at context initialization time by the Spring -| ContextLoaderListener declared in web.xml. It finds this file because this -| file is among those declared in the context parameter "contextConfigLocation". -| -| By far the most common change you will need to make in this file is to change the last bean -| declaration to replace the default authentication handler with -| one implementing your approach for authenticating usernames and passwords. -+--> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:c="http://www.springframework.org/schema/c" - xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - - <!-- - | The authentication manager defines security policy for authentication by specifying at a minimum - | the authentication handlers that will be used to authenticate credential. While the AuthenticationManager - | interface supports plugging in another implementation, the default PolicyBasedAuthenticationManager should - | be sufficient in most cases. - +--> - <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager"> - <constructor-arg> - <map> - <!-- - | IMPORTANT - | Every handler requires a unique name. - | If more than one instance of the same handler class is configured, you must explicitly - | set its name to something other than its default name (typically the simple class name). - --> - <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /> - <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> - </map> - </constructor-arg> - - <!-- Uncomment the metadata populator to allow clearpass to capture and cache the password - This switch effectively will turn on clearpass. - <property name="authenticationMetaDataPopulators"> - <util:list> - <bean class="org.jasig.cas.extension.clearpass.CacheCredentialsMetaDataPopulator" - c:credentialCache-ref="encryptedMap" /> - </util:list> - </property> - --> - - <!-- - | Defines the security policy around authentication. Some alternative policies that ship with CAS: - | - | * NotPreventedAuthenticationPolicy - all credential must either pass or fail authentication - | * AllAuthenticationPolicy - all presented credential must be authenticated successfully - | * RequiredHandlerAuthenticationPolicy - specifies a handler that must authenticate its credential to pass - --> - <property name="authenticationPolicy"> - <bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" /> - </property> - </bean> - - <!-- Required for proxy ticket mechanism. --> - <bean id="proxyAuthenticationHandler" - class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" - p:httpClient-ref="httpClient" /> - - <!-- - | TODO: Replace this component with one suitable for your enviroment. - | - | This component provides authentication for the kind of credential used in your environment. In most cases - | credential is a username/password pair that lives in a system of record like an LDAP directory. - | The most common authentication handler beans: - | - | * org.jasig.cas.authentication.LdapAuthenticationHandler - | * org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler - | * org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler - | * org.jasig.cas.support.spnego.authentication.handler.support.JCIFSSpnegoAuthenticationHandler - --> - <bean id="primaryAuthenticationHandler" - class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler"> - <property name="users"> - <map> - <entry key="scott" value="scott"/> - <entry key="rod" value="rod"/> - <entry key="dianne" value="dianne"/> - </map> - </property> - </bean> - - <!-- Required for proxy ticket mechanism --> - <bean id="proxyPrincipalResolver" - class="org.jasig.cas.authentication.principal.BasicPrincipalResolver" /> - - <!-- - | Resolves a principal from a credential using an attribute repository that is configured to resolve - | against a deployer-specific store (e.g. LDAP). - --> - <bean id="primaryPrincipalResolver" - class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" > - <property name="attributeRepository" ref="attributeRepository" /> - </bean> - - <!-- - Bean that defines the attributes that a service may return. This example uses the Stub/Mock version. A real implementation - may go against a database or LDAP server. The id should remain "attributeRepository" though. - +--> - <bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao" - p:backingMap-ref="attrRepoBackingMap" /> - - <util:map id="attrRepoBackingMap"> - <entry key="uid" value="uid" /> - <entry key="eduPersonAffiliation" value="eduPersonAffiliation" /> - <entry key="groupMembership" value="groupMembership" /> - </util:map> - - <!-- - Sample, in-memory data store for the ServiceRegistry. A real implementation - would probably want to replace this with the JPA-backed ServiceRegistry DAO - The name of this bean should remain "serviceRegistryDao". - +--> - <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl" - p:registeredServices-ref="registeredServicesList" /> - - <util:list id="registeredServicesList"> - <bean class="org.jasig.cas.services.RegexRegisteredService" - p:id="0" p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols" - p:serviceId="^(https?|imaps?)://.*" p:evaluationOrder="10000001" - p:allowedToProxy="true"/> - <!-- - Use the following definition instead of the above to further restrict access - to services within your domain (including sub domains). - Note that example.com must be replaced with the domain you wish to permit. - This example also demonstrates the configuration of an attribute filter - that only allows for attributes whose length is 3. - --> - <!-- - <bean class="org.jasig.cas.services.RegexRegisteredService"> - <property name="id" value="1" /> - <property name="name" value="HTTP and IMAP on example.com" /> - <property name="description" value="Allows HTTP(S) and IMAP(S) protocols on example.com" /> - <property name="serviceId" value="^(https?|imaps?)://([A-Za-z0-9_-]+\.)*example\.com/.*" /> - <property name="evaluationOrder" value="0" /> - <property name="attributeFilter"> - <bean class="org.jasig.cas.services.support.RegisteredServiceRegexAttributeFilter" c:regex="^\w{3}$" /> - </property> - </bean> - --> - </util:list> - - <bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" /> - - <bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor" p:monitors-ref="monitorsList" /> - - <util:list id="monitorsList"> - <bean class="org.jasig.cas.monitor.MemoryMonitor" p:freeMemoryWarnThreshold="10" /> - <!-- - NOTE - The following ticket registries support SessionMonitor: - * DefaultTicketRegistry - * JpaTicketRegistry - Remove this monitor if you use an unsupported registry. - --> - <bean class="org.jasig.cas.monitor.SessionMonitor" - p:ticketRegistry-ref="ticketRegistry" - p:serviceTicketCountWarnThreshold="5000" - p:sessionCountWarnThreshold="100000" /> - </util:list> -</beans> diff --git a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml b/samples/xml/cas/casserver/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml deleted file mode 100644 index d07ef7be6ab..00000000000 --- a/samples/xml/cas/casserver/src/main/webapp/WEB-INF/spring-configuration/applicationContext.xml +++ /dev/null @@ -1,149 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Licensed to Jasig under one or more contributor license - agreements. See the NOTICE file distributed with this work - for additional information regarding copyright ownership. - Jasig licenses this file to you under the Apache License, - Version 2.0 (the "License"); you may not use this file - except in compliance with the License. You may obtain a - copy of the License at the following location: - - https://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. - ---> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:c="http://www.springframework.org/schema/c" - xmlns:util="http://www.springframework.org/schema/util" - xmlns:aop="http://www.springframework.org/schema/aop" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd - http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - <description> - This is the main Spring configuration file with some of the main "core" classes defined. You shouldn't really - modify this unless you - know what you're doing! - </description> - - <!-- - Including this aspectj-autoproxy element will cause spring to automatically - create proxies around any beans defined in this file that match the pointcuts - of any aspects defined in this file. - --> - <aop:aspectj-autoproxy/> - - <!-- - Declare the TimingAspect that we want to weave into the other beans - defined in this config file. - --> - <bean id="timingAspect" class="org.perf4j.log4j.aop.TimingAspect"/> - - - <!-- - Message source for this context, loaded from localized "messages_xx" files.] - - Disable the fallback mechanism to the system/JVM locale. By turning off this behavior, CAS - will be able to revert back to the default language bundle that is "messages.properties" - and will not rely on the JVM default locale which introduces the side effect of rendering - the UI in the JVM locale by default. - - Also, explicitly set the default encoding to be UTF-8 when parsing message bundles. - The default, if not set, is none which forces ISO-8859-1 of java.util.ResourceBundle. - --> - <bean id="messageSource" class="org.jasig.cas.web.view.CasReloadableMessageBundle" - p:basenames-ref="basenames" p:fallbackToSystemLocale="false" p:defaultEncoding="UTF-8" - p:cacheSeconds="180" p:useCodeAsDefaultMessage="true" /> - - <util:list id="basenames"> - <value>classpath:custom_messages</value> - <value>classpath:messages</value> - </util:list> - - <bean id="servicesManager" class="org.jasig.cas.services.DefaultServicesManagerImpl" - c:serviceRegistryDao-ref="serviceRegistryDao" /> - - <!-- - Job to periodically reload services from service registry. - This job is needed for a clustered CAS environment since service changes - in one CAS node are not known to the other until a reload. - --> - <bean id="serviceRegistryReloaderJobDetail" - class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" - p:targetObject-ref="servicesManager" - p:targetMethod="reload"/> - - <bean id="periodicServiceRegistryReloaderTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean" - p:jobDetail-ref="serviceRegistryReloaderJobDetail" - p:startDelay="${service.registry.quartz.reloader.startDelay:120000}" - p:repeatInterval="${service.registry.quartz.reloader.repeatInterval:120000}"/> - - - <bean id="noRedirectHttpClient" class="org.jasig.cas.util.SimpleHttpClient" parent="httpClient" - p:followRedirects="false" /> - - <bean id="persistentIdGenerator" - class="org.jasig.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator" - p:salt="casrocks"/> - - <bean id="logoutManager" class="org.jasig.cas.logout.LogoutManagerImpl" - c:servicesManager-ref="servicesManager" - c:httpClient-ref="noRedirectHttpClient" - c:logoutMessageBuilder-ref="logoutBuilder" - p:disableSingleSignOut="${slo.callbacks.disabled:false}" /> - - <bean id="logoutBuilder" class="org.jasig.cas.logout.SamlCompliantLogoutMessageCreator" /> - - <!-- CentralAuthenticationService --> - <bean id="centralAuthenticationService" class="org.jasig.cas.CentralAuthenticationServiceImpl"> - <constructor-arg index="0" ref="ticketRegistry"/> - <constructor-arg index="1"> - <null /> - </constructor-arg> - <constructor-arg index="2" ref="authenticationManager"/> - <constructor-arg index="3" ref="ticketGrantingTicketUniqueIdGenerator"/> - <constructor-arg index="4" ref="uniqueIdGeneratorsMap"/> - <constructor-arg index="5" ref="grantingTicketExpirationPolicy"/> - <constructor-arg index="6" ref="serviceTicketExpirationPolicy"/> - <constructor-arg index="7" ref="servicesManager"/> - <constructor-arg index="8" ref="logoutManager"/> - <property name="persistentIdGenerator" ref="persistentIdGenerator"/> - </bean> - - <bean id="proxy10Handler" class="org.jasig.cas.ticket.proxy.support.Cas10ProxyHandler"/> - - <bean id="proxy20Handler" class="org.jasig.cas.ticket.proxy.support.Cas20ProxyHandler" - p:httpClient-ref="httpClient" - p:uniqueTicketIdGenerator-ref="proxy20TicketUniqueIdGenerator"/> - - <!-- ADVISORS --> - <bean id="advisorAutoProxyCreator" - class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> - - <bean id="validationAnnotationBeanPostProcessor" class="org.jasig.cas.util.CustomBeanValidationPostProcessor" - p:afterInitialization="true" /> - - <!-- The scheduler bean wires up any triggers that define scheduled tasks --> - <bean id="scheduler" class="org.jasig.cas.util.AutowiringSchedulerFactoryBean"/> - - - <bean id="httpClient" class="org.jasig.cas.util.SimpleHttpClient" - p:readTimeout="5000" - p:connectionTimeout="5000"> - <property name="executorService"> - <bean class="org.springframework.core.task.support.ExecutorServiceAdapter"> - <constructor-arg> - <bean class="org.springframework.core.task.SyncTaskExecutor"/> - </constructor-arg> - </bean> - </property> - </bean> -</beans> \ No newline at end of file diff --git a/samples/xml/cas/spring-security-samples-xml-cas.gradle b/samples/xml/cas/spring-security-samples-xml-cas.gradle deleted file mode 100644 index a39d857e39b..00000000000 --- a/samples/xml/cas/spring-security-samples-xml-cas.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -def keystore = "$rootDir/samples/certificates/server.jks" -def password = 'password' - -gretty { - httpsEnabled = true - httpsPort = 8443 - sslKeyStorePath = keystore - sslKeyStorePassword = password - jvmArgs = ["-Djavax.net.ssl.trustStore=${keystore}", - "-Djavax.net.ssl.trustStorePassword=${password}", - "-Dcas.server.host=localhost:$httpsPort", - "-Dcas.service.host=localhost:$httpsPort"] -} - -farm { - webapp ':spring-security-samples-xml-casserver' - webapp ':spring-security-samples-xml-cassample' -} - -task cas(dependsOn: 'farmRunWar') { - group 'Gretty tasks' - description 'Run CAS Server and Sample' -} diff --git a/samples/xml/contacts/client/client.properties b/samples/xml/contacts/client/client.properties deleted file mode 100644 index 9f3feee9ea4..00000000000 --- a/samples/xml/contacts/client/client.properties +++ /dev/null @@ -1,8 +0,0 @@ -# Properties file with server URL settings for remote access. -# Applied by PropertyPlaceholderConfigurer from "clientContext.xml". -# - -serverName=localhost -httpPort=8080 -contextPath=/spring-security-sample-contacts-filter -rmiPort=1099 diff --git a/samples/xml/contacts/client/clientContext.xml b/samples/xml/contacts/client/clientContext.xml deleted file mode 100644 index f30d1728a5b..00000000000 --- a/samples/xml/contacts/client/clientContext.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "https://www.springframework.org/dtd/spring-beans.dtd"> - -<!-- - - Contacts web application - - Client application context - --> - -<beans> - - <!-- Resolves ${...} placeholders from client.properties --> - <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> - <property name="location"><value>client.properties</value></property> - </bean> - - <!-- Proxy for the RMI-exported ContactManager --> - <!-- COMMENTED OUT BY DEFAULT TO AVOID CONFLICTS WITH APPLICATION SERVERS - <bean id="rmiProxy" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> - <property name="serviceInterface"> - <value>sample.contact.ContactManager</value> - </property> - <property name="serviceUrl"> - <value>rmi://${serverName}:${rmiPort}/contactManager</value> - </property> - <property name="remoteInvocationFactory"> - <ref bean="remoteInvocationFactory"/> - </property> - </bean> - - <bean id="remoteInvocationFactory" class="org.springframework.security.ui.rmi.ContextPropagatingRemoteInvocationFactory"/> - --> - - <!-- Proxy for the HTTP-invoker-exported ContactManager --> - <!-- Spring's HTTP invoker uses Java serialization via HTTP --> - <bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> - <property name="serviceInterface"> - <value>sample.contact.ContactManager</value> - </property> - <property name="serviceUrl"> - <value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-httpinvoker</value> - </property> - <property name="httpInvokerRequestExecutor"> - <ref bean="httpInvokerRequestExecutor"/> - </property> - </bean> - - <!-- Automatically propagates ContextHolder-managed Authentication principal - and credentials to a HTTP invoker BASIC authentication header --> - <bean id="httpInvokerRequestExecutor" class="org.springframework.security.core.context.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor"/> - - <!-- Proxy for the Hessian-exported ContactManager - <bean id="hessianProxy" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> - <property name="serviceInterface"> - <value>sample.contact.ContactManager</value> - </property> - <property name="serviceUrl"> - <value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-hessian</value> - </property> - </bean> - --> - - <!-- Proxy for the Burlap-exported ContactManager - <bean id="burlapProxy" class="org.springframework.remoting.caucho.BurlapProxyFactoryBean"> - <property name="serviceInterface"> - <value>sample.contact.ContactManager</value> - </property> - <property name="serviceUrl"> - <value>http://${serverName}:${httpPort}${contextPath}/remoting/ContactManager-burlap</value> - </property> - </bean> - --> - -</beans> diff --git a/samples/xml/contacts/spring-security-samples-xml-contacts.gradle b/samples/xml/contacts/spring-security-samples-xml-contacts.gradle deleted file mode 100644 index 46f2eccde56..00000000000 --- a/samples/xml/contacts/spring-security-samples-xml-contacts.gradle +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-acl') - compile project(':spring-security-core') - compile slf4jDependencies - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-tx' - compile 'org.springframework:spring-web' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-taglibs') - runtime project(':spring-security-web') - runtime jstlDependencies - runtime 'ch.qos.logback:logback-classic' - runtime 'net.sf.ehcache:ehcache' - runtime 'org.hsqldb:hsqldb' - runtime 'org.slf4j:jcl-over-slf4j' - runtime 'org.springframework:spring-context-support' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/ContactsTests.java b/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/ContactsTests.java deleted file mode 100644 index db1e4e2e334..00000000000 --- a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/ContactsTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.ContactsPage; -import org.springframework.security.samples.pages.HomePage; - -/** - * @author Michael Simons - */ -public class ContactsTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSuccess() { - final HomePage homePage = HomePage.to(this.driver, this.port); - homePage.assertAt(); - } - - @Test - public void authenticatedUserCanAddContacts() { - final String name = "Rob Winch"; - final String email = "rob@example.com"; - - // @formatter:off - ContactsPage.accessManagePageWithUnauthenticatedUser(this.driver, this.port) - .sendsToLoginPage() - .username("rod") - .password("koala") - .submit() - .isAtContactsPage() - .addContact() - .name(name) - .email(email) - .submit() - .andHasContact(name, email) - .delete() - .andConfirmDeletion() - .isAtContactsPage() - .andConctactHasBeenRemoved(name, email); - // @formatter:on - } - - - @Test - public void authenticatedUserLogsOut() { - // @formatter:off - final HomePage homePage = ContactsPage.accessManagePageWithUnauthenticatedUser(this.driver, this.port) - .sendsToLoginPage() - .username("rod") - .password("koala") - .submit() - .isAtContactsPage() - .logout(); - // @formatter:on - homePage.assertAt(); - - ContactsPage.accessManagePageWithUnauthenticatedUser(this.driver, this.port) - .sendsToLoginPage(); - } -} diff --git a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/AddPage.java b/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/AddPage.java deleted file mode 100644 index 303e4263750..00000000000 --- a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/AddPage.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class AddPage { - - private final WebDriver webDriver; - - private final AddForm addForm; - - public AddPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.addForm = PageFactory.initElements(this.webDriver, AddForm.class); - } - - AddForm addForm() { - assertThat(this.webDriver.getTitle()).isEqualTo("Add New Contact"); - return this.addForm; - } - - public static class AddForm { - private WebDriver webDriver; - private WebElement name; - private WebElement email; - @FindBy(css = "input[type=submit]") - private WebElement submit; - - public AddForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public AddForm name(String name) { - this.name.sendKeys(name); - return this; - } - - public AddForm email(String email) { - this.email.sendKeys(email); - return this; - } - - public ContactsPage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, ContactsPage.class); - } - } -} diff --git a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/ContactsPage.java b/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/ContactsPage.java deleted file mode 100644 index a6b4ff5d6f5..00000000000 --- a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/ContactsPage.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; -import org.springframework.security.samples.pages.AddPage.AddForm; - -import java.util.List; -import java.util.function.Predicate; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The contacts / manage page. - * - * @author Michael Simons - */ -public class ContactsPage { - public static LoginPage accessManagePageWithUnauthenticatedUser(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/secure/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - private final WebDriver webDriver; - - @FindBy(linkText = "Add") - private WebElement a; - - @FindBy(css = "table tr") - private List<WebElement> contacts; - - @FindBy(xpath = "//input[@type='submit' and @value='Logoff']") - private WebElement logout; - - public ContactsPage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public ContactsPage isAtContactsPage() { - assertThat(this.webDriver.getTitle()).isEqualTo("Your Contacts"); - return this; - } - - public AddForm addContact() { - a.click(); - final AddPage addPage = PageFactory.initElements(this.webDriver, AddPage.class); - return addPage.addForm(); - } - - Predicate<WebElement> byEmail(final String val) { - return (e) -> e.findElements(By.xpath("td[position()=3 and normalize-space()='" + val + "']")).size() == 1; - } - - Predicate<WebElement> byName(final String val) { - return (e) -> e.findElements(By.xpath("td[position()=2 and normalize-space()='" + val + "']")).size() == 1; - } - - public DeleteContactLink andHasContact(final String name, final String email) { - return this.contacts.stream() - .filter(byEmail(email).and(byName(name))) - .map((e) -> e.findElement(By.cssSelector("td:nth-child(4) > a"))) - .findFirst() - .map((e) -> new DeleteContactLink(webDriver, e)) - .get(); - } - - public ContactsPage andConctactHasBeenRemoved(final String name, final String email) { - assertThat(this.contacts.stream() - .filter(byEmail(email).and(byName(name))) - .findAny()).isEmpty(); - return this; - } - - public HomePage logout() { - this.logout.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - - public static class DeleteContactLink { - - private final WebDriver webDriver; - - private final WebElement a; - - public DeleteContactLink(WebDriver webDriver, WebElement a) { - this.webDriver = webDriver; - this.a = a; - } - - public DeleteConfirmationPage delete() { - this.a.click(); - return PageFactory.initElements(this.webDriver, DeleteConfirmationPage.class); - } - } - - public static class DeleteConfirmationPage { - private final WebDriver webDriver; - - @FindBy(linkText = "Manage") - private WebElement a; - - - public DeleteConfirmationPage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public ContactsPage andConfirmDeletion() { - assertThat(this.webDriver.getTitle()).isEqualTo("Deletion completed"); - this.a.click(); - return PageFactory.initElements(this.webDriver, ContactsPage.class); - } - } -} diff --git a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 8c985c8d83e..00000000000 --- a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; -import org.springframework.security.samples.pages.LoginPage; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The home page. - * - * @author Michael Simons - */ -public class HomePage { - - public static HomePage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, HomePage.class); - } - - private final WebDriver webDriver; - - @FindBy(css = "p") - private WebElement message; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Contacts Security Demo"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andTheUserNameIsDisplayed() { - assertThat(message.getText()).isEqualTo("Hello user"); - return this; - } - } -} diff --git a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index d0f9efefbf3..00000000000 --- a/samples/xml/contacts/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The login page. - * - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginForm sendsToLoginPage() { - assertThat(this.webDriver.getTitle()).isEqualTo("Login"); - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "input[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public ContactsPage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, ContactsPage.class); - } - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/AddDeleteContactController.java b/samples/xml/contacts/src/main/java/sample/contact/AddDeleteContactController.java deleted file mode 100644 index 8c6ca3c256e..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/AddDeleteContactController.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.validation.BindingResult; -import org.springframework.validation.Validator; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.ModelAndView; - -/** - * - * @author Luke Taylor - * @since 3.0 - */ -@Controller -public class AddDeleteContactController { - @Autowired - private ContactManager contactManager; - private final Validator validator = new WebContactValidator(); - - /** - * Displays the "add contact" form. - */ - @RequestMapping(value = "/secure/add.htm", method = RequestMethod.GET) - public ModelAndView addContactDisplay() { - return new ModelAndView("add", "webContact", new WebContact()); - } - - @InitBinder - public void initBinder(WebDataBinder binder) { - System.out.println("A binder for object: " + binder.getObjectName()); - } - - /** - * Handles the submission of the contact form, creating a new instance if the username - * and email are valid. - */ - @RequestMapping(value = "/secure/add.htm", method = RequestMethod.POST) - public String addContact(WebContact form, BindingResult result) { - validator.validate(form, result); - - if (result.hasErrors()) { - return "add"; - } - - Contact contact = new Contact(form.getName(), form.getEmail()); - contactManager.create(contact); - - return "redirect:/secure/index.htm"; - } - - @RequestMapping(value = "/secure/del.htm", method = RequestMethod.GET) - public ModelAndView delContact(@RequestParam("contactId") int contactId) { - Contact contact = contactManager.getById((long) contactId); - contactManager.delete(contact); - - return new ModelAndView("deleted", "contact", contact); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/AddPermission.java b/samples/xml/contacts/src/main/java/sample/contact/AddPermission.java deleted file mode 100644 index 254841e42e1..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/AddPermission.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.security.acls.domain.BasePermission; - -/** - * Model object for add permission use case. - * - * @author Ben Alex - */ -public class AddPermission { - - - public Contact contact; - public Integer permission = BasePermission.READ.getMask(); - public String recipient; - - - public Contact getContact() { - return contact; - } - - public Integer getPermission() { - return permission; - } - - public String getRecipient() { - return recipient; - } - - public void setContact(Contact contact) { - this.contact = contact; - } - - public void setPermission(Integer permission) { - this.permission = permission; - } - - public void setRecipient(String recipient) { - this.recipient = recipient; - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/AddPermissionValidator.java b/samples/xml/contacts/src/main/java/sample/contact/AddPermissionValidator.java deleted file mode 100644 index f56b7647e2c..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/AddPermissionValidator.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.security.acls.domain.BasePermission; - -import org.springframework.validation.Errors; -import org.springframework.validation.ValidationUtils; -import org.springframework.validation.Validator; - -/** - * Validates {@link AddPermission}. - * - * @author Ben Alex - */ -public class AddPermissionValidator implements Validator { - - - @SuppressWarnings("unchecked") - public boolean supports(Class clazz) { - return clazz.equals(AddPermission.class); - } - - public void validate(Object obj, Errors errors) { - AddPermission addPermission = (AddPermission) obj; - - ValidationUtils.rejectIfEmptyOrWhitespace(errors, "permission", "err.permission", - "Permission is required. *"); - ValidationUtils.rejectIfEmptyOrWhitespace(errors, "recipient", "err.recipient", - "Recipient is required. *"); - - if (addPermission.getPermission() != null) { - int permission = addPermission.getPermission(); - - if ((permission != BasePermission.ADMINISTRATION.getMask()) - && (permission != BasePermission.READ.getMask()) - && (permission != BasePermission.DELETE.getMask())) { - errors.rejectValue("permission", "err.permission.invalid", - "The indicated permission is invalid. *"); - } - } - - if (addPermission.getRecipient() != null) { - if (addPermission.getRecipient().length() > 100) { - errors.rejectValue("recipient", "err.recipient.length", - "The recipient is too long (maximum 100 characters). *"); - } - } - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/AdminPermissionController.java b/samples/xml/contacts/src/main/java/sample/contact/AdminPermissionController.java deleted file mode 100644 index 7f048f16669..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/AdminPermissionController.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.contact; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.dao.DataAccessException; -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.DefaultPermissionFactory; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PermissionFactory; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.acls.model.Acl; -import org.springframework.security.acls.model.AclService; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.acls.model.Sid; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.validation.BindingResult; -import org.springframework.validation.Validator; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.SessionAttributes; -import org.springframework.web.servlet.ModelAndView; - -/** - * Web controller to handle <tt>Permission</tt> administration functions - adding and - * deleting permissions for contacts. - * - * @author Luke Taylor - * @since 3.0 - */ -@Controller -@SessionAttributes("addPermission") -public final class AdminPermissionController implements MessageSourceAware { - @Autowired - private AclService aclService; - @Autowired - private ContactManager contactManager; - private MessageSourceAccessor messages; - private final Validator addPermissionValidator = new AddPermissionValidator(); - private final PermissionFactory permissionFactory = new DefaultPermissionFactory(); - - /** - * Displays the permission admin page for a particular contact. - */ - @RequestMapping(value = "/secure/adminPermission.htm", method = RequestMethod.GET) - public ModelAndView displayAdminPage(@RequestParam("contactId") int contactId) { - Contact contact = contactManager.getById((long) contactId); - Acl acl = aclService.readAclById(new ObjectIdentityImpl(contact)); - - Map<String, Object> model = new HashMap<>(); - model.put("contact", contact); - model.put("acl", acl); - - return new ModelAndView("adminPermission", "model", model); - } - - /** - * Displays the "add permission" page for a contact. - */ - @RequestMapping(value = "/secure/addPermission.htm", method = RequestMethod.GET) - public ModelAndView displayAddPermissionPageForContact( - @RequestParam("contactId") long contactId) { - Contact contact = contactManager.getById(contactId); - - AddPermission addPermission = new AddPermission(); - addPermission.setContact(contact); - - Map<String, Object> model = new HashMap<>(); - model.put("addPermission", addPermission); - model.put("recipients", listRecipients()); - model.put("permissions", listPermissions()); - - return new ModelAndView("addPermission", model); - } - - @InitBinder("addPermission") - public void initBinder(WebDataBinder binder) { - binder.setAllowedFields("recipient", "permission"); - } - - /** - * Handles submission of the "add permission" form. - */ - @RequestMapping(value = "/secure/addPermission.htm", method = RequestMethod.POST) - public String addPermission(AddPermission addPermission, BindingResult result, - ModelMap model) { - addPermissionValidator.validate(addPermission, result); - - if (result.hasErrors()) { - model.put("recipients", listRecipients()); - model.put("permissions", listPermissions()); - - return "addPermission"; - } - - PrincipalSid sid = new PrincipalSid(addPermission.getRecipient()); - Permission permission = permissionFactory.buildFromMask(addPermission - .getPermission()); - - try { - contactManager.addPermission(addPermission.getContact(), sid, permission); - } - catch (DataAccessException existingPermission) { - existingPermission.printStackTrace(); - result.rejectValue("recipient", "err.recipientExistsForContact", - "Addition failure."); - - model.put("recipients", listRecipients()); - model.put("permissions", listPermissions()); - return "addPermission"; - } - - return "redirect:/secure/index.htm"; - } - - /** - * Deletes a permission - */ - @RequestMapping(value = "/secure/deletePermission.htm") - public ModelAndView deletePermission(@RequestParam("contactId") long contactId, - @RequestParam("sid") String sid, @RequestParam("permission") int mask) { - - Contact contact = contactManager.getById(contactId); - - Sid sidObject = new PrincipalSid(sid); - Permission permission = permissionFactory.buildFromMask(mask); - - contactManager.deletePermission(contact, sidObject, permission); - - Map<String, Object> model = new HashMap<>(); - model.put("contact", contact); - model.put("sid", sidObject); - model.put("permission", permission); - - return new ModelAndView("deletePermission", "model", model); - } - - private Map<Integer, String> listPermissions() { - Map<Integer, String> map = new LinkedHashMap<>(); - map.put(BasePermission.ADMINISTRATION.getMask(), - messages.getMessage("select.administer", "Administer")); - map.put(BasePermission.READ.getMask(), - messages.getMessage("select.read", "Read")); - map.put(BasePermission.DELETE.getMask(), - messages.getMessage("select.delete", "Delete")); - - return map; - } - - private Map<String, String> listRecipients() { - Map<String, String> map = new LinkedHashMap<>(); - map.put("", messages.getMessage("select.pleaseSelect", "-- please select --")); - - for (String recipient : contactManager.getAllRecipients()) { - map.put(recipient, recipient); - } - - return map; - } - - public void setMessageSource(MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/ClientApplication.java b/samples/xml/contacts/src/main/java/sample/contact/ClientApplication.java deleted file mode 100644 index 1203b77e1a0..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/ClientApplication.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.context.support.FileSystemXmlApplicationContext; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.StopWatch; - -/** - * Demonstrates accessing the {@link ContactManager} via remoting protocols. - * <p> - * Based on Spring's JPetStore sample, written by Juergen Hoeller. - * - * @author Ben Alex - */ -public class ClientApplication { - - - private final ListableBeanFactory beanFactory; - - - public ClientApplication(ListableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - - public void invokeContactManager(Authentication authentication, int nrOfCalls) { - StopWatch stopWatch = new StopWatch(nrOfCalls + " ContactManager call(s)"); - Map<String, ContactManager> contactServices = this.beanFactory.getBeansOfType( - ContactManager.class, true, true); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - for (Map.Entry<String, ContactManager> entry : contactServices.entrySet()) { - String beanName = entry.getKey(); - ContactManager remoteContactManager = entry.getValue(); - Object object = this.beanFactory.getBean("&" + beanName); - - try { - System.out.println("Trying to find setUsername(String) method on: " - + object.getClass().getName()); - - Method method = object.getClass().getMethod("setUsername", - new Class[] { String.class }); - System.out.println("Found; Trying to setUsername(String) to " - + authentication.getPrincipal()); - method.invoke(object, authentication.getPrincipal()); - } - catch (NoSuchMethodException ignored) { - System.out - .println("This client proxy factory does not have a setUsername(String) method"); - } - catch (IllegalAccessException | InvocationTargetException ignored) { - ignored.printStackTrace(); - } - - try { - System.out.println("Trying to find setPassword(String) method on: " - + object.getClass().getName()); - - Method method = object.getClass().getMethod("setPassword", - new Class[] { String.class }); - method.invoke(object, authentication.getCredentials()); - System.out.println("Found; Trying to setPassword(String) to " - + authentication.getCredentials()); - } - catch (NoSuchMethodException ignored) { - System.out - .println("This client proxy factory does not have a setPassword(String) method"); - } - catch (IllegalAccessException | InvocationTargetException ignored) { - } - - System.out.println("Calling ContactManager '" + beanName + "'"); - - stopWatch.start(beanName); - - List<Contact> contacts = null; - - for (int i = 0; i < nrOfCalls; i++) { - contacts = remoteContactManager.getAll(); - } - - stopWatch.stop(); - - if (contacts.size() != 0) { - for (Contact contact : contacts) { - System.out.println("Contact: " + contact); - } - } - else { - System.out.println("No contacts found which this user has permission to"); - } - - System.out.println(); - System.out.println(stopWatch.prettyPrint()); - } - - SecurityContextHolder.clearContext(); - } - - public static void main(String[] args) { - String username = System.getProperty("username", ""); - String password = System.getProperty("password", ""); - String nrOfCallsString = System.getProperty("nrOfCalls", ""); - - if ("".equals(username) || "".equals(password)) { - System.out - .println("You need to specify the user ID to use, the password to use, and optionally a number of calls " - + "using the username, password, and nrOfCalls system properties respectively. eg for user rod, " - + "use: -Dusername=rod -Dpassword=koala' for a single call per service and " - + "use: -Dusername=rod -Dpassword=koala -DnrOfCalls=10 for ten calls per service."); - System.exit(-1); - } - else { - int nrOfCalls = 1; - - if (!"".equals(nrOfCallsString)) { - nrOfCalls = Integer.parseInt(nrOfCallsString); - } - - ListableBeanFactory beanFactory = new FileSystemXmlApplicationContext( - "clientContext.xml"); - ClientApplication client = new ClientApplication(beanFactory); - - client.invokeContactManager(new UsernamePasswordAuthenticationToken(username, - password), nrOfCalls); - System.exit(0); - } - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/Contact.java b/samples/xml/contacts/src/main/java/sample/contact/Contact.java deleted file mode 100644 index ec719e856b0..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/Contact.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import java.io.Serializable; - -/** - * Represents a contact. - * - * @author Ben Alex - */ -public class Contact implements Serializable { - - - private Long id; - private String email; - private String name; - - - public Contact(String name, String email) { - this.name = name; - this.email = email; - } - - public Contact() { - } - - - /** - * @return Returns the email. - */ - public String getEmail() { - return email; - } - - /** - * @return Returns the id. - */ - public Long getId() { - return id; - } - - /** - * @return Returns the name. - */ - public String getName() { - return name; - } - - /** - * @param email The email to set. - */ - public void setEmail(String email) { - this.email = email; - } - - public void setId(Long id) { - this.id = id; - } - - /** - * @param name The name to set. - */ - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(super.toString() + ": "); - sb.append("Id: " + this.getId() + "; "); - sb.append("Name: " + this.getName() + "; "); - sb.append("Email: " + this.getEmail()); - - return sb.toString(); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/ContactDao.java b/samples/xml/contacts/src/main/java/sample/contact/ContactDao.java deleted file mode 100644 index 9f245582d2b..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/ContactDao.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import java.util.List; - -/** - * Provides access to the application's persistence layer. - * - * @author Ben Alex - */ -public interface ContactDao { - - - void create(Contact contact); - - void delete(Long contactId); - - List<Contact> findAll(); - - List<String> findAllPrincipals(); - - List<String> findAllRoles(); - - Contact getById(Long id); - - void update(Contact contact); -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/ContactDaoSpring.java b/samples/xml/contacts/src/main/java/sample/contact/ContactDaoSpring.java deleted file mode 100644 index f2b80295168..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/ContactDaoSpring.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -import org.springframework.jdbc.core.support.JdbcDaoSupport; - -/** - * Base implementation of {@link ContactDao} that uses Spring's JdbcTemplate. - * - * @author Ben Alex - * @author Luke Taylor - */ -public class ContactDaoSpring extends JdbcDaoSupport implements ContactDao { - - - public void create(final Contact contact) { - getJdbcTemplate().update("insert into contacts values (?, ?, ?)", - (ps) -> { - ps.setLong(1, contact.getId()); - ps.setString(2, contact.getName()); - ps.setString(3, contact.getEmail()); - }); - } - - public void delete(final Long contactId) { - getJdbcTemplate().update("delete from contacts where id = ?", - (ps) -> ps.setLong(1, contactId)); - } - - public void update(final Contact contact) { - getJdbcTemplate().update( - "update contacts set contact_name = ?, address = ? where id = ?", - (ps) -> { - ps.setString(1, contact.getName()); - ps.setString(2, contact.getEmail()); - ps.setLong(3, contact.getId()); - }); - } - - public List<Contact> findAll() { - return getJdbcTemplate().query( - "select id, contact_name, email from contacts order by id", - (rs, rowNum) -> mapContact(rs)); - } - - public List<String> findAllPrincipals() { - return getJdbcTemplate().queryForList( - "select username from users order by username", String.class); - } - - public List<String> findAllRoles() { - return getJdbcTemplate().queryForList( - "select distinct authority from authorities order by authority", - String.class); - } - - public Contact getById(Long id) { - List<Contact> list = getJdbcTemplate().query( - "select id, contact_name, email from contacts where id = ? order by id", - (rs, rowNum) -> mapContact(rs), id); - - if (list.size() == 0) { - return null; - } - else { - return list.get(0); - } - } - - private Contact mapContact(ResultSet rs) throws SQLException { - Contact contact = new Contact(); - contact.setId(rs.getLong("id")); - contact.setName(rs.getString("contact_name")); - contact.setEmail(rs.getString("email")); - - return contact; - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/ContactManager.java b/samples/xml/contacts/src/main/java/sample/contact/ContactManager.java deleted file mode 100644 index 5d3ff14a470..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/ContactManager.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.security.access.prepost.PostFilter; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.acls.model.Sid; - -import java.util.List; - -/** - * Interface for the application's services layer. - * - * @author Ben Alex - */ -public interface ContactManager { - - @PreAuthorize("hasPermission(#contact, admin)") - void addPermission(Contact contact, Sid recipient, Permission permission); - - @PreAuthorize("hasPermission(#contact, admin)") - void deletePermission(Contact contact, Sid recipient, Permission permission); - - @PreAuthorize("hasRole('ROLE_USER')") - void create(Contact contact); - - @PreAuthorize("hasPermission(#contact, 'delete') or hasPermission(#contact, admin)") - void delete(Contact contact); - - @PreAuthorize("hasRole('ROLE_USER')") - @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, admin)") - List<Contact> getAll(); - - @PreAuthorize("hasRole('ROLE_USER')") - List<String> getAllRecipients(); - - @PreAuthorize("hasPermission(#id, 'sample.contact.Contact', read) or " - + "hasPermission(#id, 'sample.contact.Contact', admin)") - Contact getById(Long id); - - Contact getRandomContact(); -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/ContactManagerBackend.java b/samples/xml/contacts/src/main/java/sample/contact/ContactManagerBackend.java deleted file mode 100644 index 3ba1a7dbf21..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/ContactManagerBackend.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.acls.model.AccessControlEntry; -import org.springframework.security.acls.model.MutableAcl; -import org.springframework.security.acls.model.MutableAclService; -import org.springframework.security.acls.model.NotFoundException; -import org.springframework.security.acls.model.ObjectIdentity; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.acls.model.Sid; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; - -import org.springframework.transaction.annotation.Transactional; - -import org.springframework.beans.factory.InitializingBean; - -import org.springframework.context.support.ApplicationObjectSupport; - -import org.springframework.util.Assert; - -import java.util.List; -import java.util.Random; - -/** - * Concrete implementation of {@link ContactManager}. - * - * @author Ben Alex - */ -@Transactional -public class ContactManagerBackend extends ApplicationObjectSupport implements - ContactManager, InitializingBean { - - - private ContactDao contactDao; - private MutableAclService mutableAclService; - private int counter = 1000; - - - public void afterPropertiesSet() { - Assert.notNull(contactDao, "contactDao required"); - Assert.notNull(mutableAclService, "mutableAclService required"); - } - - public void addPermission(Contact contact, Sid recipient, Permission permission) { - MutableAcl acl; - ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); - - try { - acl = (MutableAcl) mutableAclService.readAclById(oid); - } - catch (NotFoundException nfe) { - acl = mutableAclService.createAcl(oid); - } - - acl.insertAce(acl.getEntries().size(), permission, recipient, true); - mutableAclService.updateAcl(acl); - - logger.debug("Added permission " + permission + " for Sid " + recipient - + " contact " + contact); - } - - public void create(Contact contact) { - // Create the Contact itself - contact.setId((long) counter++); - contactDao.create(contact); - - // Grant the current principal administrative permission to the contact - addPermission(contact, new PrincipalSid(getUsername()), - BasePermission.ADMINISTRATION); - - if (logger.isDebugEnabled()) { - logger.debug("Created contact " + contact - + " and granted admin permission to recipient " + getUsername()); - } - } - - public void delete(Contact contact) { - contactDao.delete(contact.getId()); - - // Delete the ACL information as well - ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); - mutableAclService.deleteAcl(oid, false); - - if (logger.isDebugEnabled()) { - logger.debug("Deleted contact " + contact + " including ACL permissions"); - } - } - - public void deletePermission(Contact contact, Sid recipient, Permission permission) { - ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); - MutableAcl acl = (MutableAcl) mutableAclService.readAclById(oid); - - // Remove all permissions associated with this particular recipient (string - // equality to KISS) - List<AccessControlEntry> entries = acl.getEntries(); - - for (int i = 0; i < entries.size(); i++) { - if (entries.get(i).getSid().equals(recipient) - && entries.get(i).getPermission().equals(permission)) { - acl.deleteAce(i); - } - } - - mutableAclService.updateAcl(acl); - - if (logger.isDebugEnabled()) { - logger.debug("Deleted contact " + contact + " ACL permissions for recipient " - + recipient); - } - } - - @Transactional(readOnly = true) - public List<Contact> getAll() { - logger.debug("Returning all contacts"); - - return contactDao.findAll(); - } - - @Transactional(readOnly = true) - public List<String> getAllRecipients() { - logger.debug("Returning all recipients"); - - return contactDao.findAllPrincipals(); - } - - @Transactional(readOnly = true) - public Contact getById(Long id) { - if (logger.isDebugEnabled()) { - logger.debug("Returning contact with id: " + id); - } - - return contactDao.getById(id); - } - - /** - * This is a public method. - */ - @Transactional(readOnly = true) - public Contact getRandomContact() { - logger.debug("Returning random contact"); - - Random rnd = new Random(); - List<Contact> contacts = contactDao.findAll(); - int getNumber = rnd.nextInt(contacts.size()); - - return contacts.get(getNumber); - } - - protected String getUsername() { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - - if (auth.getPrincipal() instanceof UserDetails) { - return ((UserDetails) auth.getPrincipal()).getUsername(); - } - else { - return auth.getPrincipal().toString(); - } - } - - public void setContactDao(ContactDao contactDao) { - this.contactDao = contactDao; - } - - public void setMutableAclService(MutableAclService mutableAclService) { - this.mutableAclService = mutableAclService; - } - - public void update(Contact contact) { - contactDao.update(contact); - - logger.debug("Updated contact " + contact); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java b/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java deleted file mode 100644 index 23154269090..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/DataSourcePopulator.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import java.util.Random; - -import javax.sql.DataSource; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.acls.domain.AclImpl; -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.acls.model.MutableAcl; -import org.springframework.security.acls.model.MutableAclService; -import org.springframework.security.acls.model.ObjectIdentity; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.Assert; - -/** - * Populates the Contacts in-memory database with contact and ACL information. - * - * @author Ben Alex - */ -public class DataSourcePopulator implements InitializingBean { - - - JdbcTemplate template; - private MutableAclService mutableAclService; - final Random rnd = new Random(); - TransactionTemplate tt; - final String[] firstNames = { "Bob", "Mary", "James", "Jane", "Kristy", "Kirsty", - "Kate", "Jeni", "Angela", "Melanie", "Kent", "William", "Geoff", "Jeff", - "Adrian", "Amanda", "Lisa", "Elizabeth", "Prue", "Richard", "Darin", - "Phillip", "Michael", "Belinda", "Samantha", "Brian", "Greg", "Matthew" }; - final String[] lastNames = { "Smith", "Williams", "Jackson", "Rictor", "Nelson", - "Fitzgerald", "McAlpine", "Sutherland", "Abbott", "Hall", "Edwards", "Gates", - "Black", "Brown", "Gray", "Marwell", "Booch", "Johnson", "McTaggart", - "Parklin", "Findlay", "Robinson", "Giugni", "Lang", "Chi", "Carmichael" }; - private int createEntities = 50; - - - public void afterPropertiesSet() { - Assert.notNull(mutableAclService, "mutableAclService required"); - Assert.notNull(template, "dataSource required"); - Assert.notNull(tt, "platformTransactionManager required"); - - // Set a user account that will initially own all the created data - Authentication authRequest = new UsernamePasswordAuthenticationToken("rod", - "koala", AuthorityUtils.createAuthorityList("ROLE_IGNORED")); - SecurityContextHolder.getContext().setAuthentication(authRequest); - - try { - template.execute("DROP TABLE CONTACTS"); - template.execute("DROP TABLE AUTHORITIES"); - template.execute("DROP TABLE USERS"); - template.execute("DROP TABLE ACL_ENTRY"); - template.execute("DROP TABLE ACL_OBJECT_IDENTITY"); - template.execute("DROP TABLE ACL_CLASS"); - template.execute("DROP TABLE ACL_SID"); - } - catch (Exception e) { - System.out.println("Failed to drop tables: " + e.getMessage()); - } - - template.execute("CREATE TABLE ACL_SID(" - + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," - + "PRINCIPAL BOOLEAN NOT NULL," + "SID VARCHAR_IGNORECASE(100) NOT NULL," - + "CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));"); - template.execute("CREATE TABLE ACL_CLASS(" - + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," - + "CLASS VARCHAR_IGNORECASE(100) NOT NULL," - + "CLASS_ID_TYPE VARCHAR_IGNORECASE(100)," - + "CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));"); - template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(" - + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," - + "OBJECT_ID_CLASS BIGINT NOT NULL," - + "OBJECT_ID_IDENTITY VARCHAR_IGNORECASE(36) NOT NULL," - + "PARENT_OBJECT BIGINT," - + "OWNER_SID BIGINT," - + "ENTRIES_INHERITING BOOLEAN NOT NULL," - + "CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY)," - + "CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID)," - + "CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID)," - + "CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));"); - template.execute("CREATE TABLE ACL_ENTRY(" - + "ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," - + "ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL," - + "MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL," - + "AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER)," - + "CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID)," - + "CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));"); - - template.execute("CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL);"); - template.execute("CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME));"); - template.execute("CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY);"); - - template.execute("CREATE TABLE CONTACTS(ID BIGINT NOT NULL PRIMARY KEY, CONTACT_NAME VARCHAR_IGNORECASE(50) NOT NULL, EMAIL VARCHAR_IGNORECASE(50) NOT NULL)"); - - /* - * Passwords encoded using MD5, NOT in Base64 format, with null as salt Encoded - * password for rod is "koala" Encoded password for dianne is "emu" Encoded - * password for scott is "wombat" Encoded password for peter is "opal" (but user - * is disabled) Encoded password for bill is "wombat" Encoded password for bob is - * "wombat" Encoded password for jane is "wombat" - */ - template.execute("INSERT INTO USERS VALUES('rod','$2a$10$75pBjapg4Nl8Pzd.3JRnUe7PDJmk9qBGwNEJDAlA3V.dEJxcDKn5O',TRUE);"); - template.execute("INSERT INTO USERS VALUES('dianne','$2a$04$bCMEyxrdF/7sgfUiUJ6Ose2vh9DAMaVBldS1Bw2fhi1jgutZrr9zm',TRUE);"); - template.execute("INSERT INTO USERS VALUES('scott','$2a$06$eChwvzAu3TSexnC3ynw4LOSw1qiEbtNItNeYv5uI40w1i3paoSfLu',TRUE);"); - template.execute("INSERT INTO USERS VALUES('peter','$2a$04$8.H8bCMROLF4CIgd7IpeQ.tcBXLP5w8iplO0n.kCIkISwrIgX28Ii',FALSE);"); - template.execute("INSERT INTO USERS VALUES('bill','$2a$04$8.H8bCMROLF4CIgd7IpeQ.3khQlPVNWbp8kzSQqidQHGFurim7P8O',TRUE);"); - template.execute("INSERT INTO USERS VALUES('bob','$2a$06$zMgxlMf01SfYNcdx7n4NpeFlAGU8apCETz/i2C7VlYWu6IcNyn4Ay',TRUE);"); - template.execute("INSERT INTO USERS VALUES('jane','$2a$05$ZrdS7yMhCZ1J.AAidXZhCOxdjD8LO/dhlv4FJzkXA6xh9gdEbBT/u',TRUE);"); - template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR');"); - template.execute("INSERT INTO AUTHORITIES VALUES('dianne','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('scott','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('peter','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('bill','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('bob','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('jane','ROLE_USER');"); - - template.execute("INSERT INTO contacts VALUES (1, 'John Smith', 'john@somewhere.com');"); - template.execute("INSERT INTO contacts VALUES (2, 'Michael Citizen', 'michael@xyz.com');"); - template.execute("INSERT INTO contacts VALUES (3, 'Joe Bloggs', 'joe@demo.com');"); - template.execute("INSERT INTO contacts VALUES (4, 'Karen Sutherland', 'karen@sutherland.com');"); - template.execute("INSERT INTO contacts VALUES (5, 'Mitchell Howard', 'mitchell@abcdef.com');"); - template.execute("INSERT INTO contacts VALUES (6, 'Rose Costas', 'rose@xyz.com');"); - template.execute("INSERT INTO contacts VALUES (7, 'Amanda Smith', 'amanda@abcdef.com');"); - template.execute("INSERT INTO contacts VALUES (8, 'Cindy Smith', 'cindy@smith.com');"); - template.execute("INSERT INTO contacts VALUES (9, 'Jonathan Citizen', 'jonathan@xyz.com');"); - - for (int i = 10; i < createEntities; i++) { - String[] person = selectPerson(); - template.execute("INSERT INTO contacts VALUES (" + i + ", '" + person[2] - + "', '" + person[0].toLowerCase() + "@" + person[1].toLowerCase() - + ".com');"); - } - - // Create acl_object_identity rows (and also acl_class rows as needed - for (int i = 1; i < createEntities; i++) { - final ObjectIdentity objectIdentity = new ObjectIdentityImpl(Contact.class, - (long) i); - tt.execute((arg0) -> { - mutableAclService.createAcl(objectIdentity); - - return null; - }); - } - - // Now grant some permissions - grantPermissions(1, "rod", BasePermission.ADMINISTRATION); - grantPermissions(2, "rod", BasePermission.READ); - grantPermissions(3, "rod", BasePermission.READ); - grantPermissions(3, "rod", BasePermission.WRITE); - grantPermissions(3, "rod", BasePermission.DELETE); - grantPermissions(4, "rod", BasePermission.ADMINISTRATION); - grantPermissions(4, "dianne", BasePermission.ADMINISTRATION); - grantPermissions(4, "scott", BasePermission.READ); - grantPermissions(5, "dianne", BasePermission.ADMINISTRATION); - grantPermissions(5, "dianne", BasePermission.READ); - grantPermissions(6, "dianne", BasePermission.READ); - grantPermissions(6, "dianne", BasePermission.WRITE); - grantPermissions(6, "dianne", BasePermission.DELETE); - grantPermissions(6, "scott", BasePermission.READ); - grantPermissions(7, "scott", BasePermission.ADMINISTRATION); - grantPermissions(8, "dianne", BasePermission.ADMINISTRATION); - grantPermissions(8, "dianne", BasePermission.READ); - grantPermissions(8, "scott", BasePermission.READ); - grantPermissions(9, "scott", BasePermission.ADMINISTRATION); - grantPermissions(9, "scott", BasePermission.READ); - grantPermissions(9, "scott", BasePermission.WRITE); - grantPermissions(9, "scott", BasePermission.DELETE); - - // Now expressly change the owner of the first ten contacts - // We have to do this last, because "rod" owns all of them (doing it sooner would - // prevent ACL updates) - // Note that ownership has no impact on permissions - they're separate (ownership - // only allows ACl editing) - changeOwner(5, "dianne"); - changeOwner(6, "dianne"); - changeOwner(7, "scott"); - changeOwner(8, "dianne"); - changeOwner(9, "scott"); - - String[] users = { "bill", "bob", "jane" }; // don't want to mess around with - // consistent sample data - Permission[] permissions = { BasePermission.ADMINISTRATION, BasePermission.READ, - BasePermission.DELETE }; - - for (int i = 10; i < createEntities; i++) { - String user = users[rnd.nextInt(users.length)]; - Permission permission = permissions[rnd.nextInt(permissions.length)]; - grantPermissions(i, user, permission); - - String user2 = users[rnd.nextInt(users.length)]; - Permission permission2 = permissions[rnd.nextInt(permissions.length)]; - grantPermissions(i, user2, permission2); - } - - SecurityContextHolder.clearContext(); - } - - private void changeOwner(int contactNumber, String newOwnerUsername) { - AclImpl acl = (AclImpl) mutableAclService.readAclById(new ObjectIdentityImpl( - Contact.class, (long) contactNumber)); - acl.setOwner(new PrincipalSid(newOwnerUsername)); - updateAclInTransaction(acl); - } - - public int getCreateEntities() { - return createEntities; - } - - private void grantPermissions(int contactNumber, String recipientUsername, - Permission permission) { - AclImpl acl = (AclImpl) mutableAclService.readAclById(new ObjectIdentityImpl( - Contact.class, (long) contactNumber)); - acl.insertAce(acl.getEntries().size(), permission, new PrincipalSid( - recipientUsername), true); - updateAclInTransaction(acl); - } - - private String[] selectPerson() { - String firstName = firstNames[rnd.nextInt(firstNames.length)]; - String lastName = lastNames[rnd.nextInt(lastNames.length)]; - - return new String[] { firstName, lastName, firstName + " " + lastName }; - } - - public void setCreateEntities(int createEntities) { - this.createEntities = createEntities; - } - - public void setDataSource(DataSource dataSource) { - this.template = new JdbcTemplate(dataSource); - } - - public void setMutableAclService(MutableAclService mutableAclService) { - this.mutableAclService = mutableAclService; - } - - public void setPlatformTransactionManager( - PlatformTransactionManager platformTransactionManager) { - this.tt = new TransactionTemplate(platformTransactionManager); - } - - private void updateAclInTransaction(final MutableAcl acl) { - tt.execute((arg0) -> { - mutableAclService.updateAcl(acl); - - return null; - }); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/IndexController.java b/samples/xml/contacts/src/main/java/sample/contact/IndexController.java deleted file mode 100644 index b46b4e9810e..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/IndexController.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.contact; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.PermissionEvaluator; -import org.springframework.security.acls.AclPermissionEvaluator; -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.ModelAndView; - -/** - * Controller which handles simple, single request use cases such as index pages and - * contact deletion. - * - * @author Luke Taylor - * @since 3.0 - */ -@Controller -public class IndexController { - private final static Permission[] HAS_DELETE = new Permission[] { - BasePermission.DELETE, BasePermission.ADMINISTRATION }; - private final static Permission[] HAS_ADMIN = new Permission[] { BasePermission.ADMINISTRATION }; - - - @Autowired - private ContactManager contactManager; - @Autowired - private PermissionEvaluator permissionEvaluator; - - - /** - * The public index page, used for unauthenticated users. - */ - @RequestMapping(value = "/hello.htm", method = RequestMethod.GET) - public ModelAndView displayPublicIndex() { - Contact rnd = contactManager.getRandomContact(); - - return new ModelAndView("hello", "contact", rnd); - } - - /** - * The index page for an authenticated user. - * <p> - * This controller displays a list of all the contacts for which the current user has - * read or admin permissions. It makes a call to {@link ContactManager#getAll()} which - * automatically filters the returned list using Spring Security's ACL mechanism (see - * the expression annotations on this interface for the details). - * <p> - * In addition to rendering the list of contacts, the view will also include a "Del" - * or "Admin" link beside the contact, depending on whether the user has the - * corresponding permissions (admin permission is assumed to imply delete here). This - * information is stored in the model using the injected {@link PermissionEvaluator} - * instance. The implementation should be an instance of - * {@link AclPermissionEvaluator} or one which is compatible with Spring Security's - * ACL module. - */ - @RequestMapping(value = "/secure/index.htm", method = RequestMethod.GET) - public ModelAndView displayUserContacts() { - List<Contact> myContactsList = contactManager.getAll(); - Map<Contact, Boolean> hasDelete = new HashMap<>( - myContactsList.size()); - Map<Contact, Boolean> hasAdmin = new HashMap<>( - myContactsList.size()); - - Authentication user = SecurityContextHolder.getContext().getAuthentication(); - - for (Contact contact : myContactsList) { - hasDelete.put(contact, permissionEvaluator.hasPermission( - user, contact, HAS_DELETE)); - hasAdmin.put(contact, permissionEvaluator.hasPermission(user, - contact, HAS_ADMIN)); - } - - Map<String, Object> model = new HashMap<>(); - model.put("contacts", myContactsList); - model.put("hasDeletePermission", hasDelete); - model.put("hasAdminPermission", hasAdmin); - - return new ModelAndView("index", "model", model); - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/WebContact.java b/samples/xml/contacts/src/main/java/sample/contact/WebContact.java deleted file mode 100644 index c4ae49df672..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/WebContact.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -/** - * An object that represents user-editable sections of a {@link Contact}. - * - * @author Ben Alex - */ -public class WebContact { - - - private String email; - private String name; - - - public String getEmail() { - return email; - } - - public String getName() { - return name; - } - - public void setEmail(String email) { - this.email = email; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/samples/xml/contacts/src/main/java/sample/contact/WebContactValidator.java b/samples/xml/contacts/src/main/java/sample/contact/WebContactValidator.java deleted file mode 100644 index 8d0b754489e..00000000000 --- a/samples/xml/contacts/src/main/java/sample/contact/WebContactValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; - -/** - * Validates {@link WebContact}. - * - * @author Ben Alex - */ -public class WebContactValidator implements Validator { - - - @SuppressWarnings("unchecked") - public boolean supports(Class clazz) { - return clazz.equals(WebContact.class); - } - - public void validate(Object obj, Errors errors) { - WebContact wc = (WebContact) obj; - - if ((wc.getName() == null) || (wc.getName().length() < 3) - || (wc.getName().length() > 50)) { - errors.rejectValue("name", "err.name", "Name 3-50 characters is required. *"); - } - - if ((wc.getEmail() == null) || (wc.getEmail().length() < 3) - || (wc.getEmail().length() > 50)) { - errors.rejectValue("email", "err.email", - "Email 3-50 characters is required. *"); - } - } -} diff --git a/samples/xml/contacts/src/main/resources/applicationContext-common-authorization.xml b/samples/xml/contacts/src/main/resources/applicationContext-common-authorization.xml deleted file mode 100644 index 397b39ec0af..00000000000 --- a/samples/xml/contacts/src/main/resources/applicationContext-common-authorization.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - -<!-- - - Application context containing the ACL beans. - - - --> - - <!-- ========= ACL SERVICE DEFINITIONS ========= --> - - <bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache"> - <constructor-arg> - <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean"> - <property name="cacheManager"> - <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/> - </property> - <property name="cacheName" value="aclCache"/> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy"> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/> - </constructor-arg> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"> - <constructor-arg> - <list> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ACL_ADMIN"/> - </bean> - </list> - </constructor-arg> - </bean> - </constructor-arg> - </bean> - - <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="aclCache"/> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"> - <constructor-arg> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ADMINISTRATOR"/> - </bean> - </constructor-arg> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/> - </constructor-arg> - </bean> - - <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="lookupStrategy"/> - <constructor-arg ref="aclCache"/> - </bean> - -</beans> diff --git a/samples/xml/contacts/src/main/resources/applicationContext-common-business.xml b/samples/xml/contacts/src/main/resources/applicationContext-common-business.xml deleted file mode 100644 index 28ebc0a8001..00000000000 --- a/samples/xml/contacts/src/main/resources/applicationContext-common-business.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - Application context containing business beans. - - - - Used by all artifacts. - - - --> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:tx="http://www.springframework.org/schema/tx" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"> - - <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> - <property name="basename" value="classpath:org/springframework/security/messages"/> - </bean> - - <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> - <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> - <property name="url" value="jdbc:hsqldb:mem:test"/> - <!-- <value>jdbc:hsqldb:hsql://localhost/acl</value> --> - <property name="username" value="sa"/> - <property name="password" value=""/> - </bean> - - <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <tx:annotation-driven transaction-manager="transactionManager" /> - - <bean id="dataSourcePopulator" class="sample.contact.DataSourcePopulator"> - <property name="dataSource" ref="dataSource"/> - <property name="mutableAclService" ref="aclService"/> - <property name="platformTransactionManager" ref="transactionManager"/> - </bean> - - <bean id="contactManager" class="sample.contact.ContactManagerBackend"> - <property name="contactDao"> - <bean class="sample.contact.ContactDaoSpring"> - <property name="dataSource" ref="dataSource"/> - </bean> - </property> - <property name="mutableAclService" ref="aclService"/> - </bean> - -</beans> diff --git a/samples/xml/contacts/src/main/resources/applicationContext-security.xml b/samples/xml/contacts/src/main/resources/applicationContext-security.xml deleted file mode 100644 index 40ff54396be..00000000000 --- a/samples/xml/contacts/src/main/resources/applicationContext-security.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Application context containing authentication, channel - - security and web URI beans. - - - - Only used by "filter" artifact. - - - --> - -<b:beans xmlns="http://www.springframework.org/schema/security" - xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <global-method-security pre-post-annotations="enabled"> - <expression-handler ref="expressionHandler"/> - </global-method-security> - - <http realm="Contacts Realm" use-expressions="false"> - <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY"/> - <intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/> - <intercept-url pattern="/hello.htm" access="IS_AUTHENTICATED_ANONYMOUSLY"/> - <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/> - <intercept-url pattern="/switchuser.jsp" access="ROLE_SUPERVISOR"/> - <intercept-url pattern="/login/impersonate" access="ROLE_SUPERVISOR"/> - <intercept-url pattern="/**" access="ROLE_USER"/> - - <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/> - <http-basic/> - <logout logout-success-url="/index.jsp"/> - <remember-me /> - <headers/> - <csrf/> - <custom-filter ref="switchUserProcessingFilter" position="SWITCH_USER_FILTER"/> - </http> - - <b:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> - - <authentication-manager> - <authentication-provider> - <password-encoder ref="encoder"/> - <jdbc-user-service data-source-ref="dataSource"/> - </authentication-provider> - </authentication-manager> - - <!-- Automatically receives AuthenticationEvent messages --> - <b:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/> - - <!-- Filter used to switch the user context. Note: the switch and exit url must be secured - based on the role granted the ability to 'switch' to another user --> - <!-- In this example 'rod' has ROLE_SUPERVISOR that can switch to regular ROLE_USER(s) --> - <b:bean id="switchUserProcessingFilter" class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter" autowire="byType"> - <b:property name="targetUrl" value="/secure/index.htm"/> - </b:bean> - - <b:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> - <b:property name="permissionEvaluator" ref="permissionEvaluator"/> - <b:property name="permissionCacheOptimizer"> - <b:bean class="org.springframework.security.acls.AclPermissionCacheOptimizer"> - <b:constructor-arg ref="aclService"/> - </b:bean> - </b:property> - </b:bean> - - <b:bean id="permissionEvaluator" class="org.springframework.security.acls.AclPermissionEvaluator"> - <b:constructor-arg ref="aclService"/> - </b:bean> - -</b:beans> diff --git a/samples/xml/contacts/src/main/resources/logback.xml b/samples/xml/contacts/src/main/resources/logback.xml deleted file mode 100644 index b0b65dbb153..00000000000 --- a/samples/xml/contacts/src/main/resources/logback.xml +++ /dev/null @@ -1,14 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <!-- <logger name="org.springframework.security" level="DEBUG"/> --> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/contacts/src/main/resources/messages.properties b/samples/xml/contacts/src/main/resources/messages.properties deleted file mode 100644 index 058905c6c14..00000000000 --- a/samples/xml/contacts/src/main/resources/messages.properties +++ /dev/null @@ -1,6 +0,0 @@ -err.name=Name 3-50 characters is required. -err.email=Email 3-50 characters is required. -err.permission=Permission is required. -err.recipient=Recipient is required. -err.permission.invalid=The indicated permission is invalid. -err.recipient.length=The recipient is too long (maximum 100 characters). \ No newline at end of file diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/contacts-servlet.xml b/samples/xml/contacts/src/main/webapp/WEB-INF/contacts-servlet.xml deleted file mode 100644 index 1ec14d631e5..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/contacts-servlet.xml +++ /dev/null @@ -1,26 +0,0 @@ -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" - xmlns:mvc="http://www.springframework.org/schema/mvc" - xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> - - <!-- ========================== WEB DEFINITIONS ======================= --> - - <context:component-scan base-package="sample.contact"/> - <context:annotation-config /> - - <mvc:annotation-driven/> - <mvc:view-controller path="/frames.htm" view-name="/frames"/> - - <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> - <property name="basename" value="messages"/> - </bean> - - <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> - <property name="prefix" value="/WEB-INF/jsp/"/> - <property name="suffix" value=".jsp"/> - </bean> - -</beans> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/add.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/add.jsp deleted file mode 100644 index 02630f98589..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/add.jsp +++ /dev/null @@ -1,42 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> -<html> -<head><title>Add New Contact</title></head> -<body> -<h1>Add Contact</h1> -<form method="post"> - <table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5"> - <tr> - <td alignment="right" width="20%">Name:</td> - <spring:bind path="webContact.name"> - <td width="20%"> - <input type="text" name="name" value="<c:out value="${status.value}"/>"> - </td> - <td width="60%"> - <font color="red"><c:out value="${status.errorMessage}"/></font> - </td> - </spring:bind> - </tr> - <tr> - <td alignment="right" width="20%">Email:</td> - <spring:bind path="webContact.email"> - <td width="20%"> - <input type="text" name="email" value="<c:out value="${status.value}"/>"> - </td> - <td width="60%"> - <font color="red"><c:out value="${status.errorMessage}"/></font> - </td> - </spring:bind> - </tr> - </table> - <br> - <spring:hasBindErrors name="webContact"> - <b>Please fix all errors!</b> - </spring:hasBindErrors> - <br><br> - - <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> - <input name="execute" type="submit" alignment="center" value="Execute"> -</form> -<a href="<c:url value="../hello.htm"/>">Home</a> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/addPermission.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/addPermission.jsp deleted file mode 100644 index dfd2476c643..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/addPermission.jsp +++ /dev/null @@ -1,56 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> -<html> -<head><title>Add Permission</title></head> -<body> -<h1>Add Permission</h1> -<form method="post"> - <table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5"> - <tr> - <td alignment="right" width="20%">Contact:</td> - <td width="60%"><c:out value="${addPermission.contact}"/></td> - </tr> - <tr> - <td alignment="right" width="20%">Recipient:</td> - <spring:bind path="addPermission.recipient"> - <td width="20%"> - <select name="<c:out value="${status.expression}"/>"> - <c:forEach var="thisRecipient" items="${recipients}"> - <option <c:if test="${thisRecipient.key == status.value}">selected</c:if> value="<c:out value="${thisRecipient.key}"/>"> - <c:out value="${thisRecipient.value}"/></option> - </c:forEach> - </select> - </td> - <td width="60%"> - <font color="red"><c:out value="${status.errorMessage}"/></font> - </td> - </spring:bind> - </tr> - <tr> - <td alignment="right" width="20%">Permission:</td> - <spring:bind path="addPermission.permission"> - <td width="20%"> - <select name="<c:out value="${status.expression}"/>"> - <c:forEach var="thisPermission" items="${permissions}"> - <option <c:if test="${thisPermission.key == status.value}">selected</c:if> value="<c:out value="${thisPermission.key}"/>"> - <c:out value="${thisPermission.value}"/></option> - </c:forEach> - </select> - </td> - <td width="60%"> - <font color="red"><c:out value="${status.errorMessage}"/></font> - </td> - </spring:bind> - </tr> - </table> - <br> - <spring:hasBindErrors name="webContact"> - <b>Please fix all errors!</b> - </spring:hasBindErrors> - <br><br> - <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> - <input name="execute" type="submit" alignment="center" value="Execute"> -</form> -<p> -<A HREF="<c:url value="adminPermission.htm"><c:param name="contactId" value="${addPermission.contact.id}"/></c:url>">Admin Permission</A> <a href="<c:url value="index.htm"/>">Manage</a> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/adminPermission.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/adminPermission.jsp deleted file mode 100644 index 2e0d43fae46..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/adminPermission.jsp +++ /dev/null @@ -1,30 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<html> -<head><title>Administer Permissions</title></head> -<body> -<h1>Administer Permissions</h1> -<p> -<code> -<c:out value="${model.contact}"/> -</code> -</p> -<table cellpadding="3" border="0"> -<c:forEach var="acl" items="${model.acl.entries}"> - <tr> - <td> - <code> - <c:out value="${acl}"/> - </code> - </td> - <td> - <a href="<c:url value="deletePermission.htm"><c:param name="contactId" value="${model.contact.id}"/><c:param name="sid" value="${acl.sid.principal}"/><c:param name="permission" value="${acl.permission.mask}"/></c:url>">Del</a> - </td> - </tr> -</c:forEach> -</table> -<p> -<a href="<c:url value="addPermission.htm"><c:param name="contactId" value="${model.contact.id}"/></c:url>">Add Permission</a> <a href="<c:url value="index.htm"/>">Manage</a> -</p> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deletePermission.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deletePermission.jsp deleted file mode 100644 index 85a71a2b6b0..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deletePermission.jsp +++ /dev/null @@ -1,20 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<html> -<head><title>Permission Deleted</title></head> -<body> -<h1>Permission Deleted</h1> -<P> -<code> -<c:out value="${model.contact}"/> -</code> -<P> -<code> -<c:out value="${model.sid}"/> -</code> -<code> -<c:out value="${model.permission}"/> -</code> -<p><a href="<c:url value="index.htm"/>">Manage</a> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deleted.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deleted.jsp deleted file mode 100644 index 8fed87c4fdd..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/deleted.jsp +++ /dev/null @@ -1,13 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<html> -<head><title>Deletion completed</title></head> -<body> -<h1>Deleted</h1> -<P> -<code> -<c:out value="${contact}"/> -</code> -<p><a href="<c:url value="index.htm"/>">Manage</a> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/frames.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/frames.jsp deleted file mode 100644 index cf3ad2066a9..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/frames.jsp +++ /dev/null @@ -1,10 +0,0 @@ -<html> -<head> -<title>Frames</title> -</head> -<body> -<p>This contains frames, but the frames will not be loaded due to the <a href="https://tools.ietf.org/html/draft-ietf-websec-x-frame-options">X-Frame-Options</a> -being specified as denied. This protects against <a href="https://en.wikipedia.org/wiki/Clickjacking">clickjacking attacks</a></p> -<iframe src="./hello.htm" width="500" height="500"></iframe> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/hello.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/hello.jsp deleted file mode 100644 index 8713e0af0ce..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/hello.jsp +++ /dev/null @@ -1,52 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<html> -<head><title>Contacts Security Demo</title></head> -<body> -<h1>Contacts Security Demo</h1> -<P>Contacts demonstrates the following central Spring Security capabilities: -<ul> -<li><b>Role-based security</b>. Each principal is a member of certain roles, - which are used to restrict access to certain secure objects.</li> -<li><b>Domain object instance security</b>. The <code>Contact</code>, the - main domain object in the application, has an access control list (ACL) - that indicates who is allowed read, administer and delete the object.</li> -<li><b>Method invocation security</b>. The <code>ContactManager</code> service - layer bean has a number of secured (protected) and public (unprotected) - methods.</li> -<li><b>Web request security</b>. The <code>/secure</code> URI path is protected - by Spring Security from principals not holding the - <code>ROLE_USER</code> granted authority.</li> -<li><b>Security unaware application objects</b>. None of the objects - are aware of the security being implemented by Spring Security. *</li> -<li><b>Security taglib usage</b>. All of the JSPs use Spring Security's - taglib to evaluate security information. *</li> -<li><b>Fully declarative security</b>. Every capability is configured in - the application context using standard Spring Security classes. *</li> -<li><b>Database-sourced security data</b>. All of the user, role and ACL - information is obtained from an in-memory JDBC-compliant database.</li> -<li><b>Integrated form-based and BASIC authentication</b>. Any BASIC - authentication header is detected and used for authentication. Normal - interactive form-based authentication is used by default.</li> -<li><b>Remember-me services</b>. Spring Security's pluggable remember-me - strategy is demonstrated, with a corresponding checkbox on the login form.</li> -</ul> - -* As the application provides an "ACL Administration" use case, those -classes are necessarily aware of security. But no business use cases are. - -<p>Please excuse the lack of look 'n' feel polish in this application. -It is about security, after all! :-) - -<p>To demonstrate a public method on <code>ContactManager</code>, -here's a random <code>Contact</code>: -<p> -<code> -<c:out value="${contact}"/> -</code> -<p>Get started by clicking "Manage"... -<p><A HREF="<c:url value="secure/index.htm"/>">Manage</a> -<a href="<c:url value="secure/debug.jsp"/>">Debug</a> -<a href="<c:url value="./frames.htm"/>">Frames</a> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/include.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/include.jsp deleted file mode 100644 index 3b31878e13c..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/include.jsp +++ /dev/null @@ -1,6 +0,0 @@ -<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> -<%@ taglib prefix="security" 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" %> -<%@ page pageEncoding="UTF-8" %> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/index.jsp b/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/index.jsp deleted file mode 100644 index 10dc023d2fb..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/jsp/index.jsp +++ /dev/null @@ -1,37 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<html> -<head><title>Your Contacts</title></head> -<body> -<h1><security:authentication property="principal.username"/>'s Contacts</h1> -<P> -<table cellpadding=3 border=0> -<tr><td><b>id</b></td><td><b>Name</b></td><td><b>Email</b></td></tr> -<c:forEach var="contact" items="${model.contacts}" > - <tr> - <td> - <c:out value="${contact.id}"/> - </td> - <td> - <c:out value="${contact.name}"/> - </td> - <td> - <c:out value="${contact.email}"/> - </td> - <c:if test="${model.hasDeletePermission[contact]}"> - <td><a href="<c:url value="del.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Del</a></td> - </c:if> - <c:if test="${model.hasAdminPermission[contact]}"> - <td><a href="<c:url value="adminPermission.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Admin Permission</a></td> - </c:if> - </tr> -</c:forEach> -</table> -<p><a href="<c:url value="add.htm"/>">Add</a> </p> - -<form action="<c:url value="/logout"/>" method="post"> - <input type="submit" value="Logoff"/> (also clears any remember-me cookie) - <security:csrfInput/> -</form> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/remoting-servlet.xml b/samples/xml/contacts/src/main/webapp/WEB-INF/remoting-servlet.xml deleted file mode 100644 index f3f25bd5046..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/remoting-servlet.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "https://www.springframework.org/dtd/spring-beans.dtd"> - -<!-- - - Contacts web application - --> -<beans> - - <!-- RMI exporter for the ContactManager --> - <!-- This could just as easily have been in - applicationContext-common-business.xml, because it doesn't rely on - DispatcherServlet or indeed any other HTTP services. It's in this - application context simply for logical placement with other - remoting exporters. --> - <!-- COMMENTED OUT BY DEFAULT TO AVOID CONFLICTS WITH APPLICATION SERVERS - <bean id="contactManager-rmi" class="org.springframework.remoting.rmi.RmiServiceExporter"> - <property name="service"><ref bean="contactManager"/></property> - <property name="serviceInterface"> - <value>sample.contact.ContactManager</value> - </property> - <property name="serviceName"><value>contactManager</value></property> - <property name="registryPort"><value>1099</value></property> - </bean> - --> - - <!-- HTTP invoker exporter for the ContactManager --> - <!-- Spring's HTTP invoker uses Java serialization via HTTP --> - <bean name="/ContactManager-httpinvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> - <property name="service" ref="contactManager"/> - <property name="serviceInterface" value="sample.contact.ContactManager"/> - </bean> - - <!-- Hessian exporter for the ContactManager --> - <!-- Hessian is a slim binary HTTP remoting protocol --> -<!-- - <bean name="/ContactManager-hessian" class="org.springframework.remoting.caucho.HessianServiceExporter"> - <property name="service" ref="contactManager"/> - <property name="serviceInterface" value="sample.contact.ContactManager"/> - </bean> ---> - <!-- Burlap exporter for the ContactManager --> - <!-- Burlap is a slim XML-based HTTP remoting protocol --> -<!-- - <bean name="/ContactManager-burlap" class="org.springframework.remoting.caucho.BurlapServiceExporter"> - <property name="service" ref="contactManager"/> - <property name="serviceInterface" value="sample.contact.ContactManager"/> - </bean> ---> -</beans> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/spring.tld b/samples/xml/contacts/src/main/webapp/WEB-INF/spring.tld deleted file mode 100644 index 895f80affcb..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/spring.tld +++ /dev/null @@ -1,311 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "https://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> - -<taglib> - - <tlib-version>1.1.1</tlib-version> - - <jsp-version>1.2</jsp-version> - - <short-name>Spring</short-name> - - <uri>http://www.springframework.org/tags</uri> - - <description>Spring Framework JSP Tag Library. Authors: Rod Johnson, Juergen Hoeller</description> - - - <tag> - - <name>htmlEscape</name> - <tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class> - <body-content>JSP</body-content> - - <description> - Sets default HTML escape value for the current page. - Overrides a "defaultHtmlEscape" context-param in web.xml, if any. - </description> - - <attribute> - <name>defaultHtmlEscape</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>escapeBody</name> - <tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class> - <body-content>JSP</body-content> - - <description> - Escapes its enclosed body content, applying HTML escaping and/or JavaScript escaping. - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>javaScriptEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>message</name> - <tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class> - <body-content>JSP</body-content> - - <description> - Retrieves the message with the given code, or text if code isn't resolvable. - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <attribute> - <name>code</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>arguments</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>text</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>var</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>scope</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>javaScriptEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>theme</name> - <tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class> - <body-content>JSP</body-content> - - <description> - Retrieves the theme message with the given code, or text if code isn't resolvable. - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <attribute> - <name>code</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>arguments</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>text</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>var</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>scope</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>javaScriptEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>hasBindErrors</name> - <tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class> - <body-content>JSP</body-content> - - <description> - Provides Errors instance in case of bind errors. - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <variable> - <name-given>errors</name-given> - <variable-class>org.springframework.validation.Errors</variable-class> - </variable> - - <attribute> - <name>name</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>nestedPath</name> - <tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class> - <body-content>JSP</body-content> - - <description> - Sets a nested path to be used by the bind tag's path. - </description> - - <variable> - <name-given>nestedPath</name-given> - <variable-class>java.lang.String</variable-class> - </variable> - - <attribute> - <name>path</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>bind</name> - <tag-class>org.springframework.web.servlet.tags.BindTag</tag-class> - <body-content>JSP</body-content> - - <description> - Provides BindStatus object for the given bind path. - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <variable> - <name-given>status</name-given> - <variable-class>org.springframework.web.servlet.support.BindStatus</variable-class> - </variable> - - <attribute> - <name>path</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>ignoreNestedPath</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - - - <tag> - - <name>transform</name> - <tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class> - <body-content>JSP</body-content> - - <description> - Provides transformation of variables to Strings, using an appropriate - custom PropertyEditor from BindTag (can only be used inside BindTag). - The HTML escaping flag participates in a page-wide or application-wide setting - (i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml). - </description> - - <attribute> - <name>value</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>var</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>scope</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - <attribute> - <name>htmlEscape</name> - <required>false</required> - <rtexprvalue>true</rtexprvalue> - </attribute> - - </tag> - -</taglib> diff --git a/samples/xml/contacts/src/main/webapp/WEB-INF/web.xml b/samples/xml/contacts/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 7b4f0d9c4f8..00000000000 --- a/samples/xml/contacts/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - Contacts web application - - - --> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - <display-name>Contacts Sample Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:applicationContext-common-business.xml - classpath:applicationContext-common-authorization.xml - classpath:applicationContext-security.xml - </param-value> - </context-param> - - <!-- Nothing below here needs to be modified --> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>contacts.root</param-value> - </context-param> - - <filter> - <filter-name>localizationFilter</filter-name> - <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> - </filter> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>localizationFilter</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <!-- - - Provides core MVC application controller. See contacts-servlet.xml. - --> - <servlet> - <servlet-name>contacts</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>1</load-on-startup> - </servlet> - - <!-- - - Provides web services endpoint. See remoting-servlet.xml. - --> - <servlet> - <servlet-name>remoting</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>2</load-on-startup> - </servlet> - - <servlet-mapping> - <servlet-name>contacts</servlet-name> - <url-pattern>*.htm</url-pattern> - </servlet-mapping> - - <servlet-mapping> - <servlet-name>remoting</servlet-name> - <url-pattern>/remoting/*</url-pattern> - </servlet-mapping> - - <welcome-file-list> - <welcome-file>index.jsp</welcome-file> - </welcome-file-list> - - <error-page> - <error-code>403</error-code> - <location>/error.html</location> - </error-page> - -</web-app> diff --git a/samples/xml/contacts/src/main/webapp/accessDenied.jsp b/samples/xml/contacts/src/main/webapp/accessDenied.jsp deleted file mode 100644 index c94ae99b604..00000000000 --- a/samples/xml/contacts/src/main/webapp/accessDenied.jsp +++ /dev/null @@ -1,22 +0,0 @@ -<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %> -<%@ page import="org.springframework.security.core.Authentication" %> - -<html> - <head> - <title>Access Denied</title> - </head> - -<body> -<h1>Sorry, access is denied</h1> - -<p> -<%= request.getAttribute("SPRING_SECURITY_403_EXCEPTION")%> -</p> -<p> -<% Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null) { %> - Authentication object as a String: <%= auth.toString() %><br /><br /> -<% } %> -</p> -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/error.html b/samples/xml/contacts/src/main/webapp/error.html deleted file mode 100644 index 3c58108da56..00000000000 --- a/samples/xml/contacts/src/main/webapp/error.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> - <title>Access denied!</title> - <h1>Access Denied</h1> - <p>We're sorry, but you are not authorized to perform the requested operation.</p> -</html> \ No newline at end of file diff --git a/samples/xml/contacts/src/main/webapp/exitUser.jsp b/samples/xml/contacts/src/main/webapp/exitUser.jsp deleted file mode 100644 index 5f9e33b31d1..00000000000 --- a/samples/xml/contacts/src/main/webapp/exitUser.jsp +++ /dev/null @@ -1,39 +0,0 @@ -<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> - -<%@ page import="org.springframework.security.core.Authentication" %> -<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %> -<%@ page pageEncoding="UTF-8" %> - -<html> - <head> - <title>Exit User</title> - </head> - - <body> - <h1>Exit User</h1> - - <c:if test="${not empty param.login_error}"> - <font color="red"> - Your 'Exit User' attempt was not successful, try again.<br/><br/> - Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> - </font> - </c:if> - - <form action="<c:url value='logout/impersonate'/>" method="POST"> - <table> - <tr><td>Current User:</td><td> - -<% - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null) { %> - - <%= auth.getPrincipal().toString() %> - - <% } %> - </td></tr> - <tr><td colspan='2'><input name="exit" type="submit" value="Exit"></td></tr> - </table> - <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> - </form> - </body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/index.jsp b/samples/xml/contacts/src/main/webapp/index.jsp deleted file mode 100644 index 4c86e330937..00000000000 --- a/samples/xml/contacts/src/main/webapp/index.jsp +++ /dev/null @@ -1,4 +0,0 @@ -<%@ include file="/WEB-INF/jsp/include.jsp" %> - -<%-- Redirected because we can't set the welcome page to a virtual URL. --%> -<c:redirect url="/hello.htm"/> diff --git a/samples/xml/contacts/src/main/webapp/login.jsp b/samples/xml/contacts/src/main/webapp/login.jsp deleted file mode 100644 index ed072882b98..00000000000 --- a/samples/xml/contacts/src/main/webapp/login.jsp +++ /dev/null @@ -1,47 +0,0 @@ -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ page pageEncoding="UTF-8" %> - -<html> - <head> - <title>Login</title> - </head> - - <body onload="document.f.username.focus();"> - <h1>Login</h1> - - <p>Valid users: - <p> - <p>username <b>rod</b>, password <b>koala</b> - <p>username <b>dianne</b>, password <b>emu</b> - <p>username <b>scott</b>, password <b>wombat</b> - <p>username <b>peter</b>, password <b>opal</b> (user disabled) - <p>username <b>bill</b>, password <b>wombat</b> - <p>username <b>bob</b>, password <b>wombat</b> - <p>username <b>jane</b>, password <b>wombat</b> - <p> - - <p>Locale is: <%= request.getLocale() %></p> - <%-- this form-login-page form is also used as the - form-error-page to ask for a login again. - --%> - <c:if test="${not empty param.login_error}"> - <font color="red"> - Your login attempt was not successful, try again.<br/><br/> - Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>. - </font> - </c:if> - - <form name="f" action="<c:url value='login'/>" method="POST"> - <table> - <tr><td>User:</td><td><input type='text' name='username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>'/></td></tr> - <tr><td>Password:</td><td><input type='password' name='password'></td></tr> - <tr><td><input type="checkbox" name="remember-me"></td><td>Don't ask for my password for two weeks</td></tr> - - <tr><td colspan='2'><input name="submit" type="submit"></td></tr> - <tr><td colspan='2'><input name="reset" type="reset"></td></tr> - </table> - <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> - </form> - - </body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/secure/debug.jsp b/samples/xml/contacts/src/main/webapp/secure/debug.jsp deleted file mode 100644 index 553bc3a15a4..00000000000 --- a/samples/xml/contacts/src/main/webapp/secure/debug.jsp +++ /dev/null @@ -1,40 +0,0 @@ -<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %> -<%@ page import="org.springframework.security.core.Authentication" %> -<%@ page import="org.springframework.security.core.GrantedAuthority" %> - -<html> -<head> -<title>Security Debug Information</title> -</head> -<body> - -<h3>Security Debug Information</h3> - -<% - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null) { %> -<p> - Authentication object is of type: <em><%= auth.getClass().getName() %></em> -</p> -<p> - Authentication object as a String: <br/><br/><%= auth.toString() %> -</p> - - Authentication object holds the following granted authorities:<br /><br /> -<% - for (GrantedAuthority authority : auth.getAuthorities()) { %> - <%= authority %> (<em>getAuthority()</em>: <%= authority.getAuthority() %>)<br /> -<% } -%> - - <p><b>Success! Your web filters appear to be properly configured!</b></p> -<% - } else { -%> - Authentication object is null.<br /> - This is an error and your Spring Security application will not operate properly until corrected.<br /><br /> -<% } -%> - -</body> -</html> diff --git a/samples/xml/contacts/src/main/webapp/switchUser.jsp b/samples/xml/contacts/src/main/webapp/switchUser.jsp deleted file mode 100644 index a8d3e15204d..00000000000 --- a/samples/xml/contacts/src/main/webapp/switchUser.jsp +++ /dev/null @@ -1,42 +0,0 @@ -<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %> -<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %> -<%@ page import="org.springframework.security.core.AuthenticationException" %> -<%@ page pageEncoding="UTF-8" %> - -<html> - <head> - <title>Switch User</title> - </head> - - <body> - <h1>Switch to User</h1> - - <h3>Valid users:</h3> - - <p>username <b>rod</b>, password <b>koala</b></p> - <p>username <b>dianne</b>, password <b>emu</b></p> - <p>username <b>scott</b>, password <b>wombat</b></p> - <p>username <b>bill</b>, password <b>wombat</b></p> - <p>username <b>bob</b>, password <b>wombat</b></p> - <p>username <b>jane</b>, password <b>wombat</b></p> - <%-- this form-login-page form is also used as the - form-error-page to ask for a login again. - --%> - <c:if test="${not empty param.login_error}"> - <p> - <font color="red"> - Your 'su' attempt was not successful, try again.<br/> - </font> - </p> - </c:if> - - <form action="<c:url value='login/impersonate'/>" method="POST"> - <table> - <tr><td>User:</td><td><input type='text' name='username'></td></tr> - <tr><td colspan='2'><input name="switch" type="submit" value="Switch to User"></td></tr> - </table> - <input type="hidden" name="<c:out value="${_csrf.parameterName}"/>" value="<c:out value="${_csrf.token}"/>"/> - </form> - - </body> -</html> diff --git a/samples/xml/contacts/src/site/resources/logback-test.xml b/samples/xml/contacts/src/site/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/contacts/src/site/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/contacts/src/site/resources/sslhowto.txt b/samples/xml/contacts/src/site/resources/sslhowto.txt deleted file mode 100644 index 9e073a6dd4e..00000000000 --- a/samples/xml/contacts/src/site/resources/sslhowto.txt +++ /dev/null @@ -1,99 +0,0 @@ -$Id$ - -CAS requires HTTPS be used for all operations, with the certificate used -having been signed by a certificate in the cacerts files shipped with Java. - -If you're using a HTTPS certificate signed by a well known authority -(like Verisign), you can safely ignore the procedure below (although you -might find the troubleshooting section at the end helpful). - -The following demonstrates how to create a self-signed certificate and add -it to the cacerts file. If you just want to use the certificate we have -already created and shipped with Spring Security, you -can skip directly to step 3. - - -1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password - -What is your first and last name? - [Unknown]: localhost -What is the name of your organizational unit? - [Unknown]: Spring Security -What is the name of your organization? - [Unknown]: TEST CERTIFICATE ONLY. DO NOT USE IN PRODUCTION. -What is the name of your City or Locality? - [Unknown]: -What is the name of your State or Province? - [Unknown]: -What is the two-letter country code for this unit? - [Unknown]: -Is CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ONLY. D -O NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown correct? - [no]: yes - - -2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password - -3. copy acegisecurity.txt %JAVA_HOME%\lib\security - -4. copy keystore %YOUR_WEB_CONTAINER_LOCATION% - - NOTE: You will need to configure your web container as appropriate. - We recommend you test the certificate works by visiting - https://localhost:8443. When prompted by your browser, select to - install the certificate. - -5. cd %JAVA_HOME%\lib\security - -6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity - -Owner: CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ONL -Y. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown -Issuer: CN=localhost, OU=Spring Security, O=TEST CERTIFICATE ON -LY. DO NOT USE IN PRODUCTION., L=Unknown, ST=Unknown, C=Unknown -Serial number: 4080daf4 -Valid from: Sat Apr 17 07:21:24 GMT 2004 until: Tue Sep 02 07:21:24 GMT 2031 -Certificate fingerprints: - MD5: B4:AC:A8:24:34:99:F1:A9:F8:1D:A5:6C:BF:0A:34:FA - SHA1: F1:E6:B1:3A:01:39:2D:CF:06:FA:82:AB:86:0D:77:9D:06:93:D6:B0 -Trust this certificate? [no]: yes -Certificate was added to keystore -[Saving cacerts] - - -7. Finished. You can now run the sample application as if you purchased a - properly signed certificate. For production applications, of course you should - use an appropriately signed certificate so your web visitors will trust it - (such as issued by Thawte, Verisign etc). - -TROUBLESHOOTING - -* First of all, most CAS-Acegi Security problems are because of untrusted - SSL certificates. So it's important to understand why. Most people can - load the Acegi Security webapp, get redirected to the CAS server, then - after login they get redirected back to the Acegi Security webapp and - receive a failure. This is because the CAS server redirects to something - like https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ - which causes the "service ticket" (the "ticket" parameter) to be validated. - net.sf.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator - performs service ticket validation by delegation to CAS' - ProxyTicketValidator class. The ProxyTicketValidator class will perform a - HTTPS connection from the web server running the Acegi Security webapp - (server3.company.com) above to the CAS server. If for some reason the - web server keystore does not trust the HTTPS certificate presented by the - CAS server, you will receive various failures as discussed below. NB: This - has NOTHING to do with client-side (browser) certificates. You need to - correct the trust between the two webserver keystores alone. - -* A "sun.security.validator.ValidatorException: No trusted certificate - found" indicates the cacerts is not being used or it did not correctly - import the certificate. To rule out your web container replacing or in - some way modifying the trust manager, set the - CasProxyTicketValidator.trustStore property to the full file system - location to your cacerts file. - -* If your web container is ignoring your cacerts file, double-check it - is stored in $JAVA_HOME\lib\security\cacerts. $JAVA_HOME might be - pointing to the SDK, not JRE. In that case, copy - $JAVA_HOME\jre\lib\security\cacerts to $JAVA_HOME\lib\security\cacerts - diff --git a/samples/xml/contacts/src/test/java/sample/contact/ContactManagerTests.java b/samples/xml/contacts/src/test/java/sample/contact/ContactManagerTests.java deleted file mode 100644 index 2c64395c1dd..00000000000 --- a/samples/xml/contacts/src/test/java/sample/contact/ContactManagerTests.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * 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 - * - * https://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 sample.contact; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -import java.util.List; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * Tests {@link ContactManager}. - * - * @author David Leal - * @author Ben Alex - * @author Luke Taylor - */ -@ContextConfiguration(locations = { "/applicationContext-security.xml", - "/applicationContext-common-authorization.xml", - "/applicationContext-common-business.xml" }) -@RunWith(SpringJUnit4ClassRunner.class) -public class ContactManagerTests { - - - @Autowired - protected ContactManager contactManager; - - - void assertContainsContact(long id, List<Contact> contacts) { - for (Contact contact : contacts) { - if (contact.getId().equals(id)) { - return; - } - } - - fail("List of contacts should have contained: " + id); - } - - void assertDoestNotContainContact(long id, List<Contact> contacts) { - for (Contact contact : contacts) { - if (contact.getId().equals(id)) { - fail("List of contact should NOT (but did) contain: " + id); - } - } - } - - /** - * Locates the first <code>Contact</code> of the exact name specified. - * <p> - * Uses the {@link ContactManager#getAll()} method. - * - * @param id Identify of the contact to locate (must be an exact match) - * - * @return the domain or <code>null</code> if not found - */ - Contact getContact(String id) { - for (Contact contact : contactManager.getAll()) { - if (contact.getId().equals(id)) { - return contact; - } - } - - return null; - } - - private void makeActiveUser(String username) { - String password = ""; - - if ("rod".equals(username)) { - password = "koala"; - } - else if ("dianne".equals(username)) { - password = "emu"; - } - else if ("scott".equals(username)) { - password = "wombat"; - } - else if ("peter".equals(username)) { - password = "opal"; - } - - Authentication authRequest = new UsernamePasswordAuthenticationToken(username, - password); - SecurityContextHolder.getContext().setAuthentication(authRequest); - } - - @After - public void clearContext() { - SecurityContextHolder.clearContext(); - } - - @Test - public void testDianne() { - makeActiveUser("dianne"); // has ROLE_USER - - List<Contact> contacts = contactManager.getAll(); - assertThat(contacts).hasSize(4); - - assertContainsContact(4, contacts); - assertContainsContact(5, contacts); - assertContainsContact(6, contacts); - assertContainsContact(8, contacts); - - assertDoestNotContainContact(1, contacts); - assertDoestNotContainContact(2, contacts); - assertDoestNotContainContact(3, contacts); - } - - @Test - public void testrod() { - makeActiveUser("rod"); // has ROLE_SUPERVISOR - - List<Contact> contacts = contactManager.getAll(); - - assertThat(contacts).hasSize(4); - - assertContainsContact(1, contacts); - assertContainsContact(2, contacts); - assertContainsContact(3, contacts); - assertContainsContact(4, contacts); - - assertDoestNotContainContact(5, contacts); - - Contact c1 = contactManager.getById(4L); - - contactManager.deletePermission(c1, new PrincipalSid("bob"), - BasePermission.ADMINISTRATION); - contactManager.addPermission(c1, new PrincipalSid("bob"), - BasePermission.ADMINISTRATION); - } - - @Test - public void testScott() { - makeActiveUser("scott"); // has ROLE_USER - - List<Contact> contacts = contactManager.getAll(); - - assertThat(contacts).hasSize(5); - - assertContainsContact(4, contacts); - assertContainsContact(6, contacts); - assertContainsContact(7, contacts); - assertContainsContact(8, contacts); - assertContainsContact(9, contacts); - - assertDoestNotContainContact(1, contacts); - } -} diff --git a/samples/xml/contacts/src/test/resources/logback-test.xml b/samples/xml/contacts/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/contacts/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/dms/spring-security-samples-xml-dms.gradle b/samples/xml/dms/spring-security-samples-xml-dms.gradle deleted file mode 100644 index 51a003853b8..00000000000 --- a/samples/xml/dms/spring-security-samples-xml-dms.gradle +++ /dev/null @@ -1,17 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-acl') - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-tx' - - optional 'net.sf.ehcache:ehcache' - - runtime project(':spring-security-config') - runtime 'org.hsqldb:hsqldb' - runtime 'org.springframework:spring-context-support' - - testCompile 'org.springframework:spring-context' -} diff --git a/samples/xml/dms/src/main/java/sample/dms/AbstractElement.java b/samples/xml/dms/src/main/java/sample/dms/AbstractElement.java deleted file mode 100755 index a65f3341796..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/AbstractElement.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.util.Assert; - -/** - * @author Ben Alex - * - */ -public abstract class AbstractElement { - /** The name of this token (a filename or directory segment name */ - private String name; - - /** The parent of this token (a directory, or null if referring to root) */ - private AbstractElement parent; - - /** The database identifier for this object (null if not persisted) */ - private Long id; - - /** - * Constructor to use to represent a root element. A root element has an id of -1. - */ - protected AbstractElement() { - this.name = "/"; - this.parent = null; - this.id = -1L; - } - - /** - * Constructor to use to represent a non-root element. - * - * @param name name for this element (required, cannot be "/") - * @param parent for this element (required, cannot be null) - */ - protected AbstractElement(String name, AbstractElement parent) { - Assert.hasText(name, "Name required"); - Assert.notNull(parent, "Parent required"); - Assert.notNull(parent.getId(), - "The parent must have been saved in order to create a child"); - this.name = name; - this.parent = parent; - } - - public Long getId() { - return id; - } - - /** - * @return the name of this token (never null, although will be "/" if root, otherwise - * it won't include separators) - */ - public String getName() { - return name; - } - - public AbstractElement getParent() { - return parent; - } - - /** - * @return the fully-qualified name of this element, including any parents - */ - public String getFullName() { - List<String> strings = new ArrayList<>(); - AbstractElement currentElement = this; - while (currentElement != null) { - strings.add(0, currentElement.getName()); - currentElement = currentElement.getParent(); - } - - StringBuilder sb = new StringBuilder(); - String lastCharacter = null; - for (String token : strings) { - if (!"/".equals(lastCharacter) && lastCharacter != null) { - sb.append("/"); - } - sb.append(token); - lastCharacter = token.substring(token.length() - 1); - } - return sb.toString(); - } -} diff --git a/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java b/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java deleted file mode 100755 index 476a73dd918..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/DataSourcePopulator.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -import javax.sql.DataSource; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.Assert; - -/** - * Populates the DMS in-memory database with document and ACL information. - * - * @author Ben Alex - */ -public class DataSourcePopulator implements InitializingBean { - protected static final int LEVEL_NEGATE_READ = 0; - protected static final int LEVEL_GRANT_READ = 1; - protected static final int LEVEL_GRANT_WRITE = 2; - protected static final int LEVEL_GRANT_ADMIN = 3; - protected JdbcTemplate template; - protected DocumentDao documentDao; - - public DataSourcePopulator(DataSource dataSource, DocumentDao documentDao) { - Assert.notNull(dataSource, "DataSource required"); - Assert.notNull(documentDao, "DocumentDao required"); - this.template = new JdbcTemplate(dataSource); - this.documentDao = documentDao; - } - - public void afterPropertiesSet() { - // ACL tables - template.execute("CREATE TABLE ACL_SID(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,PRINCIPAL BOOLEAN NOT NULL,SID VARCHAR_IGNORECASE(100) NOT NULL,CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));"); - template.execute("CREATE TABLE ACL_CLASS(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,CLASS VARCHAR_IGNORECASE(100) NOT NULL,CLASS_ID_TYPE VARCHAR_IGNORECASE(100),CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));"); - template.execute("CREATE TABLE ACL_OBJECT_IDENTITY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,OBJECT_ID_CLASS BIGINT NOT NULL,OBJECT_ID_IDENTITY VARCHAR_IGNORECASE(36) NOT NULL,PARENT_OBJECT BIGINT,OWNER_SID BIGINT,ENTRIES_INHERITING BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));"); - template.execute("CREATE TABLE ACL_ENTRY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL,MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL,AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));"); - - // Normal authentication tables - template.execute("CREATE TABLE USERS(USERNAME VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR_IGNORECASE(500) NOT NULL,ENABLED BOOLEAN NOT NULL);"); - template.execute("CREATE TABLE AUTHORITIES(USERNAME VARCHAR_IGNORECASE(50) NOT NULL,AUTHORITY VARCHAR_IGNORECASE(50) NOT NULL,CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME));"); - template.execute("CREATE UNIQUE INDEX IX_AUTH_USERNAME ON AUTHORITIES(USERNAME,AUTHORITY);"); - - // Document management system business tables - template.execute("CREATE TABLE DIRECTORY(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY, DIRECTORY_NAME VARCHAR_IGNORECASE(50) NOT NULL, PARENT_DIRECTORY_ID BIGINT)"); - template.execute("CREATE TABLE FILE(ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY, FILE_NAME VARCHAR_IGNORECASE(50) NOT NULL, CONTENT VARCHAR_IGNORECASE(1024), PARENT_DIRECTORY_ID BIGINT)"); - - // Populate the authentication and role tables - template.execute("INSERT INTO USERS VALUES('rod','$2a$10$75pBjapg4Nl8Pzd.3JRnUe7PDJmk9qBGwNEJDAlA3V.dEJxcDKn5O',TRUE);"); - template.execute("INSERT INTO USERS VALUES('dianne','$2a$04$bCMEyxrdF/7sgfUiUJ6Ose2vh9DAMaVBldS1Bw2fhi1jgutZrr9zm',TRUE);"); - template.execute("INSERT INTO USERS VALUES('scott','$2a$06$eChwvzAu3TSexnC3ynw4LOSw1qiEbtNItNeYv5uI40w1i3paoSfLu',TRUE);"); - template.execute("INSERT INTO USERS VALUES('peter','$2a$04$8.H8bCMROLF4CIgd7IpeQ.tcBXLP5w8iplO0n.kCIkISwrIgX28Ii',FALSE);"); - template.execute("INSERT INTO USERS VALUES('bill','$2a$04$8.H8bCMROLF4CIgd7IpeQ.3khQlPVNWbp8kzSQqidQHGFurim7P8O',TRUE);"); - template.execute("INSERT INTO USERS VALUES('bob','$2a$06$zMgxlMf01SfYNcdx7n4NpeFlAGU8apCETz/i2C7VlYWu6IcNyn4Ay',TRUE);"); - template.execute("INSERT INTO USERS VALUES('jane','$2a$05$ZrdS7yMhCZ1J.AAidXZhCOxdjD8LO/dhlv4FJzkXA6xh9gdEbBT/u',TRUE);"); - template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('rod','ROLE_SUPERVISOR');"); - template.execute("INSERT INTO AUTHORITIES VALUES('dianne','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('scott','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('peter','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('bill','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('bob','ROLE_USER');"); - template.execute("INSERT INTO AUTHORITIES VALUES('jane','ROLE_USER');"); - - // Now create an ACL entry for the root directory - SecurityContextHolder.getContext().setAuthentication( - new UsernamePasswordAuthenticationToken("rod", "ignored", AuthorityUtils - .createAuthorityList(("ROLE_IGNORED")))); - - addPermission(documentDao, Directory.ROOT_DIRECTORY, "ROLE_USER", - LEVEL_GRANT_WRITE); - - // Now go off and create some directories and files for our users - createSampleData("rod", "koala"); - createSampleData("dianne", "emu"); - createSampleData("scott", "wombat"); - - } - - /** - * Creates a directory for the user, and a series of sub-directories. The root - * directory is the parent for the user directory. The sub-directories are - * "confidential" and "shared". The ROLE_USER will be given read and write access to - * "shared". - */ - private void createSampleData(String username, String password) { - Assert.notNull(documentDao, "DocumentDao required"); - Assert.hasText(username, "Username required"); - - Authentication auth = new UsernamePasswordAuthenticationToken(username, password); - - try { - // Set the SecurityContextHolder ThreadLocal so any subclasses - // automatically know which user is operating - SecurityContextHolder.getContext().setAuthentication(auth); - - // Create the home directory first - Directory home = new Directory(username, Directory.ROOT_DIRECTORY); - documentDao.create(home); - addPermission(documentDao, home, username, LEVEL_GRANT_ADMIN); - addPermission(documentDao, home, "ROLE_USER", LEVEL_GRANT_READ); - createFiles(documentDao, home); - - // Now create the confidential directory - Directory confid = new Directory("confidential", home); - documentDao.create(confid); - addPermission(documentDao, confid, "ROLE_USER", LEVEL_NEGATE_READ); - createFiles(documentDao, confid); - - // Now create the shared directory - Directory shared = new Directory("shared", home); - documentDao.create(shared); - addPermission(documentDao, shared, "ROLE_USER", LEVEL_GRANT_READ); - addPermission(documentDao, shared, "ROLE_USER", LEVEL_GRANT_WRITE); - createFiles(documentDao, shared); - } - finally { - // Clear the SecurityContextHolder ThreadLocal so future calls are - // guaranteed to be clean - SecurityContextHolder.clearContext(); - } - } - - private void createFiles(DocumentDao documentDao, Directory parent) { - Assert.notNull(documentDao, "DocumentDao required"); - Assert.notNull(parent, "Parent required"); - int countBeforeInsert = documentDao.findElements(parent).length; - for (int i = 0; i < 10; i++) { - File file = new File("file_" + i + ".txt", parent); - documentDao.create(file); - } - Assert.isTrue(countBeforeInsert + 10 == documentDao.findElements(parent).length, - "Failed to increase count by 10"); - } - - /** - * Allows subclass to add permissions. - * - * @param documentDao that will presumably offer methods to enable the operation to be - * completed - * @param element to the subject of the new permissions - * @param recipient to receive permission (if it starts with ROLE_ it is assumed to be - * a GrantedAuthority, else it is a username) - * @param level based on the static final integer fields on this class - */ - protected void addPermission(DocumentDao documentDao, AbstractElement element, - String recipient, int level) { - } -} diff --git a/samples/xml/dms/src/main/java/sample/dms/Directory.java b/samples/xml/dms/src/main/java/sample/dms/Directory.java deleted file mode 100755 index 06a0b20991e..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/Directory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -/** - * - * @author Ben Alex - * - */ -public class Directory extends AbstractElement { - public static final Directory ROOT_DIRECTORY = new Directory(); - - private Directory() { - } - - public Directory(String name, Directory parent) { - super(name, parent); - } - - @Override - public String toString() { - return "Directory[fullName='" + getFullName() + "'; name='" + getName() - + "'; id='" + getId() + "'; parent='" + getParent() + "']"; - } - -} diff --git a/samples/xml/dms/src/main/java/sample/dms/DocumentDao.java b/samples/xml/dms/src/main/java/sample/dms/DocumentDao.java deleted file mode 100755 index 1419d028083..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/DocumentDao.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -/** - * - * @author Ben Alex - * - */ -public interface DocumentDao { - /** - * Creates an entry in the database for the element. - * - * @param element an unsaved element (the "id" will be updated after method is - * invoked) - */ - void create(AbstractElement element); - - /** - * Removes a file from the database for the specified element. - * - * @param file the file to remove (cannot be null) - */ - void delete(File file); - - /** - * Modifies a file in the database. - * - * @param file the file to update (cannot be null) - */ - void update(File file); - - /** - * Locates elements in the database which appear under the presented directory - * - * @param directory the directory (cannot be null - use - * {@link Directory#ROOT_DIRECTORY} for root) - * @return zero or more elements in the directory (an empty array may be returned - - * never null) - */ - AbstractElement[] findElements(Directory directory); -} diff --git a/samples/xml/dms/src/main/java/sample/dms/DocumentDaoImpl.java b/samples/xml/dms/src/main/java/sample/dms/DocumentDaoImpl.java deleted file mode 100755 index 0d83bf75f70..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/DocumentDaoImpl.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -import java.util.List; - -import org.springframework.jdbc.core.support.JdbcDaoSupport; -import org.springframework.security.util.FieldUtils; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.util.Assert; - -/** - * Basic JDBC implementation of {@link DocumentDao}. - * - * @author Ben Alex - */ -public class DocumentDaoImpl extends JdbcDaoSupport implements DocumentDao { - - private static final String INSERT_INTO_DIRECTORY = "insert into directory(directory_name, parent_directory_id) values (?,?)"; - private static final String INSERT_INTO_FILE = "insert into file(file_name, content, parent_directory_id) values (?,?,?)"; - private static final String SELECT_FROM_DIRECTORY = "select id from directory where parent_directory_id = ?"; - private static final String SELECT_FROM_DIRECTORY_NULL = "select id from directory where parent_directory_id is null"; - private static final String SELECT_FROM_FILE = "select id, file_name, content, parent_directory_id from file where parent_directory_id = ?"; - private static final String SELECT_FROM_DIRECTORY_SINGLE = "select id, directory_name, parent_directory_id from directory where id = ?"; - private static final String DELETE_FROM_FILE = "delete from file where id = ?"; - private static final String UPDATE_FILE = "update file set content = ? where id = ?"; - private static final String SELECT_IDENTITY = "call identity()"; - - private Long obtainPrimaryKey() { - Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), - "Transaction must be running"); - return getJdbcTemplate().queryForObject(SELECT_IDENTITY, Long.class); - } - - public void create(AbstractElement element) { - Assert.notNull(element, "Element required"); - Assert.isNull(element.getId(), "Element has previously been saved"); - if (element instanceof Directory) { - Directory directory = (Directory) element; - Long parentId = directory.getParent() == null ? null : directory.getParent() - .getId(); - getJdbcTemplate().update(INSERT_INTO_DIRECTORY, - new Object[] { directory.getName(), parentId }); - FieldUtils.setProtectedFieldValue("id", directory, obtainPrimaryKey()); - } - else if (element instanceof File) { - File file = (File) element; - Long parentId = file.getParent() == null ? null : file.getParent().getId(); - getJdbcTemplate().update(INSERT_INTO_FILE, - new Object[] { file.getName(), file.getContent(), parentId }); - FieldUtils.setProtectedFieldValue("id", file, obtainPrimaryKey()); - } - else { - throw new IllegalArgumentException("Unsupported AbstractElement"); - } - } - - public void delete(File file) { - Assert.notNull(file, "File required"); - Assert.notNull(file.getId(), "File ID required"); - getJdbcTemplate().update(DELETE_FROM_FILE, new Object[] { file.getId() }); - } - - /** Executes recursive SQL as needed to build a full Directory hierarchy of objects */ - private Directory getDirectoryWithImmediateParentPopulated(final Long id) { - return getJdbcTemplate().queryForObject(SELECT_FROM_DIRECTORY_SINGLE, - new Object[] { id }, (rs, rowNumber) -> { - Long parentDirectoryId = rs - .getLong("parent_directory_id"); - Directory parentDirectory = Directory.ROOT_DIRECTORY; - if (parentDirectoryId != null - && !parentDirectoryId.equals(-1L)) { - // Need to go and lookup the parent, so do that first - parentDirectory = getDirectoryWithImmediateParentPopulated(parentDirectoryId); - } - Directory directory = new Directory(rs - .getString("directory_name"), parentDirectory); - FieldUtils.setProtectedFieldValue("id", directory, - rs.getLong("id")); - return directory; - }); - } - - public AbstractElement[] findElements(Directory directory) { - Assert.notNull(directory, - "Directory required (the ID can be null to refer to root)"); - if (directory.getId() == null) { - List<Directory> directories = getJdbcTemplate().query( - SELECT_FROM_DIRECTORY_NULL, (rs, rowNumber) -> getDirectoryWithImmediateParentPopulated(rs - .getLong("id"))); - return directories.toArray(new AbstractElement[] {}); - } - List<AbstractElement> directories = getJdbcTemplate().query( - SELECT_FROM_DIRECTORY, new Object[] { directory.getId() }, - (rs, rowNumber) -> getDirectoryWithImmediateParentPopulated(rs - .getLong("id"))); - List<File> files = getJdbcTemplate().query(SELECT_FROM_FILE, - new Object[] { directory.getId() }, (rs, rowNumber) -> { - Long parentDirectoryId = rs - .getLong("parent_directory_id"); - Directory parentDirectory = null; - if (parentDirectoryId != null) { - parentDirectory = getDirectoryWithImmediateParentPopulated(parentDirectoryId); - } - File file = new File(rs.getString("file_name"), parentDirectory); - FieldUtils.setProtectedFieldValue("id", file, - rs.getLong("id")); - return file; - }); - // Add the File elements after the Directory elements - directories.addAll(files); - return directories.toArray(new AbstractElement[] {}); - } - - public void update(File file) { - Assert.notNull(file, "File required"); - Assert.notNull(file.getId(), "File ID required"); - getJdbcTemplate().update(UPDATE_FILE, - new Object[] { file.getContent(), file.getId() }); - } - -} diff --git a/samples/xml/dms/src/main/java/sample/dms/File.java b/samples/xml/dms/src/main/java/sample/dms/File.java deleted file mode 100755 index 1ceaf8ecdb9..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/File.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms; - -import org.springframework.util.Assert; - -/** - * - * @author Ben Alex - */ -public class File extends AbstractElement { - /** Content of the file, which can be null */ - private String content; - - public File(String name, Directory parent) { - super(name, parent); - Assert.isTrue(!parent.equals(Directory.ROOT_DIRECTORY), - "Cannot insert File into root directory"); - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - @Override - public String toString() { - return "File[fullName='" + getFullName() + "'; name='" + getName() + "'; id='" - + getId() + "'; content=" + getContent() + "'; parent='" + getParent() - + "']"; - } - -} diff --git a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDataSourcePopulator.java b/samples/xml/dms/src/main/java/sample/dms/secured/SecureDataSourcePopulator.java deleted file mode 100755 index 7487c73db70..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDataSourcePopulator.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms.secured; - -import javax.sql.DataSource; - -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.GrantedAuthoritySid; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.acls.model.MutableAcl; -import org.springframework.security.acls.model.MutableAclService; -import org.springframework.security.acls.model.NotFoundException; -import org.springframework.security.acls.model.ObjectIdentity; -import org.springframework.security.acls.model.Permission; -import org.springframework.security.acls.model.Sid; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.Assert; - -import sample.dms.AbstractElement; -import sample.dms.DataSourcePopulator; -import sample.dms.DocumentDao; - -public class SecureDataSourcePopulator extends DataSourcePopulator { - - private MutableAclService aclService; - - public SecureDataSourcePopulator(DataSource dataSource, - SecureDocumentDao documentDao, MutableAclService aclService) { - super(dataSource, documentDao); - Assert.notNull(aclService, "MutableAclService required"); - this.aclService = aclService; - } - - protected void addPermission(DocumentDao documentDao, AbstractElement element, - String recipient, int level) { - Assert.notNull(documentDao, "DocumentDao required"); - Assert.isInstanceOf(SecureDocumentDao.class, documentDao, - "DocumentDao should have been a SecureDocumentDao"); - Assert.notNull(element, "Element required"); - Assert.hasText(recipient, "Recipient required"); - Assert.notNull(SecurityContextHolder.getContext().getAuthentication(), - "SecurityContextHolder must contain an Authentication"); - - // We need SecureDocumentDao to assign different permissions - // SecureDocumentDao dao = (SecureDocumentDao) documentDao; - - // We need to construct an ACL-specific Sid. Note the prefix contract is defined - // on the superclass method's JavaDocs - Sid sid = null; - if (recipient.startsWith("ROLE_")) { - sid = new GrantedAuthoritySid(recipient); - } - else { - sid = new PrincipalSid(recipient); - } - - // We need to identify the target domain object and create an ObjectIdentity for - // it - // This works because AbstractElement has a "getId()" method - ObjectIdentity identity = new ObjectIdentityImpl(element); - // ObjectIdentity identity = new ObjectIdentityImpl(element.getClass(), - // element.getId()); // equivalent - - // Next we need to create a Permission - Permission permission = null; - if (level == LEVEL_NEGATE_READ || level == LEVEL_GRANT_READ) { - permission = BasePermission.READ; - } - else if (level == LEVEL_GRANT_WRITE) { - permission = BasePermission.WRITE; - } - else if (level == LEVEL_GRANT_ADMIN) { - permission = BasePermission.ADMINISTRATION; - } - else { - throw new IllegalArgumentException("Unsupported LEVEL_"); - } - - // Attempt to retrieve the existing ACL, creating an ACL if it doesn't already - // exist for this ObjectIdentity - MutableAcl acl = null; - try { - acl = (MutableAcl) aclService.readAclById(identity); - } - catch (NotFoundException nfe) { - acl = aclService.createAcl(identity); - Assert.notNull(acl, "Acl could not be retrieved or created"); - } - - // Now we have an ACL, add another ACE to it - if (level == LEVEL_NEGATE_READ) { - acl.insertAce(acl.getEntries().size(), permission, sid, false); // not - // granting - } - else { - acl.insertAce(acl.getEntries().size(), permission, sid, true); // granting - } - - // Finally, persist the modified ACL - aclService.updateAcl(acl); - } - -} diff --git a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDao.java b/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDao.java deleted file mode 100755 index d4132d29796..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDao.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms.secured; - -import sample.dms.DocumentDao; - -/** - * Extends the {@link DocumentDao} and introduces ACL-related methods. - * - * @author Ben Alex - * - */ -public interface SecureDocumentDao extends DocumentDao { - /** - * @return all the usernames existing in the system. - */ - String[] getUsers(); -} diff --git a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDaoImpl.java b/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDaoImpl.java deleted file mode 100755 index caae4574b3b..00000000000 --- a/samples/xml/dms/src/main/java/sample/dms/secured/SecureDocumentDaoImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample.dms.secured; - -import org.springframework.security.acls.domain.BasePermission; -import org.springframework.security.acls.domain.ObjectIdentityImpl; -import org.springframework.security.acls.domain.PrincipalSid; -import org.springframework.security.acls.model.MutableAcl; -import org.springframework.security.acls.model.MutableAclService; -import org.springframework.security.acls.model.ObjectIdentity; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.Assert; - -import sample.dms.AbstractElement; -import sample.dms.DocumentDaoImpl; - -/** - * Adds extra {@link SecureDocumentDao} methods. - * - * @author Ben Alex - * - */ -public class SecureDocumentDaoImpl extends DocumentDaoImpl implements SecureDocumentDao { - - private static final String SELECT_FROM_USERS = "SELECT USERNAME FROM USERS ORDER BY USERNAME"; - private MutableAclService mutableAclService; - - public SecureDocumentDaoImpl(MutableAclService mutableAclService) { - Assert.notNull(mutableAclService, "MutableAclService required"); - this.mutableAclService = mutableAclService; - } - - public String[] getUsers() { - return getJdbcTemplate().query(SELECT_FROM_USERS, - (rs, rowNumber) -> rs.getString("USERNAME")).toArray(new String[] {}); - } - - public void create(AbstractElement element) { - super.create(element); - - // Create an ACL identity for this element - ObjectIdentity identity = new ObjectIdentityImpl(element); - MutableAcl acl = mutableAclService.createAcl(identity); - - // If the AbstractElement has a parent, go and retrieve its identity (it should - // already exist) - if (element.getParent() != null) { - ObjectIdentity parentIdentity = new ObjectIdentityImpl(element.getParent()); - MutableAcl aclParent = (MutableAcl) mutableAclService - .readAclById(parentIdentity); - acl.setParent(aclParent); - } - acl.insertAce(acl.getEntries().size(), BasePermission.ADMINISTRATION, - new PrincipalSid(SecurityContextHolder.getContext().getAuthentication()), - true); - - mutableAclService.updateAcl(acl); - } -} diff --git a/samples/xml/dms/src/main/resources/applicationContext-dms-insecure.xml b/samples/xml/dms/src/main/resources/applicationContext-dms-insecure.xml deleted file mode 100755 index 4ad7faf023e..00000000000 --- a/samples/xml/dms/src/main/resources/applicationContext-dms-insecure.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Application context representing the application without any security services. - - - --> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> - <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> - <property name="url" value="jdbc:hsqldb:mem:insecuredms"/> - <property name="username" value="sa"/> - <property name="password" value=""/> - </bean> - - <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> - <property name="transactionAttributeSource"> - <value> - sample.dms.DocumentDao.*=PROPAGATION_REQUIRED - </value> - </property> - <property name="transactionManager" ref="transactionManager" /> - </bean> - - <bean id="documentDao" class="sample.dms.DocumentDaoImpl"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="dataSourcePopulator" class="sample.dms.DataSourcePopulator"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="documentDao"/> - </bean> - -</beans> diff --git a/samples/xml/dms/src/main/resources/applicationContext-dms-secure.xml b/samples/xml/dms/src/main/resources/applicationContext-dms-secure.xml deleted file mode 100755 index 5a291d3844f..00000000000 --- a/samples/xml/dms/src/main/resources/applicationContext-dms-secure.xml +++ /dev/null @@ -1,245 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - Application context representing the application WITH security services. - - - --> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:s="http://www.springframework.org/schema/security" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> - <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> - <property name="url" value="jdbc:hsqldb:mem:securedms"/> - <property name="username" value="sa"/> - <property name="password" value=""/> - </bean> - - <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> - <property name="transactionAttributeSource"> - <value> - sample.dms.secured.SecureDocumentDao.*=PROPAGATION_REQUIRED - sample.dms.DocumentDao.*=PROPAGATION_REQUIRED - org.springframework.security.acls.model.AclService.*=PROPAGATION_REQUIRED - org.springframework.security.acls.model.MutableAclService.*=PROPAGATION_REQUIRED - org.springframework.security.acls.jdbc.JdbcMutableAclService.*=PROPAGATION_REQUIRED - org.springframework.security.acls.jdbc.JdbcAclService.*=PROPAGATION_REQUIRED - </value> - </property> - <property name="transactionManager" ref="transactionManager" /> - </bean> - - <bean id="documentDao" class="sample.dms.secured.SecureDocumentDaoImpl"> - <constructor-arg ref="aclService"/> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="dataSourcePopulator" class="sample.dms.secured.SecureDataSourcePopulator"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="documentDao"/> - <constructor-arg ref="aclService"/> - </bean> - - <!-- =================================== SECURITY DEFINITION BEANS ======================================== --> - - <!-- ======================== AUTHENTICATION (note there is no UI and this is for integration tests only) ======================= --> - - <s:authentication-manager alias="authenticationManager"> - <s:authentication-provider ref="daoAuthenticationProvider"/> - </s:authentication-manager> - - - <bean id="jdbcDaoImpl" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> - <property name="userDetailsService" ref="jdbcDaoImpl"/> - <property name="userCache" ref="userCache"/> - <property name="passwordEncoder"> - <bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> - </property> - </bean> - - <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/> - - <bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> - <property name="cacheManager" ref="cacheManager"/> - <property name="cacheName" value="userCache"/> - </bean> - - <bean id="userCache" class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache"> - <property name="cache" ref="userCacheBackend"/> - </bean> - - <!-- Automatically receives AuthenticationEvent messages --> - <bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/> - - <!-- ========================= "BEFORE INVOCATION" AUTHORIZATION DEFINITIONS ============================== --> - - <!-- ACL permission masks used by this application --> - <bean id="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> - <property name="staticField" value="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> - </bean> - <bean id="org.springframework.security.acls.domain.BasePermission.READ" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> - <property name="staticField" value="org.springframework.security.acls.domain.BasePermission.READ"/> - </bean> - <bean id="org.springframework.security.acls.domain.BasePermission.WRITE" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> - <property name="staticField" value="org.springframework.security.acls.domain.BasePermission.WRITE"/> - </bean> - - - <!-- An access decision voter that reads ROLE_* configuration settings --> - <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/> - - <!-- An access decision voter that reads ACL_ABSTRACT_ELEMENT_WRITE_PARENT configuration settings --> - <bean id="aclAbstractElementWriteParentVoter" class="org.springframework.security.acls.AclEntryVoter"> - <constructor-arg ref="aclService"/> - <constructor-arg value="ACL_ABSTRACT_ELEMENT_WRITE_PARENT"/> - <constructor-arg> - <list> - <ref bean="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> - <ref bean="org.springframework.security.acls.domain.BasePermission.WRITE"/> - </list> - </constructor-arg> - <property name="processDomainObjectClass" value="sample.dms.AbstractElement"/> - <property name="internalMethod" value="getParent"/> - </bean> - - <!-- An access decision voter that reads ACL_ABSTRACT_ELEMENT_WRITE configuration settings --> - <bean id="aclAbstractElementWriteVoter" class="org.springframework.security.acls.AclEntryVoter"> - <constructor-arg ref="aclService"/> - <constructor-arg value="ACL_ABSTRACT_ELEMENT_WRITE"/> - <constructor-arg> - <list> - <ref bean="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> - <ref bean="org.springframework.security.acls.domain.BasePermission.WRITE"/> - </list> - </constructor-arg> - <property name="processDomainObjectClass" value="sample.dms.AbstractElement"/> - </bean> - - <!-- An access decision manager used by the business objects --> - <bean id="businessAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> - <constructor-arg> - <list> - <ref bean="roleVoter"/> - <ref bean="aclAbstractElementWriteParentVoter"/> - <ref bean="aclAbstractElementWriteVoter"/> - </list> - </constructor-arg> - <property name="allowIfAllAbstainDecisions" value="true"/> - </bean> - - <!-- ========= ACCESS CONTROL LIST LOOKUP MANAGER DEFINITIONS ========= --> - - <bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache"> - <constructor-arg> - <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean"> - <property name="cacheManager" ref="cacheManager"/> - <property name="cacheName" value="aclCache"/> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy"> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/> - </constructor-arg> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"> - <constructor-arg> - <list> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ACL_ADMIN"/> - </bean> - </list> - </constructor-arg> - </bean> - </constructor-arg> - </bean> - - <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="aclCache"/> - <constructor-arg ref="aclAuthorizationStrategy"/> - <constructor-arg> - <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/> - </constructor-arg> - </bean> - - <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"> - <constructor-arg> - <list> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ADMINISTRATOR"/> - </bean> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ADMINISTRATOR"/> - </bean> - <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority"> - <constructor-arg value="ROLE_ADMINISTRATOR"/> - </bean> - </list> - </constructor-arg> - </bean> - - <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService"> - <constructor-arg ref="dataSource"/> - <constructor-arg ref="lookupStrategy"/> - <constructor-arg ref="aclCache"/> - </bean> - - <!-- ============== "AFTER INTERCEPTION" AUTHORIZATION DEFINITIONS =========== --> - - <bean id="afterInvocationManager" class="org.springframework.security.access.intercept.AfterInvocationProviderManager"> - <property name="providers"> - <list> - <ref bean="afterAclCollectionRead"/> - </list> - </property> - </bean> - - <!-- Processes AFTER_ACL_COLLECTION_READ configuration settings --> - <bean id="afterAclCollectionRead" class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider"> - <constructor-arg ref="aclService"/> - <constructor-arg> - <list> - <ref bean="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/> - <ref bean="org.springframework.security.acls.domain.BasePermission.READ"/> - </list> - </constructor-arg> - </bean> - - <!-- ================= METHOD INVOCATION AUTHORIZATION ==================== --> - - <bean id="methodSecurityAdvisor" class="org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor"> - <constructor-arg value="methodSecurityInterceptor" /> - <constructor-arg ref="msmds" /> - <constructor-arg value="msmds" /> - </bean> - - <bean id="methodSecurityInterceptor" class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor"> - <property name="authenticationManager" ref="authenticationManager"/> - <property name="accessDecisionManager" ref="businessAccessDecisionManager"/> - <property name="afterInvocationManager" ref="afterInvocationManager"/> - <property name="securityMetadataSource" ref="msmds" /> - </bean> - - <s:method-security-metadata-source id="msmds"> - <s:protect method="sample.dms.DocumentDao.create" access="ACL_ABSTRACT_ELEMENT_WRITE_PARENT" /> - <s:protect method="sample.dms.DocumentDao.delete" access="ACL_ABSTRACT_ELEMENT_WRITE" /> - <s:protect method="sample.dms.DocumentDao.update" access="ACL_ABSTRACT_ELEMENT_WRITE" /> - <s:protect method="sample.dms.DocumentDao.findElements" access="AFTER_ACL_COLLECTION_READ" /> - <s:protect method="sample.dms.secured.SecureDocumentDao.getUsers" access="ROLE_USER" /> - </s:method-security-metadata-source> - -</beans> diff --git a/samples/xml/dms/src/main/resources/applicationContext-dms-shared.xml b/samples/xml/dms/src/main/resources/applicationContext-dms-shared.xml deleted file mode 100755 index e65da9baf5b..00000000000 --- a/samples/xml/dms/src/main/resources/applicationContext-dms-shared.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Application context representing the transaction, auto proxy and data source beans. - - - --> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> - <property name="dataSource" ref="dataSource"/> - </bean> - - <bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> - - <bean id="transactionAdvisor" class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor" autowire="constructor" /> - -</beans> diff --git a/samples/xml/dms/src/test/java/sample/DmsIntegrationTests.java b/samples/xml/dms/src/test/java/sample/DmsIntegrationTests.java deleted file mode 100644 index 11fbd3f6a63..00000000000 --- a/samples/xml/dms/src/test/java/sample/DmsIntegrationTests.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 sample; - -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 org.junit.After; -import org.junit.Test; -import sample.dms.AbstractElement; -import sample.dms.Directory; -import sample.dms.DocumentDao; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Basic integration test for DMS sample. - * - * @author Ben Alex - * - */ -@ContextConfiguration(locations = { "classpath:applicationContext-dms-shared.xml", - "classpath:applicationContext-dms-insecure.xml" }) -public class DmsIntegrationTests extends AbstractTransactionalJUnit4SpringContextTests { - - @Autowired - protected JdbcTemplate jdbcTemplate; - - @Autowired - protected DocumentDao documentDao; - - @After - public void clearContext() { - SecurityContextHolder.clearContext(); - } - - public void setDocumentDao(DocumentDao documentDao) { - this.documentDao = documentDao; - } - - @Test - public void testBasePopulation() { - assertThat(this.jdbcTemplate.queryForObject("select count(id) from DIRECTORY", - Integer.class)).isEqualTo(9); - assertThat((int) this.jdbcTemplate.queryForObject("select count(id) from FILE", - Integer.class)).isEqualTo(90); - assertThat(this.documentDao.findElements(Directory.ROOT_DIRECTORY).length) - .isEqualTo(3); - } - - @Test - public void testMarissaRetrieval() { - process("rod", "koala", false); - } - - @Test - public void testScottRetrieval() { - process("scott", "wombat", false); - } - - @Test - public void testDianneRetrieval() { - process("dianne", "emu", false); - } - - protected void process(String username, String password, boolean shouldBeFiltered) { - SecurityContextHolder.getContext().setAuthentication( - new UsernamePasswordAuthenticationToken(username, password)); - System.out.println("------ Test for username: " + username + " ------"); - AbstractElement[] rootElements = this.documentDao - .findElements(Directory.ROOT_DIRECTORY); - assertThat(rootElements).hasSize(3); - Directory homeDir = null; - Directory nonHomeDir = null; - for (AbstractElement rootElement : rootElements) { - if (rootElement.getName().equals(username)) { - homeDir = (Directory) rootElement; - } - else { - nonHomeDir = (Directory) rootElement; - } - } - System.out.println("Home directory......: " + homeDir.getFullName()); - System.out.println("Non-home directory..: " + nonHomeDir.getFullName()); - - AbstractElement[] homeElements = this.documentDao.findElements(homeDir); - assertThat(homeElements).hasSize(12); // confidential and shared - // directories, - // plus 10 files - - AbstractElement[] nonHomeElements = this.documentDao.findElements(nonHomeDir); - assertThat(nonHomeElements).hasSize(shouldBeFiltered ? 11 : 12); - - // cannot see the user's "confidential" sub-directory when filtering - - // Attempt to read the other user's confidential directory from the returned - // results - // Of course, we shouldn't find a "confidential" directory in the results if we're - // filtering - Directory nonHomeConfidentialDir = null; - for (AbstractElement nonHomeElement : nonHomeElements) { - if (nonHomeElement.getName().equals("confidential")) { - nonHomeConfidentialDir = (Directory) nonHomeElement; - } - } - - if (shouldBeFiltered) { - assertThat(nonHomeConfidentialDir) - .withFailMessage( - "Found confidential directory when we should not have") - .isNull(); - } - else { - System.out.println( - "Inaccessible dir....: " + nonHomeConfidentialDir.getFullName()); - assertThat(this.documentDao.findElements(nonHomeConfidentialDir).length) - .isEqualTo(10); // 10 - // files - // (no - // sub-directories) - } - - SecurityContextHolder.clearContext(); - } - -} diff --git a/samples/xml/dms/src/test/java/sample/SecureDmsIntegrationTests.java b/samples/xml/dms/src/test/java/sample/SecureDmsIntegrationTests.java deleted file mode 100644 index 63965afb263..00000000000 --- a/samples/xml/dms/src/test/java/sample/SecureDmsIntegrationTests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; - -import org.springframework.test.context.ContextConfiguration; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Basic integration test for DMS sample when security has been added. - * - * @author Ben Alex - * - */ -@ContextConfiguration(locations = { "classpath:applicationContext-dms-shared.xml", - "classpath:applicationContext-dms-secure.xml" }) -public class SecureDmsIntegrationTests extends DmsIntegrationTests { - - @Override - @Test - public void testBasePopulation() { - assertThat(this.jdbcTemplate.queryForObject("select count(id) from DIRECTORY", - Integer.class)).isEqualTo(9); - assertThat(this.jdbcTemplate.queryForObject("select count(id) from FILE", - Integer.class)).isEqualTo(90); - assertThat(this.jdbcTemplate.queryForObject("select count(id) from ACL_SID", - Integer.class)).isEqualTo(4); // 3 users + 1 role - assertThat(this.jdbcTemplate.queryForObject("select count(id) from ACL_CLASS", - Integer.class)).isEqualTo(2); // Directory - // and - // File - assertThat(this.jdbcTemplate.queryForObject( - "select count(id) from ACL_OBJECT_IDENTITY", Integer.class)) - .isEqualTo(100); - assertThat(this.jdbcTemplate.queryForObject("select count(id) from ACL_ENTRY", - Integer.class)).isEqualTo(115); - } - - @Override - public void testMarissaRetrieval() { - process("rod", "koala", true); - } - - @Override - public void testScottRetrieval() { - process("scott", "wombat", true); - } - - @Override - public void testDianneRetrieval() { - process("dianne", "emu", true); - } -} diff --git a/samples/xml/dms/src/test/resources/logback-test.xml b/samples/xml/dms/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/dms/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/gae/README.adoc b/samples/xml/gae/README.adoc deleted file mode 100644 index fd3f646b98f..00000000000 --- a/samples/xml/gae/README.adoc +++ /dev/null @@ -1,5 +0,0 @@ -A sample using Google App Engine. To run it use: - ----- -$ ../../gradlew appengineRun ----- \ No newline at end of file diff --git a/samples/xml/gae/spring-security-samples-xml-gae.gradle b/samples/xml/gae/spring-security-samples-xml-gae.gradle deleted file mode 100644 index 01c5e86f456..00000000000 --- a/samples/xml/gae/spring-security-samples-xml-gae.gradle +++ /dev/null @@ -1,44 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' -apply plugin: 'com.google.cloud.tools.appengine' - -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.google.cloud.tools:appengine-gradle-plugin:1.3.5' - } -} - - -// Remove logback as it causes security issues with GAE. -configurations.runtime.exclude(group: 'ch.qos.logback') - -dependencies { - compile "com.google.appengine:appengine:$gaeVersion" - - compile project(':spring-security-core') - compile project(':spring-security-taglibs') - compile project(':spring-security-web') - compile jstlDependencies - compile slf4jDependencies - compile "com.google.appengine:appengine-api-1.0-sdk:$gaeVersion" - compile "com.google.appengine:appengine-api-stubs:$gaeVersion" - compile 'javax.validation:validation-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-context-support' - compile 'org.springframework:spring-web' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - - testCompile "com.google.appengine:appengine-testing:$gaeVersion" - - testRuntime "com.google.appengine:appengine-api-labs:$gaeVersion" -} - -appengineRun.onlyIf { !gradle.taskGraph.hasTask(appengineFunctionalTest) } diff --git a/samples/xml/gae/src/main/java/samples/gae/security/AppRole.java b/samples/xml/gae/src/main/java/samples/gae/security/AppRole.java deleted file mode 100644 index 84743e60449..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/security/AppRole.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import org.springframework.security.core.GrantedAuthority; - -/** - * @author Luke Taylor - */ -public enum AppRole implements GrantedAuthority { - ADMIN(0), NEW_USER(1), USER(2); - - private final int bit; - - /** - * Creates an authority with a specific bit representation. It's important that this - * doesn't change as it will be used in the database. The enum ordinal is less - * reliable as the enum may be reordered or have new roles inserted which would change - * the ordinal values. - * - * @param bit the permission bit which will represent this authority in the datastore. - */ - AppRole(int bit) { - this.bit = bit; - } - - public int getBit() { - return bit; - } - - public String getAuthority() { - return "ROLE_"+toString(); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/security/GaeAuthenticationFilter.java b/samples/xml/gae/src/main/java/samples/gae/security/GaeAuthenticationFilter.java deleted file mode 100644 index 838529efe30..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/security/GaeAuthenticationFilter.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.google.appengine.api.users.User; -import com.google.appengine.api.users.UserServiceFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AuthenticationDetailsSource; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.util.Assert; -import org.springframework.web.filter.GenericFilterBean; -import samples.gae.users.GaeUser; - -/** - * @author Luke Taylor - */ -public class GaeAuthenticationFilter extends GenericFilterBean { - private static final String REGISTRATION_URL = "/register.htm"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> ads = new WebAuthenticationDetailsSource(); - private AuthenticationManager authenticationManager; - private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); - - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { - Authentication authentication = SecurityContextHolder.getContext() - .getAuthentication(); - User googleUser = UserServiceFactory.getUserService().getCurrentUser(); - - if (authentication != null - && !loggedInUserMatchesGaeUser(authentication, googleUser)) { - SecurityContextHolder.clearContext(); - authentication = null; - ((HttpServletRequest) request).getSession().invalidate(); - } - - if (authentication == null) { - if (googleUser != null) { - logger.debug("Currently logged on to GAE as user " + googleUser); - logger.debug("Authenticating to Spring Security"); - // User has returned after authenticating via GAE. Need to authenticate - // through Spring Security. - PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( - googleUser, null); - token.setDetails(ads.buildDetails((HttpServletRequest) request)); - - try { - authentication = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - - if (authentication.getAuthorities().contains(AppRole.NEW_USER)) { - logger.debug("New user authenticated. Redirecting to registration page"); - ((HttpServletResponse) response).sendRedirect(REGISTRATION_URL); - - return; - } - - } - catch (AuthenticationException e) { - failureHandler.onAuthenticationFailure((HttpServletRequest) request, - (HttpServletResponse) response, e); - - return; - } - } - } - - chain.doFilter(request, response); - } - - private boolean loggedInUserMatchesGaeUser(Authentication authentication, - User googleUser) { - assert authentication != null; - - if (googleUser == null) { - // User has logged out of GAE but is still logged into application - return false; - } - - GaeUser gaeUser = (GaeUser) authentication.getPrincipal(); - - if (!gaeUser.getEmail().equals(googleUser.getEmail())) { - return false; - } - - return true; - - } - - @Override - public void afterPropertiesSet() { - Assert.notNull(authenticationManager, "AuthenticationManager must be set"); - } - - public void setAuthenticationManager(AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } - - public void setFailureHandler(AuthenticationFailureHandler failureHandler) { - this.failureHandler = failureHandler; - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/security/GaeUserAuthentication.java b/samples/xml/gae/src/main/java/samples/gae/security/GaeUserAuthentication.java deleted file mode 100644 index 961ea999520..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/security/GaeUserAuthentication.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import java.util.Collection; -import java.util.HashSet; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import samples.gae.users.GaeUser; - -/** - * Authentication object representing a fully-authenticated user. - * - * @author Luke Taylor - */ -public class GaeUserAuthentication implements Authentication { - private final GaeUser principal; - private final Object details; - private boolean authenticated; - - public GaeUserAuthentication(GaeUser principal, Object details) { - this.principal = principal; - this.details = details; - authenticated = true; - } - - public Collection<GrantedAuthority> getAuthorities() { - return new HashSet<>(principal.getAuthorities()); - } - - public Object getCredentials() { - throw new UnsupportedOperationException(); - } - - public Object getDetails() { - return null; - } - - public Object getPrincipal() { - return principal; - } - - public boolean isAuthenticated() { - return authenticated; - } - - public void setAuthenticated(boolean isAuthenticated) { - authenticated = isAuthenticated; - } - - public String getName() { - return principal.getUserId(); - } - - @Override - public String toString() { - return "GaeUserAuthentication{" + "principal=" + principal + ", details=" - + details + ", authenticated=" + authenticated + '}'; - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationEntryPoint.java b/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationEntryPoint.java deleted file mode 100644 index 4929c73cc4b..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationEntryPoint.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; - -import com.google.appengine.api.users.UserService; -import com.google.appengine.api.users.UserServiceFactory; - -public class GoogleAccountsAuthenticationEntryPoint implements AuthenticationEntryPoint { - - public void commence(HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException) throws IOException { - UserService userService = UserServiceFactory.getUserService(); - - response.sendRedirect(userService.createLoginURL(request.getRequestURI())); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationProvider.java b/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationProvider.java deleted file mode 100644 index 6a8f1a37ae9..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/security/GoogleAccountsAuthenticationProvider.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import com.google.appengine.api.users.User; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import samples.gae.users.GaeUser; -import samples.gae.users.UserRegistry; - -/** - * A simple authentication provider which interacts with {@code User} returned by the GAE - * {@code UserService}, and also the local persistent {@code UserRegistry} to build an - * application user principal. - * <p> - * If the user has been authenticated through google accounts, it will check if they are - * already registered and either load the existing user information or assign them a - * temporary identity with limited access until they have registered. - * <p> - * If the account has been disabled, a {@code DisabledException} will be raised. - * - * @author Luke Taylor - */ -public class GoogleAccountsAuthenticationProvider implements AuthenticationProvider, - MessageSourceAware { - protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - - private UserRegistry userRegistry; - - public Authentication authenticate(Authentication authentication) - throws AuthenticationException { - User googleUser = (User) authentication.getPrincipal(); - - GaeUser user = userRegistry.findUser(googleUser.getUserId()); - - if (user == null) { - // User not in registry. Needs to register - user = new GaeUser(googleUser.getUserId(), googleUser.getNickname(), - googleUser.getEmail()); - } - - if (!user.isEnabled()) { - throw new DisabledException("Account is disabled"); - } - - return new GaeUserAuthentication(user, authentication.getDetails()); - } - - /** - * Indicate that this provider only supports PreAuthenticatedAuthenticationToken - * (sub)classes. - */ - public final boolean supports(Class<?> authentication) { - return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication); - } - - public void setUserRegistry(UserRegistry userRegistry) { - this.userRegistry = userRegistry; - } - - public void setMessageSource(MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/users/GaeDatastoreUserRegistry.java b/samples/xml/gae/src/main/java/samples/gae/users/GaeDatastoreUserRegistry.java deleted file mode 100644 index ea5b5c29b4b..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/users/GaeDatastoreUserRegistry.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.users; - -import com.google.appengine.api.datastore.DatastoreService; -import com.google.appengine.api.datastore.DatastoreServiceFactory; -import com.google.appengine.api.datastore.Entity; -import com.google.appengine.api.datastore.EntityNotFoundException; -import com.google.appengine.api.datastore.Key; -import com.google.appengine.api.datastore.KeyFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import samples.gae.security.AppRole; - -import java.util.*; - -/** - * UserRegistry implementation which uses GAE's low-level Datastore APIs. - * - * @author Luke Taylor - */ -public class GaeDatastoreUserRegistry implements UserRegistry { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private static final String USER_TYPE = "GaeUser"; - private static final String USER_FORENAME = "forename"; - private static final String USER_SURNAME = "surname"; - private static final String USER_NICKNAME = "nickname"; - private static final String USER_EMAIL = "email"; - private static final String USER_ENABLED = "enabled"; - private static final String USER_AUTHORITIES = "authorities"; - - public GaeUser findUser(String userId) { - Key key = KeyFactory.createKey(USER_TYPE, userId); - DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); - - try { - Entity user = datastore.get(key); - - long binaryAuthorities = (Long) user.getProperty(USER_AUTHORITIES); - Set<AppRole> roles = EnumSet.noneOf(AppRole.class); - - for (AppRole r : AppRole.values()) { - if ((binaryAuthorities & (1 << r.getBit())) != 0) { - roles.add(r); - } - } - - GaeUser gaeUser = new GaeUser(user.getKey().getName(), - (String) user.getProperty(USER_NICKNAME), - (String) user.getProperty(USER_EMAIL), - (String) user.getProperty(USER_FORENAME), - (String) user.getProperty(USER_SURNAME), roles, - (Boolean) user.getProperty(USER_ENABLED)); - - return gaeUser; - - } - catch (EntityNotFoundException e) { - logger.debug(userId + " not found in datastore"); - return null; - } - } - - public void registerUser(GaeUser newUser) { - logger.debug("Attempting to create new user " + newUser); - - Key key = KeyFactory.createKey(USER_TYPE, newUser.getUserId()); - Entity user = new Entity(key); - user.setProperty(USER_EMAIL, newUser.getEmail()); - user.setProperty(USER_NICKNAME, newUser.getNickname()); - user.setProperty(USER_FORENAME, newUser.getForename()); - user.setProperty(USER_SURNAME, newUser.getSurname()); - user.setUnindexedProperty(USER_ENABLED, newUser.isEnabled()); - - Collection<AppRole> roles = newUser.getAuthorities(); - - long binaryAuthorities = 0; - - for (AppRole r : roles) { - binaryAuthorities |= 1 << r.getBit(); - } - - user.setUnindexedProperty(USER_AUTHORITIES, binaryAuthorities); - - DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); - datastore.put(user); - } - - public void removeUser(String userId) { - DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); - Key key = KeyFactory.createKey(USER_TYPE, userId); - - datastore.delete(key); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/users/GaeUser.java b/samples/xml/gae/src/main/java/samples/gae/users/GaeUser.java deleted file mode 100644 index 0d167aa5d7b..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/users/GaeUser.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.users; - -import java.io.Serializable; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Set; - -import samples.gae.security.AppRole; - -/** - * Custom user object for the application. - * - * @author Luke Taylor - */ -public class GaeUser implements Serializable { - private final String userId; - private final String email; - private final String nickname; - private final String forename; - private final String surname; - private final Set<AppRole> authorities; - private final boolean enabled; - - /** - * Pre-registration constructor. - * - * Assigns the user the "NEW_USER" role only. - */ - public GaeUser(String userId, String nickname, String email) { - this.userId = userId; - this.nickname = nickname; - this.authorities = EnumSet.of(AppRole.NEW_USER); - this.forename = null; - this.surname = null; - this.email = email; - this.enabled = true; - } - - /** - * Post-registration constructor - */ - public GaeUser(String userId, String nickname, String email, String forename, - String surname, Set<AppRole> authorities, boolean enabled) { - this.userId = userId; - this.nickname = nickname; - this.email = email; - this.authorities = authorities; - this.forename = forename; - this.surname = surname; - this.enabled = enabled; - } - - public String getUserId() { - return userId; - } - - public String getNickname() { - return nickname; - } - - public String getEmail() { - return email; - } - - public String getForename() { - return forename; - } - - public String getSurname() { - return surname; - } - - public boolean isEnabled() { - return enabled; - } - - public Collection<AppRole> getAuthorities() { - return authorities; - } - - @Override - public String toString() { - return "GaeUser{" + "userId='" + userId + '\'' + ", nickname='" + nickname + '\'' - + ", forename='" + forename + '\'' + ", surname='" + surname + '\'' - + ", authorities=" + authorities + '}'; - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/users/InMemoryUserRegistry.java b/samples/xml/gae/src/main/java/samples/gae/users/InMemoryUserRegistry.java deleted file mode 100644 index be1e481b3f6..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/users/InMemoryUserRegistry.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.users; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.Assert; - -/** - * @author Luke Taylor - */ -public class InMemoryUserRegistry implements UserRegistry { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final Map<String, GaeUser> users = Collections - .synchronizedMap(new HashMap<>()); - - public GaeUser findUser(String userId) { - return users.get(userId); - } - - public void registerUser(GaeUser newUser) { - logger.debug("Attempting to create new user " + newUser); - - Assert.isTrue(!users.containsKey(newUser.getUserId()), "user should not exist"); - - users.put(newUser.getUserId(), newUser); - } - - public void removeUser(String userId) { - users.remove(userId); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/users/UserRegistry.java b/samples/xml/gae/src/main/java/samples/gae/users/UserRegistry.java deleted file mode 100644 index 8a53378e547..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/users/UserRegistry.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.users; - -/** - * - * Service used to maintain a list of users who are registered with the application. - * - * @author Luke Taylor - */ -public interface UserRegistry { - - GaeUser findUser(String userId); - - void registerUser(GaeUser newUser); - - void removeUser(String userId); -} diff --git a/samples/xml/gae/src/main/java/samples/gae/validation/Forename.java b/samples/xml/gae/src/main/java/samples/gae/validation/Forename.java deleted file mode 100644 index 3028c73c3c9..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/validation/Forename.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.validation; - -import static java.lang.annotation.ElementType.*; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; - -/** - * @author Luke Taylor - */ -@Target({ METHOD, FIELD, ANNOTATION_TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = ForenameValidator.class) -public @interface Forename { - String message() default "{samples.gae.forename}"; - - Class<?>[] groups() default {}; - - Class<? extends Payload>[] payload() default {}; -} diff --git a/samples/xml/gae/src/main/java/samples/gae/validation/ForenameValidator.java b/samples/xml/gae/src/main/java/samples/gae/validation/ForenameValidator.java deleted file mode 100644 index 0e4d56bc313..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/validation/ForenameValidator.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.validation; - -import java.util.regex.Pattern; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -/** - * @author Luke Taylor - */ -public class ForenameValidator implements ConstraintValidator<Forename, String> { - private static final Pattern VALID = Pattern.compile("[\\p{L}'\\-,.]+"); - - public void initialize(Forename constraintAnnotation) { - } - - public boolean isValid(String value, ConstraintValidatorContext context) { - return VALID.matcher(value).matches(); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/validation/Surname.java b/samples/xml/gae/src/main/java/samples/gae/validation/Surname.java deleted file mode 100644 index 5f10e8f4c9e..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/validation/Surname.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.validation; - -import static java.lang.annotation.ElementType.*; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; - -/** - * @author Luke Taylor - */ -@Target({ METHOD, FIELD, ANNOTATION_TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = SurnameValidator.class) -public @interface Surname { - String message() default "{samples.gae.surname}"; - - Class<?>[] groups() default {}; - - Class<? extends Payload>[] payload() default {}; -} diff --git a/samples/xml/gae/src/main/java/samples/gae/validation/SurnameValidator.java b/samples/xml/gae/src/main/java/samples/gae/validation/SurnameValidator.java deleted file mode 100644 index a894922c7f4..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/validation/SurnameValidator.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.validation; - -import java.util.regex.Pattern; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -/** - * @author Luke Taylor - */ -public class SurnameValidator implements ConstraintValidator<Surname, String> { - private static final Pattern VALID = Pattern.compile("[\\p{L}'\\-,.]+"); - - public void initialize(Surname constraintAnnotation) { - } - - public boolean isValid(String value, ConstraintValidatorContext context) { - return VALID.matcher(value).matches(); - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/web/GaeAppController.java b/samples/xml/gae/src/main/java/samples/gae/web/GaeAppController.java deleted file mode 100644 index c19ffb41947..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/web/GaeAppController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.web; - -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.google.appengine.api.users.UserServiceFactory; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * - * @author Luke Taylor - * - */ -@Controller -public class GaeAppController { - - @RequestMapping(value = "/", method = RequestMethod.GET) - public String landing() { - return "landing"; - } - - @RequestMapping(value = "/home.htm", method = RequestMethod.GET) - public String home() { - return "home"; - } - - @RequestMapping(value = "/disabled.htm", method = RequestMethod.GET) - public String disabled() { - return "disabled"; - } - - @RequestMapping(value = "/logout.htm", method = RequestMethod.GET) - public void logout(HttpServletRequest request, HttpServletResponse response) - throws IOException { - request.getSession().invalidate(); - - String logoutUrl = UserServiceFactory.getUserService().createLogoutURL( - "/loggedout.htm"); - - response.sendRedirect(logoutUrl); - } - - @RequestMapping(value = "/loggedout.htm", method = RequestMethod.GET) - public String loggedOut() { - return "loggedout"; - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/web/RegistrationController.java b/samples/xml/gae/src/main/java/samples/gae/web/RegistrationController.java deleted file mode 100644 index 0a535a2a237..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/web/RegistrationController.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.web; - -import java.util.EnumSet; -import java.util.Set; - -import javax.validation.Valid; - -import com.google.appengine.api.users.UserServiceFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Controller; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import samples.gae.security.AppRole; -import samples.gae.security.GaeUserAuthentication; -import samples.gae.users.GaeUser; -import samples.gae.users.UserRegistry; - -/** - * @author Luke Taylor - */ -@Controller -@RequestMapping(value = "/register.htm") -public class RegistrationController { - - @Autowired - private UserRegistry registry; - - @RequestMapping(method = RequestMethod.GET) - public RegistrationForm registrationForm() { - return new RegistrationForm(); - } - - @RequestMapping(method = RequestMethod.POST) - public String register(@Valid RegistrationForm form, BindingResult result) { - if (result.hasErrors()) { - return null; - } - - Authentication authentication = SecurityContextHolder.getContext() - .getAuthentication(); - GaeUser currentUser = (GaeUser) authentication.getPrincipal(); - Set<AppRole> roles = EnumSet.of(AppRole.USER); - - if (UserServiceFactory.getUserService().isUserAdmin()) { - roles.add(AppRole.ADMIN); - } - - GaeUser user = new GaeUser(currentUser.getUserId(), currentUser.getNickname(), - currentUser.getEmail(), form.getForename(), form.getSurname(), roles, - true); - - registry.registerUser(user); - - // Update the context with the full authentication - SecurityContextHolder.getContext().setAuthentication( - new GaeUserAuthentication(user, authentication.getDetails())); - - return "redirect:/home.htm"; - } -} diff --git a/samples/xml/gae/src/main/java/samples/gae/web/RegistrationForm.java b/samples/xml/gae/src/main/java/samples/gae/web/RegistrationForm.java deleted file mode 100644 index 6001e532f0d..00000000000 --- a/samples/xml/gae/src/main/java/samples/gae/web/RegistrationForm.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.web; - -import samples.gae.validation.Forename; -import samples.gae.validation.Surname; - -/** - * @author Luke Taylor - */ -public class RegistrationForm { - @Forename - private String forename; - @Surname - private String surname; - - public String getForename() { - return forename; - } - - public void setForename(String forename) { - this.forename = forename; - } - - public String getSurname() { - return surname; - } - - public void setSurname(String surname) { - this.surname = surname; - } -} diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/appengine-web.xml b/samples/xml/gae/src/main/webapp/WEB-INF/appengine-web.xml deleted file mode 100644 index 7dbd5e98200..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/appengine-web.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> - <application>gaespringsec</application> - <version>1</version> - <sessions-enabled>true</sessions-enabled> - <threadsafe>true</threadsafe> - - <system-properties> - <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> - </system-properties> -</appengine-web-app> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/gae/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index 7153f5058fc..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<b:beans xmlns="http://www.springframework.org/schema/security" - xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <http pattern="/_ah/**" security="none"/> - <http pattern="/static/**" security="none" /> - <http pattern="/favicon.ico" security="none" /> - <http pattern="/loggedout.htm" security="none" /> - <http pattern="/disabled.htm" security="none" /> - - <http use-expressions="true" entry-point-ref="gaeEntryPoint"> - <intercept-url pattern="/" access="permitAll" /> - <intercept-url pattern="/logout.htm" access="permitAll" /> - <intercept-url pattern="/register.htm*" access="hasRole('NEW_USER')" /> - <intercept-url pattern="/**" access="hasRole('USER')" /> - <custom-filter position="PRE_AUTH_FILTER" ref="gaeFilter" /> - </http> - - <b:bean id="gaeEntryPoint" class="samples.gae.security.GoogleAccountsAuthenticationEntryPoint" /> - - <b:bean id="gaeFilter" class="samples.gae.security.GaeAuthenticationFilter"> - <b:property name="authenticationManager" ref="authenticationManager"/> - <b:property name="failureHandler"> - <b:bean class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler"> - <b:property name="exceptionMappings"> - <b:map> - <b:entry key="org.springframework.security.authentication.DisabledException" value="/disabled.htm" /> - </b:map> - </b:property> - </b:bean> - </b:property> - </b:bean> - - <authentication-manager alias="authenticationManager"> - <authentication-provider ref="gaeAuthenticationProvider"/> - </authentication-manager> - - <b:bean id="gaeAuthenticationProvider" class="samples.gae.security.GoogleAccountsAuthenticationProvider"> - <b:property name="userRegistry" ref="userRegistry" /> - </b:bean> - - <b:bean id="userRegistry" class="samples.gae.users.GaeDatastoreUserRegistry" /> - -</b:beans> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/classes/ValidationMessages.properties b/samples/xml/gae/src/main/webapp/WEB-INF/classes/ValidationMessages.properties deleted file mode 100644 index d7285505956..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/classes/ValidationMessages.properties +++ /dev/null @@ -1,2 +0,0 @@ -samples.gae.forename=Must be a valid forename -samples.gae.surname=Must be a valid surname diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/gae-servlet.xml b/samples/xml/gae/src/main/webapp/WEB-INF/gae-servlet.xml deleted file mode 100644 index 573eef4dd7d..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/gae-servlet.xml +++ /dev/null @@ -1,25 +0,0 @@ -<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" - xmlns:mvc="http://www.springframework.org/schema/mvc" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd - http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> - - <!-- Enables JSR-303 --> - <mvc:annotation-driven/> - - <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> - - <context:component-scan base-package="samples.gae"/> - <context:annotation-config /> - - <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> - <property name="basename" value="messages"/> - </bean> - - <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> - <property name="prefix" value="/WEB-INF/jsp/"/> - <property name="suffix" value=".jsp"/> - </bean> - -</beans> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/disabled.jsp b/samples/xml/gae/src/main/webapp/WEB-INF/jsp/disabled.jsp deleted file mode 100644 index 390c0f008e9..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/disabled.jsp +++ /dev/null @@ -1,16 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<%@page session="false" %> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="/static/css/gae.css" type="text/css" /> - <title>Disabled Account</title></head> - <body> - <div id="content"> - <p>Sorry, it looks like your account has been disabled for some reason...</p> - </div> - </body> - -</html> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/home.jsp b/samples/xml/gae/src/main/webapp/WEB-INF/jsp/home.jsp deleted file mode 100644 index eb7bf065b74..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/home.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%@ page import="com.google.appengine.api.users.UserServiceFactory" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="/static/css/gae.css" type="text/css" /> - <title>Home Page</title> - </head> - <body> - <div id="content"> - <h3>The Home Page</h3> - <p>Welcome back <sec:authentication property="principal.nickname"/>.</p> - <p> - You can get to this page if you have authenticated and are a registered user. - You are registered as - <sec:authentication property="principal.forename"/> <sec:authentication property="principal.surname"/>. - </p> - <p> - <a href="/logout.htm">Logout</a>. - </p> - </div> - </body> -</html> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/landing.jsp b/samples/xml/gae/src/main/webapp/WEB-INF/jsp/landing.jsp deleted file mode 100644 index f9b305203d4..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/landing.jsp +++ /dev/null @@ -1,33 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@page session="false" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="/static/css/gae.css" type="text/css" /> - <title>Spring Security GAE Sample</title> - </head> - - <body> - <div id="content"> - <h3>Spring Security GAE Application</h3> - - <p> - This application demonstrates the integration of Spring Security - with the services provided by Google App Engine. It shows how to: - <ul> - <li>Authenticate using Google Accounts.</li> - <li>Implement "on–demand" authentication when a user accesses a secured resource.</li> - <li>Supplement the information from Google Accounts with application–specific roles.</li> - <li>Store user account data in an App Engine datastore using the native API.</li> - <li>Setup access-control restrictions based on the roles assigned to users.</li> - <li>Disable the accounts of specfic users to prevent access.</li> - </ul> - </p> - <p> - Go to the <a href="/home.htm">home page</a>. - </p> - </div> - </body> -</html> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/loggedout.jsp b/samples/xml/gae/src/main/webapp/WEB-INF/jsp/loggedout.jsp deleted file mode 100644 index 4bfb44537f8..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/loggedout.jsp +++ /dev/null @@ -1,17 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@page session="false" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="/static/css/gae.css" type="text/css" /> - <title>Spring Security GAE Sample</title> - </head> - - <body> - <div id="content"> - <p>You've been logged out of the application. <a href="/home.htm">Log back in</a>.</p> - </div> - </body> -</html> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/register.jsp b/samples/xml/gae/src/main/webapp/WEB-INF/jsp/register.jsp deleted file mode 100644 index 8aaf625afe3..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/jsp/register.jsp +++ /dev/null @@ -1,40 +0,0 @@ -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> -<meta http-equiv="content-type" content="text/html; charset=UTF-8"> -<link rel="stylesheet" href="/static/css/gae.css" type="text/css" /> -<title>Registration</title> -</head> -<body> -<div id="content"> -<p> -Welcome to the Spring Security GAE sample application, <sec:authentication property="principal.nickname" />. -Please enter your registration details in order to use the application. -</p> -<p> -The data you enter here will be registered in the application's GAE data store, keyed under your unique -Google Accounts identifier. It doesn't have to be accurate. When you log in again, the information will be automatically -retrieved. -</p> - -<form:form id="register" method="post" modelAttribute="registrationForm"> - <fieldset> - <form:label path="forename"> - Forename: - </form:label> <form:errors path="forename" cssClass="fieldError" /><br /> - <form:input path="forename" /> <br /> - - <form:label path="surname"> - Surname: - </form:label><form:errors path="surname" cssClass="fieldError" /> <br /> - <form:input path="surname" /><br /> - </fieldset> - <input type="submit" value="Register"> -</form:form> -</body> -</div> -</html> diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/logging.properties b/samples/xml/gae/src/main/webapp/WEB-INF/logging.properties deleted file mode 100644 index 204b09d9c2e..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/logging.properties +++ /dev/null @@ -1,5 +0,0 @@ -.level = INFO - -samples.gae.level = FINER -org.springframework.level = INFO -org.springframework.security.level = FINER diff --git a/samples/xml/gae/src/main/webapp/WEB-INF/web.xml b/samples/xml/gae/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index c123a5fef68..00000000000 --- a/samples/xml/gae/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:web="https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> - - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <servlet> - <servlet-name>gae</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <init-param> - <param-name>contextConfigLocation</param-name> - <param-value>/WEB-INF/gae-servlet.xml</param-value> - </init-param> - </servlet> - - <servlet-mapping> - <servlet-name>gae</servlet-name> - <url-pattern>/</url-pattern> - </servlet-mapping> - - <servlet-mapping> - <servlet-name>gae</servlet-name> - <url-pattern>*.htm</url-pattern> - </servlet-mapping> - -</web-app> diff --git a/samples/xml/gae/src/main/webapp/favicon.ico b/samples/xml/gae/src/main/webapp/favicon.ico deleted file mode 100644 index 54371328f65..00000000000 Binary files a/samples/xml/gae/src/main/webapp/favicon.ico and /dev/null differ diff --git a/samples/xml/gae/src/main/webapp/static/css/gae.css b/samples/xml/gae/src/main/webapp/static/css/gae.css deleted file mode 100644 index 40051c14afc..00000000000 --- a/samples/xml/gae/src/main/webapp/static/css/gae.css +++ /dev/null @@ -1,26 +0,0 @@ - -body { - font-family:"Palatino Linotype","Book Antiqua",Palatino,serif; -} - -#content { - margin: 5em auto; - width: 40em; -} - -form { - width: 25em; - margin: 0 2em; -} - -form fieldset { - margin-bottom: 0.5em; -} - -fieldset input { - margin: 0.6em 0; -} - -.fieldError { - color: red; -} diff --git a/samples/xml/gae/src/test/java/samples/gae/security/AppRoleTests.java b/samples/xml/gae/src/test/java/samples/gae/security/AppRoleTests.java deleted file mode 100644 index 8db7aea192c..00000000000 --- a/samples/xml/gae/src/test/java/samples/gae/security/AppRoleTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2017 the original author or authors. - * - * 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 - * - * https://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 samples.gae.security; - -import static org.assertj.core.api.Assertions.*; -import static samples.gae.security.AppRole.*; - -import org.junit.Test; -import org.springframework.security.core.GrantedAuthority; - -/** - * @author Luke Taylor - */ -public class AppRoleTests { - - @Test - public void getAuthorityReturnsRoleName() { - GrantedAuthority admin = ADMIN; - - assertThat(admin.getAuthority()).isEqualTo("ROLE_ADMIN"); - } - - @Test - public void bitsAreCorrect() { - // If this fails, someone has modified the Enum and the Datastore is probably - // corrupt! - assertThat(ADMIN.getBit()).isZero(); - assertThat(NEW_USER.getBit()).isEqualTo(1); - assertThat(USER.getBit()).isEqualTo(2); - } -} diff --git a/samples/xml/gae/src/test/java/samples/gae/users/GaeDataStoreUserRegistryTests.java b/samples/xml/gae/src/test/java/samples/gae/users/GaeDataStoreUserRegistryTests.java deleted file mode 100644 index 69e24d47a74..00000000000 --- a/samples/xml/gae/src/test/java/samples/gae/users/GaeDataStoreUserRegistryTests.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 samples.gae.users; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.EnumSet; -import java.util.Set; - -import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig; -import com.google.appengine.tools.development.testing.LocalServiceTestHelper; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import samples.gae.security.AppRole; - -/** - * @author Luke Taylor - */ -public class GaeDataStoreUserRegistryTests { - private final LocalServiceTestHelper helper = new LocalServiceTestHelper( - new LocalDatastoreServiceTestConfig()); - - @Before - public void setUp() { - helper.setUp(); - } - - @After - public void tearDown() { - helper.tearDown(); - } - - @Test - public void correctDataIsRetrievedAfterInsert() { - GaeDatastoreUserRegistry registry = new GaeDatastoreUserRegistry(); - - Set<AppRole> roles = EnumSet.of(AppRole.ADMIN, AppRole.USER); - String userId = "someUserId"; - - GaeUser origUser = new GaeUser(userId, "nick", "nick@blah.com", "Forename", - "Surname", roles, true); - - registry.registerUser(origUser); - - GaeUser loadedUser = registry.findUser(userId); - - assertThat(origUser.getUserId()).isEqualTo(loadedUser.getUserId()); - assertThat(loadedUser.isEnabled()).isEqualTo(true); - assertThat(loadedUser.getAuthorities()).isEqualTo(roles); - assertThat(loadedUser.getNickname()).isEqualTo("nick"); - assertThat(loadedUser.getEmail()).isEqualTo("nick@blah.com"); - assertThat(loadedUser.getForename()).isEqualTo("Forename"); - assertThat(loadedUser.getSurname()).isEqualTo("Surname"); - } -} diff --git a/samples/xml/gae/src/test/resources/logback-test.xml b/samples/xml/gae/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/gae/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/helloworld/spring-security-samples-xml-helloworld.gradle b/samples/xml/helloworld/spring-security-samples-xml-helloworld.gradle deleted file mode 100644 index 5bd5b1fd09f..00000000000 --- a/samples/xml/helloworld/spring-security-samples-xml-helloworld.gradle +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-config') - compile project(':spring-security-web') - compile jstlDependencies - compile slf4jDependencies - compile 'javax.servlet.jsp:javax.servlet.jsp-api' - - providedCompile 'javax.servlet:javax.servlet-api' - - testCompile project(':spring-security-test') - testCompile 'org.skyscreamer:jsonassert' - testCompile 'org.springframework:spring-test' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldXmlTests.java b/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldXmlTests.java deleted file mode 100644 index 68abc3a1733..00000000000 --- a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldXmlTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; - -/** - * @author Michael Simons - */ -public class HelloWorldXmlTests { - - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit(); - homePage - .assertAt() - .andTheUserNameIsDisplayed(); - } - - @Test - public void authenticatedUserLogsOut() { - LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("password") - .submit() - .logout(); - loginPage.assertAt(); - - loginPage = HomePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index ed527c39577..00000000000 --- a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - @FindBy(css = "p") - private WebElement message; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, LoginPage.class); - } - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Hello Security"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andTheUserNameIsDisplayed() { - assertThat(message.getText()).isEqualTo("Hello user"); - return this; - } - } -} diff --git a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 30822ea2a94..00000000000 --- a/samples/xml/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public HomePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, HomePage.class); - } - } -} diff --git a/samples/xml/helloworld/src/main/resources/logback.xml b/samples/xml/helloworld/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/helloworld/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/helloworld/src/main/webapp/META-INF/MANIFEST.MF b/samples/xml/helloworld/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/xml/helloworld/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/xml/helloworld/src/main/webapp/WEB-INF/spring/security.xml b/samples/xml/helloworld/src/main/webapp/WEB-INF/spring/security.xml deleted file mode 100644 index f006442e929..00000000000 --- a/samples/xml/helloworld/src/main/webapp/WEB-INF/spring/security.xml +++ /dev/null @@ -1,11 +0,0 @@ -<b:beans xmlns="http://www.springframework.org/schema/security" - xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - <http /> - - <user-service> - <user name="user" password="{noop}password" authorities="ROLE_USER" /> - </user-service> -</b:beans> diff --git a/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml b/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 5e99e514200..00000000000 --- a/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee - https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/spring/*.xml - </param-value> - </context-param> - - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> -</web-app> diff --git a/samples/xml/helloworld/src/main/webapp/index.jsp b/samples/xml/helloworld/src/main/webapp/index.jsp deleted file mode 100644 index 8b4d7a39830..00000000000 --- a/samples/xml/helloworld/src/main/webapp/index.jsp +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.0"> - - <jsp:directive.page contentType="text/html" pageEncoding="UTF-8" /> - <jsp:output omit-xml-declaration="true" /> - <jsp:output doctype-root-element="HTML" - doctype-system="about:legacy-compat" /> -<html lang="en"> - <head> - <title>Hello Security</title> - <c:url var="faviconUrl" value="/resources/img/favicon.ico"/> - <link rel="icon" type="image/x-icon" href="${faviconUrl}"/> - <c:url var="bootstrapUrl" value="/resources/css/bootstrap.css"/> - <link href="${bootstrapUrl}" rel="stylesheet"></link> - <c:url var="bootstrapResponsiveUrl" value="/resources/css/bootstrap-responsive.css"/> - <link href="${bootstrapResponsiveUrl}" rel="stylesheet"></link> - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - </head> - - <body> - <div class="container"> - <h1>This is secured!</h1> - <p> - Hello <b><c:out value="${pageContext.request.remoteUser}"/></b> - </p> - <c:url var="logoutUrl" value="/logout"/> - <form class="form-inline" action="${logoutUrl}" method="post"> - <input type="submit" value="Log out" /> - <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> - </form> - </div> - </body> -</html> -</jsp:root> diff --git a/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css b/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css deleted file mode 100644 index ba09bc8777b..00000000000 --- a/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap-responsive.css +++ /dev/null @@ -1,1092 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -@-ms-viewport { - width: device-width; -} - -.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; -} - -.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; - } -} - -@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 .dropdown-menu a:hover { - 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 .dropdown-menu a:hover { - 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/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap.css b/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap.css deleted file mode 100644 index 22aa0c17a90..00000000000 --- a/samples/xml/helloworld/src/main/webapp/resources/css/bootstrap.css +++ /dev/null @@ -1,6039 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -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; - } -} - -.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; -} - -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 { - 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 { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover { - color: #356635; -} - -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; - padding-right: 5px; - padding-left: 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; - 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: 16px; - font-weight: 300; - line-height: 25px; -} - -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 { - margin-bottom: 5px; - 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 { - 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 { - -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 { - -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 tfoot:last-child tr:last-child > td: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 tfoot:last-child tr:last-child > td: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/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:hover > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:hover > 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 { - 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 { - 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 { - 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 { - color: #999999; -} - -.dropdown-menu .disabled > a:hover { - 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; -} - -.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 { - 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 #bbbbbb; - *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: #a2a2a2; - -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: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 { - 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 { - border-color: #c5c5c5; - border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); -} - -.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: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: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: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: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: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: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 { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover { - 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-mini .caret, -.btn-small .caret, -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.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 { - 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 { - 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 { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover { - 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 { - 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 { - 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 { - 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 { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover { - 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 { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover { - 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 { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover { - 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 { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover { - 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 { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover { - 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 { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover { - 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 { - 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: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 { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.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 { - 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 { - 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 { - 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: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 > .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 { - 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 { - 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 > 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; - padding: 5px; - font-size: 11px; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - margin-top: -3px; -} - -.tooltip.right { - margin-left: 3px; -} - -.tooltip.bottom { - margin-top: 3px; -} - -.tooltip.left { - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 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; - width: 236px; - 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-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 { - 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.badge:hover { - 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 { - 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 { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.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/samples/xml/helloworld/src/main/webapp/resources/img/favicon.ico b/samples/xml/helloworld/src/main/webapp/resources/img/favicon.ico deleted file mode 100644 index bfb9974019d..00000000000 Binary files a/samples/xml/helloworld/src/main/webapp/resources/img/favicon.ico and /dev/null differ diff --git a/samples/xml/helloworld/src/main/webapp/resources/img/logo.png b/samples/xml/helloworld/src/main/webapp/resources/img/logo.png deleted file mode 100644 index 393230883fb..00000000000 Binary files a/samples/xml/helloworld/src/main/webapp/resources/img/logo.png and /dev/null differ diff --git a/samples/xml/insecure/spring-security-samples-xml-insecure.gradle b/samples/xml/insecure/spring-security-samples-xml-insecure.gradle deleted file mode 100644 index 26fc7ac5b48..00000000000 --- a/samples/xml/insecure/spring-security-samples-xml-insecure.gradle +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ - -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile jstlDependencies - compile slf4jDependencies - compile 'javax.xml.bind:jaxb-api' - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/HelloInsecureTests.java b/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/HelloInsecureTests.java deleted file mode 100644 index fb6231a809e..00000000000 --- a/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/HelloInsecureTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; - -/** - * @author Michael Simons - */ -public class HelloInsecureTests { - - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void theHomePageIsAccessible() { - HomePage.to(this.driver, this.port) - .assertAt() - .andWeCanSeeTheMessage(); - } -} diff --git a/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 9211300f625..00000000000 --- a/samples/xml/insecure/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Michael Simons - */ -public class HomePage { - private final WebDriver webDriver; - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public static HomePage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, HomePage.class); - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Hello World"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andWeCanSeeTheMessage() { - assertThat(message.getText()).isEqualTo("We would like to secure this page"); - return this; - } - } -} diff --git a/samples/xml/insecure/src/main/java/README.adoc b/samples/xml/insecure/src/main/java/README.adoc deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/samples/xml/insecure/src/main/webapp/index.jsp b/samples/xml/insecure/src/main/webapp/index.jsp deleted file mode 100644 index 431136a5034..00000000000 --- a/samples/xml/insecure/src/main/webapp/index.jsp +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" - xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.0"> - - <jsp:directive.page contentType="text/html" pageEncoding="UTF-8" /> - <jsp:output omit-xml-declaration="true" /> - <jsp:output doctype-root-element="HTML" - doctype-system="about:legacy-compat" /> -<html lang="en"> - <head> - <title>Hello World</title> - <c:url var="faviconUrl" value="/resources/img/favicon.ico"/> - <link rel="icon" type="image/x-icon" href="${faviconUrl}"/> - <c:url var="bootstrapUrl" value="/resources/css/bootstrap.css"/> - <link href="${bootstrapUrl}" rel="stylesheet"></link> - <c:url var="bootstrapResponsiveUrl" value="/resources/css/bootstrap-responsive.css"/> - <link href="${bootstrapResponsiveUrl}" rel="stylesheet"></link> - <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> - <!--[if lt IE 9]> - <script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> - </head> - - <body> - <div class="container"> - <h1>TODO Secure this</h1> - <p>We would like to secure this page</p> - </div> -</body> -</html> -</jsp:root> diff --git a/samples/xml/insecure/src/main/webapp/resources/css/bootstrap-responsive.css b/samples/xml/insecure/src/main/webapp/resources/css/bootstrap-responsive.css deleted file mode 100644 index ba09bc8777b..00000000000 --- a/samples/xml/insecure/src/main/webapp/resources/css/bootstrap-responsive.css +++ /dev/null @@ -1,1092 +0,0 @@ -/*! - * Bootstrap Responsive v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -@-ms-viewport { - width: device-width; -} - -.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; -} - -.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; - } -} - -@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 .dropdown-menu a:hover { - 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 .dropdown-menu a:hover { - 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/samples/xml/insecure/src/main/webapp/resources/css/bootstrap.css b/samples/xml/insecure/src/main/webapp/resources/css/bootstrap.css deleted file mode 100644 index 22aa0c17a90..00000000000 --- a/samples/xml/insecure/src/main/webapp/resources/css/bootstrap.css +++ /dev/null @@ -1,6039 +0,0 @@ -/*! - * Bootstrap v2.2.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ - -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; - } -} - -.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; -} - -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 { - 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 { - color: #808080; -} - -.text-warning { - color: #c09853; -} - -a.text-warning:hover { - color: #a47e3c; -} - -.text-error { - color: #b94a48; -} - -a.text-error:hover { - color: #953b39; -} - -.text-info { - color: #3a87ad; -} - -a.text-info:hover { - color: #2d6987; -} - -.text-success { - color: #468847; -} - -a.text-success:hover { - color: #356635; -} - -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; - padding-right: 5px; - padding-left: 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; - 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: 16px; - font-weight: 300; - line-height: 25px; -} - -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 { - margin-bottom: 5px; - 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 { - 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 { - -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 { - -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 tfoot:last-child tr:last-child > td: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 tfoot:last-child tr:last-child > td: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/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:hover > [class*=" icon-"], -.dropdown-menu > .active > a > [class^="icon-"], -.dropdown-menu > .active > a > [class*=" icon-"], -.dropdown-submenu:hover > a > [class^="icon-"], -.dropdown-submenu:hover > 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 { - 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 { - 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 { - 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 { - color: #999999; -} - -.dropdown-menu .disabled > a:hover { - 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; -} - -.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 { - 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 #bbbbbb; - *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: #a2a2a2; - -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: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 { - 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 { - border-color: #c5c5c5; - border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); -} - -.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: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: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: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: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: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: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 { - color: #005580; - text-decoration: underline; - background-color: transparent; -} - -.btn-link[disabled]:hover { - 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-mini .caret, -.btn-small .caret, -.btn-large .caret { - margin-top: 6px; -} - -.btn-large .caret { - border-top-width: 5px; - border-right-width: 5px; - border-left-width: 5px; -} - -.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 { - 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 { - 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 { - border-color: #eeeeee #eeeeee #dddddd; -} - -.nav-tabs > .active > a, -.nav-tabs > .active > a:hover { - 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 { - 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 { - 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 { - 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 { - cursor: pointer; -} - -.nav-tabs .open .dropdown-toggle, -.nav-pills .open .dropdown-toggle, -.nav > li.dropdown.open.active > a:hover { - 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 { - border-top-color: #ffffff; - border-bottom-color: #ffffff; - opacity: 1; - filter: alpha(opacity=100); -} - -.tabs-stacked .open > a:hover { - 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 { - border-top-color: #ddd; - border-bottom-color: transparent; -} - -.tabs-below > .nav-tabs > .active > a, -.tabs-below > .nav-tabs > .active > a:hover { - 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 { - border-color: #eeeeee #dddddd #eeeeee #eeeeee; -} - -.tabs-left > .nav-tabs .active > a, -.tabs-left > .nav-tabs .active > a:hover { - 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 { - border-color: #eeeeee #eeeeee #eeeeee #dddddd; -} - -.tabs-right > .nav-tabs .active > a, -.tabs-right > .nav-tabs .active > a:hover { - border-color: #ddd #ddd #ddd transparent; - *border-left-color: #ffffff; -} - -.nav > .disabled > a { - color: #999999; -} - -.nav > .disabled > a:hover { - 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 { - text-decoration: none; -} - -.navbar-text { - margin-bottom: 0; - line-height: 40px; - color: #777777; -} - -.navbar-link { - color: #777777; -} - -.navbar-link:hover { - 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 { - 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: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 { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.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 { - 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 { - 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 { - 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: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 > .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 { - 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 { - 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 > 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; - padding: 5px; - font-size: 11px; - opacity: 0; - filter: alpha(opacity=0); - visibility: visible; -} - -.tooltip.in { - opacity: 0.8; - filter: alpha(opacity=80); -} - -.tooltip.top { - margin-top: -3px; -} - -.tooltip.right { - margin-left: 3px; -} - -.tooltip.bottom { - margin-top: 3px; -} - -.tooltip.left { - margin-left: -3px; -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 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; - width: 236px; - 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-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 { - 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.badge:hover { - 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 { - 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 { - color: #ffffff; - text-decoration: none; - opacity: 0.9; - filter: alpha(opacity=90); -} - -.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/samples/xml/insecure/src/main/webapp/resources/img/favicon.ico b/samples/xml/insecure/src/main/webapp/resources/img/favicon.ico deleted file mode 100644 index bfb9974019d..00000000000 Binary files a/samples/xml/insecure/src/main/webapp/resources/img/favicon.ico and /dev/null differ diff --git a/samples/xml/insecure/src/main/webapp/resources/img/logo.png b/samples/xml/insecure/src/main/webapp/resources/img/logo.png deleted file mode 100644 index 393230883fb..00000000000 Binary files a/samples/xml/insecure/src/main/webapp/resources/img/logo.png and /dev/null differ diff --git a/samples/xml/insecuremvc/spring-security-samples-xml-insecuremvc.gradle b/samples/xml/insecuremvc/spring-security-samples-xml-insecuremvc.gradle deleted file mode 100644 index 5a5a2de9524..00000000000 --- a/samples/xml/insecuremvc/spring-security-samples-xml-insecuremvc.gradle +++ /dev/null @@ -1,17 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-samples-javaconfig-messages') - compile slf4jDependencies - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'javax.validation:validation-api' - compile 'javax.xml.bind:jaxb-api' - compile 'org.hibernate:hibernate-validator' - compile 'org.springframework:spring-jdbc' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' - - runtime 'opensymphony:sitemesh' -} diff --git a/samples/xml/insecuremvc/src/main/java/README.adoc b/samples/xml/insecuremvc/src/main/java/README.adoc deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/samples/xml/insecuremvc/src/main/webapp/META-INF/MANIFEST.MF b/samples/xml/insecuremvc/src/main/webapp/META-INF/MANIFEST.MF deleted file mode 100644 index 58630c02ef4..00000000000 --- a/samples/xml/insecuremvc/src/main/webapp/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/samples/xml/insecuremvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/xml/insecuremvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java deleted file mode 100644 index ec2d3f088e0..00000000000 --- a/samples/xml/insecuremvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.config; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.samples.mvc.config.WebMvcConfiguration; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -/** - * @author Rob Winch - * - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class }) -@WebAppConfiguration -public class SecurityConfigTests { - private MockMvc mvc; - - @Autowired - private WebApplicationContext context; - - @Before - public void setup() { - mvc = MockMvcBuilders.webAppContextSetup(context).build(); - } - - @Test - public void composeMessage() throws Exception { - MockHttpServletRequestBuilder composeMessage = post("/").param("summary", - "New Message").param("text", "This is a new message"); - - mvc.perform(composeMessage).andExpect(redirectedUrlPattern("/*")); - } -} diff --git a/samples/xml/insecuremvc/src/test/resources/logback-test.xml b/samples/xml/insecuremvc/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/insecuremvc/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/jaas/spring-security-samples-xml-jaas.gradle b/samples/xml/jaas/spring-security-samples-xml-jaas.gradle deleted file mode 100644 index a986704ef26..00000000000 --- a/samples/xml/jaas/spring-security-samples-xml-jaas.gradle +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-core') - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-web' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-taglibs') - runtime project(':spring-security-web') - runtime jstlDependencies - runtime slf4jDependencies - - integrationTestCompile seleniumDependencies -} diff --git a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/JaasXmlTests.java b/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/JaasXmlTests.java deleted file mode 100644 index a6345a82215..00000000000 --- a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/JaasXmlTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; -import org.springframework.security.samples.pages.LogoutPage; -import org.springframework.security.samples.pages.SecurePage; - -/** - * @author Michael Simons - */ -public class JaasXmlTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomePageWithUnauthenticatedWorks() { - final HomePage homePage = HomePage.to(this.driver, this.port); - homePage.assertAt(); - } - - @Test - public void accessSecurePageWithUnauthenticatedRequiresLogin() { - final LoginPage loginPage = SecurePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final SecurePage securePage = SecurePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("user") - .submit(); - securePage - .assertAt(); - } - - @Test - public void authenticatedUserLogsOut() { - final LogoutPage logoutPage = SecurePage.to(this.driver, this.port) - .loginForm() - .username("user") - .password("user") - .submit() - .logout(); - logoutPage.assertAt(); - - final LoginPage loginPage = SecurePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 69625fe8d22..00000000000 --- a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The home page. - * - * @author Michael Simons - */ -public class HomePage { - - public static HomePage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, HomePage.class); - } - - private final WebDriver webDriver; - - @FindBy(css = "p") - private WebElement message; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Home Page"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andTheUserNameIsDisplayed() { - assertThat(message.getText()).isEqualTo("Hello user"); - return this; - } - } -} diff --git a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 9aa9fea10f3..00000000000 --- a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The login page. - * - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public SecurePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, SecurePage.class); - } - } -} diff --git a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java b/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java deleted file mode 100644 index 819ad73e044..00000000000 --- a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Logout Page is the same as login page with an additional message. - * - * @author Michael Simons - */ -public class LogoutPage extends LoginPage { - @FindBy(css = "div[role=alert]") - private WebElement alert; - - public LogoutPage(WebDriver webDriver) { - super(webDriver); - } - - @Override - public LogoutPage assertAt() { - super.assertAt(); - - assertThat(this.alert.getText()).isEqualTo("You have been signed out"); - return this; - } -} diff --git a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java b/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java deleted file mode 100644 index d2e067cb237..00000000000 --- a/samples/xml/jaas/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * A secure page. - * - * @author Michael Simons - */ -public class SecurePage { - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port + "/secure"); - return PageFactory.initElements(driver, LoginPage.class); - } - - private final WebDriver webDriver; - - @FindBy(css = "input[type=submit]") - private WebElement submit; - - public SecurePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public SecurePage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Security Debug Information"); - return this; - } - - public LogoutPage logout() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, LogoutPage.class); - } -} diff --git a/samples/xml/jaas/src/main/java/samples/jaas/RoleUserAuthorityGranter.java b/samples/xml/jaas/src/main/java/samples/jaas/RoleUserAuthorityGranter.java deleted file mode 100644 index 8f1fee98414..00000000000 --- a/samples/xml/jaas/src/main/java/samples/jaas/RoleUserAuthorityGranter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2016 the original author or authors. - * - * 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 - * - * https://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 samples.jaas; - -import java.security.Principal; -import java.util.Collections; -import java.util.Set; - -import org.springframework.security.authentication.jaas.AuthorityGranter; - -/** - * An AuthorityGranter that always grants "ROLE_USER". - * - * @author Rob Winch - */ -public class RoleUserAuthorityGranter implements AuthorityGranter { - - public Set<String> grant(Principal principal) { - return Collections.singleton("ROLE_USER"); - } -} diff --git a/samples/xml/jaas/src/main/java/samples/jaas/UsernameEqualsPasswordLoginModule.java b/samples/xml/jaas/src/main/java/samples/jaas/UsernameEqualsPasswordLoginModule.java deleted file mode 100644 index c390c8cbc15..00000000000 --- a/samples/xml/jaas/src/main/java/samples/jaas/UsernameEqualsPasswordLoginModule.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2010-2016 the original author or authors. - * - * 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 - * - * https://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 samples.jaas; - -import java.io.Serializable; -import java.security.Principal; -import java.util.Map; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; - -/** - * A LoginModule that will allow login if the username equals the password. Upon - * successful authentication it adds the username as a Principal. - * - * @author Rob Winch - */ -public class UsernameEqualsPasswordLoginModule implements LoginModule { - - - private String password; - private String username; - private Subject subject; - - - @Override - public boolean abort() { - return true; - } - - @Override - public boolean commit() { - return true; - } - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, - Map<String, ?> sharedState, Map<String, ?> options) { - this.subject = subject; - - try { - NameCallback nameCallback = new NameCallback("prompt"); - PasswordCallback passwordCallback = new PasswordCallback("prompt", false); - - callbackHandler.handle(new Callback[] { nameCallback, passwordCallback }); - - password = new String(passwordCallback.getPassword()); - username = nameCallback.getName(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean login() throws LoginException { - if (username == null || !username.equals(password)) { - throw new LoginException("username is not equal to password"); - } - if ("".equals(username)) { - throw new LoginException("username cannot be empty string"); - } - - subject.getPrincipals().add(new UsernamePrincipal(username)); - return true; - } - - @Override - public boolean logout() { - return true; - } - - private static class UsernamePrincipal implements Principal, Serializable { - private final String username; - - UsernamePrincipal(String username) { - this.username = username; - } - - @Override - public String getName() { - return username; - } - - @Override - public String toString() { - return "Principal [name=" + getName() + "]"; - } - - private static final long serialVersionUID = 8049681145355488137L; - } -} diff --git a/samples/xml/jaas/src/main/resources/applicationContext-security.xml b/samples/xml/jaas/src/main/resources/applicationContext-security.xml deleted file mode 100644 index 98b5383c1b4..00000000000 --- a/samples/xml/jaas/src/main/resources/applicationContext-security.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:sec="http://www.springframework.org/schema/security" - xmlns:util="http://www.springframework.org/schema/util" - xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> - - - <sec:http auto-config="true" jaas-api-provision="true"> - <sec:intercept-url pattern="/secure/**" access="isAuthenticated()"/> - </sec:http> - - <sec:authentication-manager> - <sec:authentication-provider ref="jaasAuthProvider"/> - </sec:authentication-manager> - - <bean id="jaasAuthProvider" - class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider"> - <property name="configuration"> - <bean - class="org.springframework.security.authentication.jaas.memory.InMemoryConfiguration"> - <constructor-arg> - <map> - <entry key="SPRINGSECURITY"> - <array> - <bean class="javax.security.auth.login.AppConfigurationEntry"> - <constructor-arg - value="samples.jaas.UsernameEqualsPasswordLoginModule" /> - <constructor-arg> - <util:constant - static-field="javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED" /> - </constructor-arg> - <constructor-arg> - <map></map> - </constructor-arg> - </bean> - </array> - </entry> - </map> - </constructor-arg> - </bean> - </property> - <property name="authorityGranters"> - <list> - <bean class="samples.jaas.RoleUserAuthorityGranter" /> - </list> - </property> - </bean> -</beans> diff --git a/samples/xml/jaas/src/main/resources/logback.xml b/samples/xml/jaas/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/jaas/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/jaas/src/main/webapp/WEB-INF/web.xml b/samples/xml/jaas/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 0a385e23d99..00000000000 --- a/samples/xml/jaas/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - JAAS web application - - - --> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - <display-name>JAAS Sample Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:applicationContext-security.xml - </param-value> - </context-param> - - <!-- Nothing below here needs to be modified --> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>jaas.root</param-value> - </context-param> - - <filter> - <filter-name>localizationFilter</filter-name> - <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> - </filter> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>localizationFilter</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <welcome-file-list> - <welcome-file>index.jsp</welcome-file> - </welcome-file-list> -</web-app> diff --git a/samples/xml/jaas/src/main/webapp/index.jsp b/samples/xml/jaas/src/main/webapp/index.jsp deleted file mode 100644 index 5389880ec11..00000000000 --- a/samples/xml/jaas/src/main/webapp/index.jsp +++ /dev/null @@ -1,24 +0,0 @@ -<%@ page import="javax.security.auth.Subject" %> -<%@ page import="java.security.AccessController" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<html> -<head><title>Home Page</title></head> -<body> -<h1>Home Page</h1> -<p> -Anyone can view this page. -</p> -<p> -Your principal object is....: <%= request.getUserPrincipal() %> -</p> -<p> -Subject.getSubject(AccessController.getContext()) is....: <%= Subject.getSubject(AccessController.getContext()) %> -</p> -<p> -<sec:authorize url='/secure/index.jsp'>You can currently access "/secure" URLs.</sec:authorize> -</p> - -<p> -<a href="secure/index.jsp">Secure page</a></p> -</body> -</html> diff --git a/samples/xml/jaas/src/main/webapp/secure/index.jsp b/samples/xml/jaas/src/main/webapp/secure/index.jsp deleted file mode 100644 index 70dfa1fa387..00000000000 --- a/samples/xml/jaas/src/main/webapp/secure/index.jsp +++ /dev/null @@ -1,58 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ page import="javax.security.auth.Subject" %> -<%@ page import="java.security.AccessController" %> -<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %> -<%@ page import="org.springframework.security.core.Authentication" %> -<%@ page import="org.springframework.security.core.GrantedAuthority" %> - -<html> -<head> -<title>Security Debug Information</title> -</head> -<body> - -<h3>Security Debug Information</h3> - -<% - - Subject subject = Subject.getSubject(AccessController.getContext()); - if(subject != null) { %> -<p> - Subject.getSubject(AccessController.getContext()) is....: <%= subject %> -</p> - <%} %> - -<% - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth != null) { %> -<p> - Authentication object is of type: <em><%= auth.getClass().getName() %></em> -</p> -<p> - Authentication object as a String: <br/><br/><%= auth.toString() %> -</p> - - Authentication object holds the following granted authorities:<br /><br /> -<% - for (GrantedAuthority authority : auth.getAuthorities()) { %> - <%= authority %> (<em>getAuthority()</em>: <%= authority.getAuthority() %>)<br /> -<% } -%> - - <p><b>Success! Your web filters appear to be properly configured!</b></p> -<% - } else { -%> - Authentication object is null.<br /> - This is an error and your Spring Security application will not operate properly until corrected.<br /><br /> -<% } -%> - -<form action="<c:url value="/logout"/>" method="post"> - <input type="submit" value="Log Out"/> - <sec:csrfInput/> -</form> - -</body> -</html> diff --git a/samples/xml/ldap/spring-security-samples-xml-ldap.gradle b/samples/xml/ldap/spring-security-samples-xml-ldap.gradle deleted file mode 100644 index 6e7b2a6cce3..00000000000 --- a/samples/xml/ldap/spring-security-samples-xml-ldap.gradle +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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. - */ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-taglibs') - compile jstlDependencies - - runtime project(':spring-security-web') - runtime project(':spring-security-config') - runtime project(':spring-security-ldap') - runtime apachedsDependencies - runtime slf4jDependencies - - providedCompile 'javax.servlet:javax.servlet-api' - - integrationTestCompile seleniumDependencies -} diff --git a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/LdapXmlTests.java b/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/LdapXmlTests.java deleted file mode 100644 index 29f44441ea3..00000000000 --- a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/LdapXmlTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; -import org.springframework.security.samples.pages.LogoutPage; -import org.springframework.security.samples.pages.SecurePage; - -/** - * @author Michael Simons - */ -public class LdapXmlTests { - private WebDriver driver; - - private int port; - - @Before - public void setup() { - this.port = Integer.parseInt(System.getProperty("app.httpPort")); - this.driver = new HtmlUnitDriver(); - } - - @After - public void tearDown() { - this.driver.quit(); - } - - @Test - public void accessHomepageWithUnauthenticatedUserSuccess() { - final HomePage homePage = HomePage.to(this.driver, this.port); - homePage.assertAt(); - } - - @Test - public void accessManagePageWithUnauthenticatedUserSendsToLoginPage() { - final LoginPage loginPage = SecurePage.to(this.driver, this.port); - loginPage.assertAt(); - } - - @Test - public void authenticatedUserIsSentToOriginalPage() { - final SecurePage securePage = SecurePage.to(this.driver, this.port) - .loginForm() - .username("rod") - .password("koala") - .submit(); - securePage - .assertAt(); - } - - @Test - public void authenticatedUserLogsOut() { - final LogoutPage logoutPage = SecurePage.to(this.driver, this.port) - .loginForm() - .username("rod") - .password("koala") - .submit() - .logout(); - logoutPage.assertAt(); - - final LoginPage loginPage = SecurePage.to(this.driver, this.port); - loginPage.assertAt(); - } -} diff --git a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java deleted file mode 100644 index 69625fe8d22..00000000000 --- a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The home page. - * - * @author Michael Simons - */ -public class HomePage { - - public static HomePage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port +"/"); - return PageFactory.initElements(driver, HomePage.class); - } - - private final WebDriver webDriver; - - @FindBy(css = "p") - private WebElement message; - - @FindBy(css = "input[type=submit]") - private WebElement logoutButton; - - public HomePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public Content assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Home Page"); - return PageFactory.initElements(this.webDriver, Content.class); - } - - public LoginPage logout() { - this.logoutButton.submit(); - return PageFactory.initElements(this.webDriver, LoginPage.class); - } - - public static class Content { - @FindBy(css = "p") - private WebElement message; - - public Content andTheUserNameIsDisplayed() { - assertThat(message.getText()).isEqualTo("Hello user"); - return this; - } - } -} diff --git a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java deleted file mode 100644 index 9aa9fea10f3..00000000000 --- a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * The login page. - * - * @author Michael Simons - */ -public class LoginPage { - - private final WebDriver webDriver; - - private final LoginForm loginForm; - - public LoginPage(WebDriver webDriver) { - this.webDriver = webDriver; - this.loginForm = PageFactory.initElements(this.webDriver, LoginForm.class); - } - - public LoginPage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Please sign in"); - return this; - } - - public LoginForm loginForm() { - return this.loginForm; - } - - public static class LoginForm { - private WebDriver webDriver; - private WebElement username; - private WebElement password; - @FindBy(css = "button[type=submit]") - private WebElement submit; - - public LoginForm(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public LoginForm username(String username) { - this.username.sendKeys(username); - return this; - } - - public LoginForm password(String password) { - this.password.sendKeys(password); - return this; - } - - public SecurePage submit() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, SecurePage.class); - } - } -} diff --git a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java b/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java deleted file mode 100644 index 819ad73e044..00000000000 --- a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/LogoutPage.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Logout Page is the same as login page with an additional message. - * - * @author Michael Simons - */ -public class LogoutPage extends LoginPage { - @FindBy(css = "div[role=alert]") - private WebElement alert; - - public LogoutPage(WebDriver webDriver) { - super(webDriver); - } - - @Override - public LogoutPage assertAt() { - super.assertAt(); - - assertThat(this.alert.getText()).isEqualTo("You have been signed out"); - return this; - } -} diff --git a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java b/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java deleted file mode 100644 index 81c7cb40507..00000000000 --- a/samples/xml/ldap/src/integration-test/java/org/springframework/security/samples/pages/SecurePage.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2018 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.pages; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * A secure page. - * - * @author Michael Simons - */ -public class SecurePage { - - public static LoginPage to(WebDriver driver, int port) { - driver.get("http://localhost:" + port + "/secure"); - return PageFactory.initElements(driver, LoginPage.class); - } - - private final WebDriver webDriver; - - @FindBy(css = "input[type=submit]") - private WebElement submit; - - public SecurePage(WebDriver webDriver) { - this.webDriver = webDriver; - } - - public SecurePage assertAt() { - assertThat(this.webDriver.getTitle()).isEqualTo("Secure Page"); - return this; - } - - public LogoutPage logout() { - this.submit.click(); - return PageFactory.initElements(this.webDriver, LogoutPage.class); - } -} diff --git a/samples/xml/ldap/src/main/resources/logback.xml b/samples/xml/ldap/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/ldap/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/ldap/src/main/resources/users.ldif b/samples/xml/ldap/src/main/resources/users.ldif deleted file mode 100644 index 222e03793c4..00000000000 --- a/samples/xml/ldap/src/main/resources/users.ldif +++ /dev/null @@ -1,60 +0,0 @@ -dn: ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: groups - -dn: ou=people,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: people - -dn: uid=rod,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Rod Johnson -sn: Johnson -uid: rod -userPassword: koala - -dn: uid=dianne,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Dianne Emu -sn: Emu -uid: dianne -userPassword: emu - -dn: uid=scott,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Scott -sn: Wombat -uid: scott -userPassword: wombat - -dn: cn=user,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: user -member: uid=rod,ou=people,dc=springframework,dc=org -member: uid=dianne,ou=people,dc=springframework,dc=org -member: uid=scott,ou=people,dc=springframework,dc=org - -dn: cn=teller,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: teller -member: uid=rod,ou=people,dc=springframework,dc=org -member: uid=dianne,ou=people,dc=springframework,dc=org - -dn: cn=supervisor,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: supervisor -member: uid=rod,ou=people,dc=springframework,dc=org diff --git a/samples/xml/ldap/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/ldap/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index 1448f21d218..00000000000 --- a/samples/xml/ldap/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,65 +0,0 @@ -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:s="http://www.springframework.org/schema/security" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <s:http> - <s:intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/> - <s:intercept-url pattern="/secure/**" access="authenticated" /> - <s:intercept-url pattern="/**" access="permitAll" /> - - <s:form-login /> - <s:anonymous /> - <s:logout /> - </s:http> - - - <!-- Simple namespace-based configuration --> - - <s:ldap-server ldif="classpath:users.ldif" port="0"/> - - <s:authentication-manager> - <s:ldap-authentication-provider - group-search-filter="member={0}" - group-search-base="ou=groups" - user-search-base="ou=people" - user-search-filter="uid={0}" - /> - <s:authentication-provider ref='secondLdapProvider' /> - </s:authentication-manager> - - - <!-- Traditional Bean version of the same configuration --> - - <!-- This bean points at the embedded directory server created by the ldap-server element above --> - <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> - <constructor-arg value="ldap://localhost:33389/dc=springframework,dc=org"/> - </bean> - - <bean id="secondLdapProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> - <constructor-arg> - <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> - <constructor-arg ref="contextSource" /> - <property name="userSearch"> - <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> - <constructor-arg index="0" value="ou=people"/> - <constructor-arg index="1" value="(uid={0})"/> - <constructor-arg index="2" ref="contextSource" /> - </bean> - </property> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator"> - <constructor-arg ref="contextSource" /> - <constructor-arg value="ou=groups" /> - <property name="groupSearchFilter" value="(member={0})"/> - <property name="rolePrefix" value="ROLE_"/> - <property name="searchSubtree" value="true"/> - <property name="convertToUpperCase" value="true"/> - </bean> - </constructor-arg> - </bean> - -</beans> diff --git a/samples/xml/ldap/src/main/webapp/WEB-INF/web.xml b/samples/xml/ldap/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 41ffbf0e8ba..00000000000 --- a/samples/xml/ldap/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <display-name>Spring Security LDAP Demo Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>ldap.root</param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - -</web-app> diff --git a/samples/xml/ldap/src/main/webapp/index.jsp b/samples/xml/ldap/src/main/webapp/index.jsp deleted file mode 100644 index 4e98804d9da..00000000000 --- a/samples/xml/ldap/src/main/webapp/index.jsp +++ /dev/null @@ -1,12 +0,0 @@ -<html> -<head><title>Home Page</title></head> -<body> -<h1>Home Page</h1> -<p>Anyone can view this page.</p> - -<p>Your principal object is....: <%= request.getUserPrincipal() %></p> - -<p><a href="secure/index.jsp">Secure page</a></p> -<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/ldap/src/main/webapp/secure/extreme/index.jsp b/samples/xml/ldap/src/main/webapp/secure/extreme/index.jsp deleted file mode 100644 index 47028ce0c7d..00000000000 --- a/samples/xml/ldap/src/main/webapp/secure/extreme/index.jsp +++ /dev/null @@ -1,15 +0,0 @@ -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %> - -<html> -<body> -<h1>VERY Secure Page</h1> -This is a protected page. You can only see me if you are a supervisor. - -<p><a href="../../">Home</a> -<form action="<c:url value="/logout"/>" method="post"> -<input type="submit" value="Logoff"/> -<security:csrfInput/> -</form> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/ldap/src/main/webapp/secure/index.jsp b/samples/xml/ldap/src/main/webapp/secure/index.jsp deleted file mode 100644 index dbb887ceb70..00000000000 --- a/samples/xml/ldap/src/main/webapp/secure/index.jsp +++ /dev/null @@ -1,21 +0,0 @@ -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %> -<html> -<head><title>Secure Page</title></head> -<body> -<h1>Secure Page</h1> -This is a protected page. You can get to me if you've been remembered, -or if you've authenticated this session.<br><br> - -<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %> - You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br> -<% } %> - - -<p><a href="../">Home</a> -<form action="<c:url value="/logout"/>" method="post"> -<input type="submit" value="Logoff"/> (also clears any remember-me cookie) -<security:csrfInput/> -</form> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/oauth2login/README.adoc b/samples/xml/oauth2login/README.adoc deleted file mode 100644 index f18124e148e..00000000000 --- a/samples/xml/oauth2login/README.adoc +++ /dev/null @@ -1,278 +0,0 @@ -= OAuth 2.0 Login Sample - -This guide provides instructions on setting up the sample application with OAuth 2.0 Login using an OAuth 2.0 Provider or OpenID Connect 1.0 Provider. - -The following sections provide detailed steps for setting up OAuth 2.0 Login for these Providers: - -* <<google-login, Google>> -* <<github-login, GitHub>> -* <<facebook-login, Facebook>> -* <<okta-login, Okta>> - -[[google-login]] -== Login with Google - -This section shows how to configure the sample application using Google as the Authentication Provider and covers the following topics: - -* <<google-initial-setup,Initial setup>> -* <<google-redirect-uri,Setting the redirect URI>> -* <<google-application-config,Configure security.xml>> -* <<google-boot-application,Deploy and start the application>> - -[[google-initial-setup]] -=== Initial setup - -To use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials. - -NOTE: https://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the - https://openid.net/connect/[OpenID Connect 1.0] specification and is https://openid.net/certification/[OpenID Certified]. - -Follow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the section, "Setting up OAuth 2.0". - -After completing the "Obtain OAuth 2.0 credentials" instructions, you should have a new OAuth Client with credentials consisting of a Client ID and a Client Secret. - -[[google-redirect-uri]] -=== Setting the redirect URI - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google -and have granted access to the OAuth Client _(created in the previous step)_ on the Consent page. - -In the "Set a redirect URI" sub-section, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[google-application-config]] -=== Configure security.xml - -Now that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_. To do so: - -. Go to `security.xml` and set the following configuration: -+ -[source,xml] ----- -<client-registration registration-id="google" - client-id="google-client-id" - client-secret="google-client-secret" - provider-id="google"/> ----- - -. Replace the values in the `client-id` and `client-secret` attributes with the OAuth 2.0 credentials you created earlier. - -[[google-boot-application]] -=== Deploy and start the application - -Deploy the WAR to a `Servlet` container and then go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Google. - -Click on the Google link, and you are then redirected to Google for authentication. - -After authenticating with your Google account credentials, the next page presented to you is the Consent screen. -The Consent screen asks you to either allow or deny access to the OAuth Client you created earlier. -Click *Allow* to authorize the OAuth Client to access your email address and basic profile information. - -At this point, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. - -[[github-login]] -== Login with GitHub - -This section shows how to configure the sample application using GitHub as the Authentication Provider and covers the following topics: - -* <<github-register-application,Register OAuth application>> -* <<github-application-config,Configure security.xml>> -* <<github-boot-application,Deploy and start the application>> - -[[github-register-application]] -=== Register OAuth application - -To use GitHub's OAuth 2.0 authentication system for login, you must https://github.com/settings/applications/new[Register a new OAuth application]. - -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/github`. - -The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub -and have granted access to the OAuth application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[github-application-config]] -=== Configure security.xml - -Now that you have a new OAuth application with GitHub, you need to configure the application to use the OAuth application for the _authentication flow_. To do so: - -. Go to `security.xml` and set the following configuration: -+ -[source,xml] ----- -<client-registration registration-id="github" - client-id="github-client-id" - client-secret="github-client-secret" - provider-id="github"/> ----- - -. Replace the values in the `client-id` and `client-secret` attributes with the OAuth 2.0 credentials you created earlier. - -[[github-boot-application]] -=== Deploy and start the application - -Deploy the WAR to a `Servlet` container and then go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for GitHub. - -Click on the GitHub link, and you are then redirected to GitHub for authentication. - -After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your personal user data information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[TIP] -For detailed information returned from the UserInfo Endpoint, see the API documentation -for https://developer.github.com/v3/users/#get-the-authenticated-user["Get the authenticated user"]. - -[[facebook-login]] -== Login with Facebook - -This section shows how to configure the sample application using Facebook as the Authentication Provider and covers the following topics: - -* <<facebook-register-application,Add a New App>> -* <<facebook-application-config,Configure security.xml>> -* <<facebook-boot-application,Deploy and start the application>> - -[[facebook-register-application]] -=== Add a New App - -To use Facebook's OAuth 2.0 authentication system for login, you must first https://developers.facebook.com/apps[Add a New App]. - -Select "Create a New App" and then the "Create a New App ID" page is presented. Enter the Display Name, Contact Email, Category and then click "Create App ID". - -NOTE: The selection for the _Category_ field is not relevant but it's a required field - select "Local". - -The next page presented is "Product Setup". Click the "Get Started" button for the *Facebook Login* product. -In the left sidebar, under _Products -> Facebook Login_, select _Settings_. - -For the field *Valid OAuth redirect URIs*, enter `http://localhost:8080/login/oauth2/code/facebook` then click _Save Changes_. - -The OAuth redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Facebook -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[facebook-application-config]] -=== Configure security.xml - -Now that you have created a new application with Facebook, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `security.xml` and set the following configuration: -+ -[source,xml] ----- -<client-registration registration-id="facebook" - client-id="facebook-client-id" - client-secret="facebook-client-secret" - provider-id="facebook"/> ----- - -. Replace the values in the `client-id` and `client-secret` attributes with the OAuth 2.0 credentials you created earlier. - -[[facebook-boot-application]] -=== Deploy and start the application - -Deploy the WAR to a `Servlet` container and then go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Facebook. - -Click on the Facebook link, and you are then redirected to Facebook for authentication. - -After authenticating with your Facebook credentials, the next page presented to you is "Authorize application". -This page will ask you to *Authorize* the application you created in the previous step. -Click _Authorize application_ to allow the OAuth application to access your _public profile_ and _email address_ information. - -At this point, the OAuth Client retrieves your personal user information -from the UserInfo Endpoint and establishes an authenticated session. - -[[okta-login]] -== Login with Okta - -This section shows how to configure the sample application using Okta as the Authentication Provider and covers the following topics: - -* <<okta-register-application,Add Application>> -* <<okta-assign-application-people,Assign Application to People>> -* <<okta-application-config,Configure security.xml>> -* <<okta-boot-application,Deploy and start the application>> - -[[okta-register-application]] -=== Add Application - -To use Okta's OAuth 2.0 authentication system for login, you must first https://www.okta.com/developer/signup[create a developer account]. - -Sign in to your account sub-domain and navigate to _Applications -> Applications_ and then select the "Add Application" button. -From the "Add Application" page, select the "Create New App" button and enter the following: - -* *Platform:* Web -* *Sign on method:* OpenID Connect - -Select the _Create_ button. -On the "General Settings" page, enter the Application Name (for example, "Spring Security Okta Login") and then select the _Next_ button. -On the "Configure OpenID Connect" page, enter `http://localhost:8080/login/oauth2/code/okta` for the field *Redirect URIs* and then select _Finish_. - -The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Okta -and have granted access to the application on the _Authorize application_ page. - -TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. - The *_registrationId_* is a unique identifier for the `ClientRegistration`. - -IMPORTANT: If the application is running behind a proxy server, it is recommended to check https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#appendix-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. -Also, see the supported https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2Client-auth-code-redirect-uri[`URI` template variables] for `redirect-uri`. - -[[okta-assign-application-people]] -=== Assign Application to People - -From the "General" tab of the application, select the "Assignments" tab and then select the _Assign_ button. -Select _Assign to People_ and assign your account to the application. Then select the _Save and Go Back_ button. - -[[okta-application-config]] -=== Configure security.xml - -Now that you have created a new application with Okta, you need to configure the sample application to use the application for the _authentication flow_. To do so: - -. Go to `security.xml` and set the following configuration: -+ -[source,xml] ----- -<client-registration registration-id="okta" - client-id="okta-client-id" - client-secret="okta-client-secret" - provider-id="okta"/> - -<provider provider-id="okta" - authorization-uri="https://your-subdomain.oktapreview.com/oauth2/v1/authorize" - token-uri="https://your-subdomain.oktapreview.com/oauth2/v1/token" - user-info-uri="https://your-subdomain.oktapreview.com/oauth2/v1/userinfo" - jwk-set-uri="https://your-subdomain.oktapreview.com/oauth2/v1/keys"/> ----- - -. Replace the values in the `client-id` and `client-secret` attributes with the OAuth 2.0 credentials you created earlier. -As well, replace `https://your-subdomain.oktapreview.com` in `authorization-uri`, `token-uri`, `user-info-uri` and `jwk-set-uri` with the sub-domain assigned to your account during the registration process. - -[[okta-boot-application]] -=== Deploy and start the application - -Deploy the WAR to a `Servlet` container and then go to `http://localhost:8080`. -You are then redirected to the default _auto-generated_ login page, which displays a link for Okta. - -Click on the Okta link, and you are then redirected to Okta for authentication. - -After authenticating with your Okta account credentials, the OAuth Client retrieves your email address and basic profile information -from the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. diff --git a/samples/xml/oauth2login/spring-security-samples-xml-oauth2login.gradle b/samples/xml/oauth2login/spring-security-samples-xml-oauth2login.gradle deleted file mode 100644 index 29b8ef486b4..00000000000 --- a/samples/xml/oauth2login/spring-security-samples-xml-oauth2login.gradle +++ /dev/null @@ -1,15 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-webmvc' - compile project(':spring-security-config') - compile project(':spring-security-web') - compile project(':spring-security-oauth2-client') - compile project(':spring-security-oauth2-jose') - compile 'com.fasterxml.jackson.core:jackson-databind' - compile 'org.thymeleaf:thymeleaf-spring5' - compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE' - - testCompile project(':spring-security-test') -} diff --git a/samples/xml/oauth2login/src/main/java/sample/config/WebConfig.java b/samples/xml/oauth2login/src/main/java/sample/config/WebConfig.java deleted file mode 100644 index 53db0361a89..00000000000 --- a/samples/xml/oauth2login/src/main/java/sample/config/WebConfig.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.config; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.client.web.method.annotation.OAuth2AuthorizedClientArgumentResolver; -import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; -import org.thymeleaf.spring5.view.ThymeleafViewResolver; -import org.thymeleaf.templatemode.TemplateMode; - -import java.util.List; - -/** - * @author Joe Grandja - */ -@Configuration -@EnableWebMvc -public class WebConfig implements WebMvcConfigurer, ApplicationContextAware { - private ApplicationContext context; - private ClientRegistrationRepository clientRegistrationRepository; - private OAuth2AuthorizedClientRepository authorizedClientRepository; - - @Override - public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { - // @AuthenticationPrincipal - AuthenticationPrincipalArgumentResolver principalArgumentResolver = - new AuthenticationPrincipalArgumentResolver(); - principalArgumentResolver.setBeanResolver(new BeanFactoryResolver( - this.context.getAutowireCapableBeanFactory())); - resolvers.add(principalArgumentResolver); - - // @RegisteredOAuth2AuthorizedClient - resolvers.add(new OAuth2AuthorizedClientArgumentResolver( - this.clientRegistrationRepository, this.authorizedClientRepository)); - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - @Autowired - public void setClientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) { - this.clientRegistrationRepository = clientRegistrationRepository; - } - - @Autowired - public void setAuthorizedClientRepository(OAuth2AuthorizedClientRepository authorizedClientRepository) { - this.authorizedClientRepository = authorizedClientRepository; - } - - @Bean - public SpringResourceTemplateResolver templateResolver() { - SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); - templateResolver.setApplicationContext(this.context); - templateResolver.setPrefix("/WEB-INF/templates/"); - templateResolver.setSuffix(".html"); - templateResolver.setTemplateMode(TemplateMode.HTML); - templateResolver.setCacheable(true); - return templateResolver; - } - - @Bean - public SpringTemplateEngine templateEngine() { - SpringTemplateEngine templateEngine = new SpringTemplateEngine(); - templateEngine.setTemplateResolver(templateResolver()); - templateEngine.setEnableSpringELCompiler(true); - return templateEngine; - } - - @Bean - public ThymeleafViewResolver viewResolver() { - ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); - viewResolver.setTemplateEngine(templateEngine()); - return viewResolver; - } -} diff --git a/samples/xml/oauth2login/src/main/java/sample/web/OAuth2LoginController.java b/samples/xml/oauth2login/src/main/java/sample/web/OAuth2LoginController.java deleted file mode 100644 index 41654281456..00000000000 --- a/samples/xml/oauth2login/src/main/java/sample/web/OAuth2LoginController.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2020 the original author or authors. - * - * 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 - * - * https://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 sample.web; - -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * @author Joe Grandja - */ -@Controller -public class OAuth2LoginController { - - @GetMapping("/") - public String index(Model model, - @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, - @AuthenticationPrincipal OAuth2User oauth2User) { - model.addAttribute("userName", oauth2User.getName()); - model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName()); - model.addAttribute("userAttributes", oauth2User.getAttributes()); - return "index"; - } -} diff --git a/samples/xml/oauth2login/src/main/webapp/WEB-INF/security.xml b/samples/xml/oauth2login/src/main/webapp/WEB-INF/security.xml deleted file mode 100644 index 078e9f2c437..00000000000 --- a/samples/xml/oauth2login/src/main/webapp/WEB-INF/security.xml +++ /dev/null @@ -1,52 +0,0 @@ -<b:beans xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://www.springframework.org/schema/security" - xsi:schemaLocation=" - http://www.springframework.org/schema/security - https://www.springframework.org/schema/security/spring-security.xsd - http://www.springframework.org/schema/beans - https://www.springframework.org/schema/beans/spring-beans.xsd"> - - <http auto-config="true"> - <intercept-url pattern="/**" access="authenticated"/> - <oauth2-login authorized-client-repository-ref="authorizedClientRepository"/> - </http> - - <client-registrations> - <client-registration registration-id="google" - client-id="your-app-client-id" - client-secret="your-app-client-secret" - provider-id="google"/> - <client-registration registration-id="github" - client-id="your-app-client-id" - client-secret="your-app-client-secret" - provider-id="github"/> - <client-registration registration-id="facebook" - client-id="your-app-client-id" - client-secret="your-app-client-secret" - provider-id="facebook"/> - <client-registration registration-id="okta" - client-id="your-app-client-id" - client-secret="your-app-client-secret" - provider-id="okta"/> - <provider provider-id="okta" - authorization-uri="https://your-subdomain.oktapreview.com/oauth2/v1/authorize" - token-uri="https://your-subdomain.oktapreview.com/oauth2/v1/token" - user-info-uri="https://your-subdomain.oktapreview.com/oauth2/v1/userinfo" - jwk-set-uri="https://your-subdomain.oktapreview.com/oauth2/v1/keys"/> - </client-registrations> - - <user-service> - <user name="user" password="{noop}password" authorities="ROLE_USER" /> - </user-service> - - <b:bean id="authorizedClientService" - class="org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService" - autowire="constructor"/> - - <b:bean id="authorizedClientRepository" - class="org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository"> - <b:constructor-arg ref="authorizedClientService"/> - </b:bean> - -</b:beans> diff --git a/samples/xml/oauth2login/src/main/webapp/WEB-INF/spring-servlet.xml b/samples/xml/oauth2login/src/main/webapp/WEB-INF/spring-servlet.xml deleted file mode 100644 index a55d96616c1..00000000000 --- a/samples/xml/oauth2login/src/main/webapp/WEB-INF/spring-servlet.xml +++ /dev/null @@ -1,10 +0,0 @@ -<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" - xmlns:mvc="http://www.springframework.org/schema/mvc" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd - http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> - - <context:component-scan base-package="sample"/> - -</beans> diff --git a/samples/xml/oauth2login/src/main/webapp/WEB-INF/templates/index.html b/samples/xml/oauth2login/src/main/webapp/WEB-INF/templates/index.html deleted file mode 100644 index 629d8ac8ee5..00000000000 --- a/samples/xml/oauth2login/src/main/webapp/WEB-INF/templates/index.html +++ /dev/null @@ -1,34 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> -<head> - <title>Spring Security - OAuth 2.0 Login</title> - <meta charset="utf-8" /> -</head> -<body> -<div style="float: right" th:fragment="logout" sec:authorize="isAuthenticated()"> - <div style="float:left"> - <span style="font-weight:bold">User: </span><span sec:authentication="name"></span> - </div> - <div style="float:none"> </div> - <div style="float:right"> - <form action="#" th:action="@{/logout}" method="post"> - <input type="submit" value="Logout" /> - </form> - </div> -</div> -<h1>OAuth 2.0 Login with Spring Security</h1> -<div> - You are successfully logged in <span style="font-weight:bold" th:text="${userName}"></span> - via the OAuth 2.0 Client <span style="font-weight:bold" th:text="${clientName}"></span> -</div> -<div> </div> -<div> - <span style="font-weight:bold">User Attributes:</span> - <ul> - <li th:each="userAttribute : ${userAttributes}"> - <span style="font-weight:bold" th:text="${userAttribute.key}"></span>: <span th:text="${userAttribute.value}"></span> - </li> - </ul> -</div> -</body> -</html> diff --git a/samples/xml/oauth2login/src/main/webapp/WEB-INF/web.xml b/samples/xml/oauth2login/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index b88e476eff3..00000000000 --- a/samples/xml/oauth2login/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> - - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value>/WEB-INF/security.xml</param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <servlet> - <servlet-name>spring</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - </servlet> - - <servlet-mapping> - <servlet-name>spring</servlet-name> - <url-pattern>/</url-pattern> - </servlet-mapping> - -</web-app> \ No newline at end of file diff --git a/samples/xml/openid/spring-security-samples-xml-openid.gradle b/samples/xml/openid/spring-security-samples-xml-openid.gradle deleted file mode 100644 index f6af3676800..00000000000 --- a/samples/xml/openid/spring-security-samples-xml-openid.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-openid') - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-taglibs') - runtime jstlDependencies - runtime slf4jDependencies -} diff --git a/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetails.java b/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetails.java deleted file mode 100644 index 066eb0bbdc4..00000000000 --- a/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetails.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.openid; - -import java.util.Collection; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; - -/** - * Customized {@code UserDetails} implementation. - * - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - * @author Luke Taylor - * @since 3.1 - */ -public class CustomUserDetails extends User { - private String email; - private String name; - private boolean newUser; - - public CustomUserDetails(String username, Collection<GrantedAuthority> authorities) { - super(username, "unused", authorities); - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public boolean isNewUser() { - return newUser; - } - - public void setNewUser(boolean newUser) { - this.newUser = newUser; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetailsService.java b/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetailsService.java deleted file mode 100644 index b92fe9b5569..00000000000 --- a/samples/xml/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetailsService.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.openid; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.openid.OpenIDAttribute; -import org.springframework.security.openid.OpenIDAuthenticationToken; - -/** - * Custom UserDetailsService which accepts any OpenID user, "registering" new users in a - * map so they can be welcomed back to the site on subsequent logins. - * - * @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are - * <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> - * to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. - * @author Luke Taylor - * @since 3.1 - */ -public class CustomUserDetailsService implements UserDetailsService, - AuthenticationUserDetailsService<OpenIDAuthenticationToken> { - - private final Map<String, CustomUserDetails> registeredUsers = new HashMap<>(); - - private static final List<GrantedAuthority> DEFAULT_AUTHORITIES = AuthorityUtils - .createAuthorityList("ROLE_USER"); - - /** - * Implementation of {@code UserDetailsService}. We only need this to satisfy the - * {@code RememberMeServices} requirements. - */ - public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException { - UserDetails user = registeredUsers.get(id); - - if (user == null) { - throw new UsernameNotFoundException(id); - } - - return user; - } - - /** - * Implementation of {@code AuthenticationUserDetailsService} which allows full access - * to the submitted {@code Authentication} object. Used by the - * OpenIDAuthenticationProvider. - */ - public UserDetails loadUserDetails(OpenIDAuthenticationToken token) { - String id = token.getIdentityUrl(); - - CustomUserDetails user = registeredUsers.get(id); - - if (user != null) { - return user; - } - - String email = null; - String firstName = null; - String lastName = null; - String fullName = null; - - List<OpenIDAttribute> attributes = token.getAttributes(); - - for (OpenIDAttribute attribute : attributes) { - if (attribute.getName().equals("email")) { - email = attribute.getValues().get(0); - } - - if (attribute.getName().equals("firstname")) { - firstName = attribute.getValues().get(0); - } - - if (attribute.getName().equals("lastname")) { - lastName = attribute.getValues().get(0); - } - - if (attribute.getName().equals("fullname")) { - fullName = attribute.getValues().get(0); - } - } - - if (fullName == null) { - StringBuilder fullNameBldr = new StringBuilder(); - - if (firstName != null) { - fullNameBldr.append(firstName); - } - - if (lastName != null) { - fullNameBldr.append(" ").append(lastName); - } - fullName = fullNameBldr.toString(); - } - - user = new CustomUserDetails(id, DEFAULT_AUTHORITIES); - user.setEmail(email); - user.setName(fullName); - - registeredUsers.put(id, user); - - user = new CustomUserDetails(id, DEFAULT_AUTHORITIES); - user.setEmail(email); - user.setName(fullName); - user.setNewUser(true); - - return user; - } -} diff --git a/samples/xml/openid/src/main/resources/logback.xml b/samples/xml/openid/src/main/resources/logback.xml deleted file mode 100644 index 1f54c087538..00000000000 --- a/samples/xml/openid/src/main/resources/logback.xml +++ /dev/null @@ -1,16 +0,0 @@ -<!-- NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are -<a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> -to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. --> - -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/openid/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/openid/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index c79478c9223..00000000000 --- a/samples/xml/openid/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - Namespace-based OpenID configuration - --> - -<b:beans xmlns="http://www.springframework.org/schema/security" - xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <http> - <intercept-url pattern="/openidlogin.jsp*" access="permitAll"/> - <intercept-url pattern="/images/*" access="permitAll"/> - <intercept-url pattern="/css/*" access="permitAll"/> - <intercept-url pattern="/js/*" access="permitAll"/> - <intercept-url pattern="/**" access="authenticated"/> - <logout logout-success-url="/"/> - <openid-login login-page="/openidlogin.jsp" user-service-ref="registeringUserService" - authentication-failure-url="/openidlogin.jsp?login_error=true"> - <attribute-exchange identifier-match="https://www.google.com/.*"> - <openid-attribute name="email" type="https://axschema.org/contact/email" required="true" count="1"/> - <openid-attribute name="firstname" type="https://axschema.org/namePerson/first" required="true" /> - <openid-attribute name="lastname" type="https://axschema.org/namePerson/last" required="true" /> - </attribute-exchange> - <attribute-exchange identifier-match=".*yahoo.com.*"> - <openid-attribute name="email" type="https://axschema.org/contact/email" required="true"/> - <openid-attribute name="fullname" type="https://axschema.org/namePerson" required="true" /> - </attribute-exchange> - <attribute-exchange identifier-match=".*myopenid.com.*"> - <openid-attribute name="email" type="https://schema.openid.net/contact/email" required="true"/> - <openid-attribute name="fullname" type="https://schema.openid.net/namePerson" required="true" /> - </attribute-exchange> - </openid-login> - <remember-me token-repository-ref="tokenRepo"/> - </http> - - <b:bean id="tokenRepo" - class="org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl" /> - - <authentication-manager alias="authenticationManager"/> - -<!-- - A custom UserDetailsService which will allow any user to authenticate and "register" their IDs in an internal map - for use if they return to the site. This is the most common usage pattern for sites which use OpenID. - --> - <b:bean id="registeringUserService" class="org.springframework.security.samples.openid.CustomUserDetailsService" /> - -<!-- - A namespace-based UserDetailsService which will reject users who are not already defined. - This can be used as an alternative. - --> -<!-- - <user-service id="userService"> - <user name="https://luke.taylor.myopenid.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" /> - <user name="https://raykrueger.blogspot.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" /> - <user name="https://spring.security.test.myopenid.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" /> - </user-service> - --> -</b:beans> diff --git a/samples/xml/openid/src/main/webapp/WEB-INF/web.xml b/samples/xml/openid/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 38f1ffc7891..00000000000 --- a/samples/xml/openid/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <display-name>Spring Security OpenID Demo Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>openid.root</param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - -</web-app> diff --git a/samples/xml/openid/src/main/webapp/css/openid.css b/samples/xml/openid/src/main/webapp/css/openid.css deleted file mode 100644 index 8929ff07aba..00000000000 --- a/samples/xml/openid/src/main/webapp/css/openid.css +++ /dev/null @@ -1,45 +0,0 @@ -#openid_form { - width: 580px; -} - #openid_form legend { - font-weight: bold; - } -#openid_choice { - display: none; -} -#openid_input_area { - clear: both; - padding: 10px; -} -#openid_btns, #openid_btns br { - clear: both; -} - #openid_highlight { - padding: 3px; - background-color: #FFFCC9; - float: left; - } - .openid_large_btn { - width: 100px; - height: 60px; - border: 1px solid #DDD; - margin: 3px; - float: left; - } - .openid_small_btn { - width: 24px; - height: 24px; - border: 1px solid #DDD; - margin: 3px; - float: left; - } - a.openid_large_btn:focus { - outline: none; - } - a.openid_large_btn:focus - { - -moz-outline-style: none; - } - .openid_selected { - border: 4px solid #DDD; - } \ No newline at end of file diff --git a/samples/xml/openid/src/main/webapp/images/aol.gif b/samples/xml/openid/src/main/webapp/images/aol.gif deleted file mode 100644 index decc4f12362..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/aol.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/blogger.ico b/samples/xml/openid/src/main/webapp/images/blogger.ico deleted file mode 100644 index 1b9730b01c3..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/blogger.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/claimid.ico b/samples/xml/openid/src/main/webapp/images/claimid.ico deleted file mode 100644 index 2b80f49183c..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/claimid.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/facebook.gif b/samples/xml/openid/src/main/webapp/images/facebook.gif deleted file mode 100644 index b997b358f78..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/facebook.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/flickr.ico b/samples/xml/openid/src/main/webapp/images/flickr.ico deleted file mode 100644 index 11f6e07f684..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/flickr.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/google.gif b/samples/xml/openid/src/main/webapp/images/google.gif deleted file mode 100644 index 1b6cd07bd8b..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/google.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/livejournal.ico b/samples/xml/openid/src/main/webapp/images/livejournal.ico deleted file mode 100644 index f3d21ec5e8f..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/livejournal.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/myopenid.ico b/samples/xml/openid/src/main/webapp/images/myopenid.ico deleted file mode 100644 index ceb06e6a3f0..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/myopenid.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/openid-inputicon.gif b/samples/xml/openid/src/main/webapp/images/openid-inputicon.gif deleted file mode 100644 index cde836c893f..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/openid-inputicon.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/openid.gif b/samples/xml/openid/src/main/webapp/images/openid.gif deleted file mode 100644 index c718b0e6f37..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/openid.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/technorati.ico b/samples/xml/openid/src/main/webapp/images/technorati.ico deleted file mode 100644 index fa1083c1165..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/technorati.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/verisign.gif b/samples/xml/openid/src/main/webapp/images/verisign.gif deleted file mode 100644 index faa6aaafbda..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/verisign.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/verisign.ico b/samples/xml/openid/src/main/webapp/images/verisign.ico deleted file mode 100644 index 3953af93198..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/verisign.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/vidoop.ico b/samples/xml/openid/src/main/webapp/images/vidoop.ico deleted file mode 100644 index bbd9a0d50f8..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/vidoop.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/wordpress.ico b/samples/xml/openid/src/main/webapp/images/wordpress.ico deleted file mode 100644 index 31b7d2c2b77..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/wordpress.ico and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/images/yahoo.gif b/samples/xml/openid/src/main/webapp/images/yahoo.gif deleted file mode 100644 index 42adbfa57f8..00000000000 Binary files a/samples/xml/openid/src/main/webapp/images/yahoo.gif and /dev/null differ diff --git a/samples/xml/openid/src/main/webapp/index.jsp b/samples/xml/openid/src/main/webapp/index.jsp deleted file mode 100644 index 868f99600e8..00000000000 --- a/samples/xml/openid/src/main/webapp/index.jsp +++ /dev/null @@ -1,38 +0,0 @@ -<%@ page import="org.springframework.security.web.csrf.CsrfToken" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> - -<html> -<body> - -<h1>OpenID Sample Home Page</h1> - -<p><strong> -NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are -<a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> -to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. -</strong></p> - -<sec:authentication property='principal.newUser' var='isNew' /> -<p> -Welcome<c:if test="${!isNew}"> back,</c:if> <sec:authentication property='principal.name' />! -</p> -<c:if test="${isNew}"> -<p> -As a first time user of this site, your OpenID identity has been registered -by the application and will be recognized if you return. -</p> -</c:if> - -<h3>Technical Information</h3> -<p> -Your principal object is....: <%= request.getUserPrincipal() %> -</p> -<% CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); %> -<form id="logout" method="post" action="logout"> - <input type="hidden" name="<%= token.getParameterName() %>" - value="<%= token.getToken() %>"/> -</form> -<p><a href="#" onclick="document.forms[0].submit()">Logout</a></p> -</body> -</html> diff --git a/samples/xml/openid/src/main/webapp/js/jquery-3.5.1.min.js b/samples/xml/openid/src/main/webapp/js/jquery-3.5.1.min.js deleted file mode 100644 index b0614034ad3..00000000000 --- a/samples/xml/openid/src/main/webapp/js/jquery-3.5.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/samples/xml/openid/src/main/webapp/js/openid-client/jquery.query-2.1.3.js b/samples/xml/openid/src/main/webapp/js/openid-client/jquery.query-2.1.3.js deleted file mode 100644 index e4320a76222..00000000000 --- a/samples/xml/openid/src/main/webapp/js/openid-client/jquery.query-2.1.3.js +++ /dev/null @@ -1,220 +0,0 @@ -/** - * jQuery.query - Query String Modification and Creation for jQuery - * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com) - * Licensed under the WTFPL (https://www.wtfpl.net/). - * Date: 2009/02/08 - * - * @author Blair Mitchelmore - * @version 2.1.3 - * - **/ -new function(settings) { - // Various Settings - var $separator = settings.separator || '&'; - var $spaces = settings.spaces === false ? false : true; - var $suffix = settings.suffix === false ? '' : '[]'; - var $prefix = settings.prefix === false ? false : true; - var $hash = $prefix ? settings.hash === true ? "#" : "?" : ""; - var $numbers = settings.numbers === false ? false : true; - - jQuery.query = new function() { - var is = function(o, t) { - return o != undefined && o !== null && (!!t ? o.constructor == t : true); - }; - var parse = function(path) { - var m, rx = /\[([^[]*)\]/g, match = /^(\S+?)(\[\S*\])?$/.exec(path), base = match[1], tokens = []; - while (m = rx.exec(match[2])) tokens.push(m[1]); - return [base, tokens]; - }; - var set = function(target, tokens, value) { - var o, token = tokens.shift(); - if (typeof target != 'object') target = null; - if (token === "") { - if (!target) target = []; - if (is(target, Array)) { - target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value)); - } else if (is(target, Object)) { - var i = 0; - while (target[i++] != null); - target[--i] = tokens.length == 0 ? value : set(target[i], tokens.slice(0), value); - } else { - target = []; - target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value)); - } - } else if (token && token.match(/^\s*[0-9]+\s*$/)) { - var index = parseInt(token, 10); - if (!target) target = []; - target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value); - } else if (token) { - var index = token.replace(/^\s*|\s*$/g, ""); - if (!target) target = {}; - if (is(target, Array)) { - var temp = {}; - for (var i = 0; i < target.length; ++i) { - temp[i] = target[i]; - } - target = temp; - } - target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value); - } else { - return value; - } - return target; - }; - - var queryObject = function(a) { - var self = this; - self.keys = {}; - - if (a.queryObject) { - jQuery.each(a.get(), function(key, val) { - self.SET(key, val); - }); - } else { - jQuery.each(arguments, function() { - var q = "" + this; - q = decodeURIComponent(q); - q = q.replace(/^[?#]/,''); // remove any leading ? || # - q = q.replace(/[;&]$/,''); // remove any trailing & || ; - if ($spaces) q = q.replace(/[+]/g,' '); // replace +'s with spaces - - jQuery.each(q.split(/[&;]/), function(){ - var key = this.split('=')[0]; - var val = this.split('=')[1]; - - if (!key) return; - - if ($numbers) { - if (/^[+-]?[0-9]+\.[0-9]*$/.test(val)) // simple float regex - val = parseFloat(val); - else if (/^[+-]?[0-9]+$/.test(val)) // simple int regex - val = parseInt(val, 10); - } - - val = (!val && val !== 0) ? true : val; - - if (val !== false && val !== true && typeof val != 'number') - val = val; - - self.SET(key, val); - }); - }); - } - return self; - }; - - queryObject.prototype = { - queryObject: true, - has: function(key, type) { - var value = this.get(key); - return is(value, type); - }, - GET: function(key) { - if (!is(key)) return this.keys; - var parsed = parse(key), base = parsed[0], tokens = parsed[1]; - var target = this.keys[base]; - while (target != null && tokens.length != 0) { - target = target[tokens.shift()]; - } - return typeof target == 'number' ? target : target || ""; - }, - get: function(key) { - var target = this.GET(key); - if (is(target, Object)) - return jQuery.extend(true, {}, target); - else if (is(target, Array)) - return target.slice(0); - return target; - }, - SET: function(key, val) { - var value = !is(val) ? null : val; - var parsed = parse(key), base = parsed[0], tokens = parsed[1]; - var target = this.keys[base]; - this.keys[base] = set(target, tokens.slice(0), value); - return this; - }, - set: function(key, val) { - return this.copy().SET(key, val); - }, - REMOVE: function(key) { - return this.SET(key, null).COMPACT(); - }, - remove: function(key) { - return this.copy().REMOVE(key); - }, - EMPTY: function() { - var self = this; - jQuery.each(self.keys, function(key, value) { - delete self.keys[key]; - }); - return self; - }, - load: function(url) { - var hash = url.replace(/^.*?[#](.+?)(?:\?.+)?$/, "$1"); - var search = url.replace(/^.*?[?](.+?)(?:#.+)?$/, "$1"); - return new queryObject(url.length == search.length ? '' : search, url.length == hash.length ? '' : hash); - }, - empty: function() { - return this.copy().EMPTY(); - }, - copy: function() { - return new queryObject(this); - }, - COMPACT: function() { - function build(orig) { - var obj = typeof orig == "object" ? is(orig, Array) ? [] : {} : orig; - if (typeof orig == 'object') { - function add(o, key, value) { - if (is(o, Array)) - o.push(value); - else - o[key] = value; - } - jQuery.each(orig, function(key, value) { - if (!is(value)) return true; - add(obj, key, build(value)); - }); - } - return obj; - } - this.keys = build(this.keys); - return this; - }, - compact: function() { - return this.copy().COMPACT(); - }, - toString: function() { - var i = 0, queryString = [], chunks = [], self = this; - var addFields = function(arr, key, value) { - if (!is(value) || value === false) return; - var o = [encodeURIComponent(key)]; - if (value !== true) { - o.push("="); - o.push(encodeURIComponent(value)); - } - arr.push(o.join("")); - }; - var build = function(obj, base) { - var newKey = function(key) { - return !base || base == "" ? [key].join("") : [base, "[", key, "]"].join(""); - }; - jQuery.each(obj, function(key, value) { - if (typeof value == 'object') - build(value, newKey(key)); - else - addFields(chunks, newKey(key), value); - }); - }; - - build(this.keys); - - if (chunks.length > 0) queryString.push($hash); - queryString.push(chunks.join($separator)); - - return queryString.join(""); - } - }; - - return new queryObject(location.search, location.hash); - }; -}(jQuery.query || {}); // Pass in jQuery.query as settings object diff --git a/samples/xml/openid/src/main/webapp/js/openid-client/openid-client-config.js b/samples/xml/openid/src/main/webapp/js/openid-client/openid-client-config.js deleted file mode 100644 index 4cb0920c8e4..00000000000 --- a/samples/xml/openid/src/main/webapp/js/openid-client/openid-client-config.js +++ /dev/null @@ -1,20 +0,0 @@ -/* -Defines the base of where the OpenID Provider redirects its response to. - */ -var server_root = "https://openid-selector.googlecode.com/svn/trunk/" - -/* -On the server-side you'd accept an OpenID URL and perform discovery -on it to find out the actual OpenID endpoint to send the authentication -request to. On the client side it isn't possible to lookup the endpoint -from the target server due to XSS restrictions. The endpoint for each -provider is therefore cached in this static file. If an endpoint isn't -specified for a provider then authentication on the client side cannot -proceed. -*/ -var providers_endpoint = { - google: 'https://www.google.com/accounts/o8/ud', - yahoo: 'https://open.login.yahooapis.com/openid/op/auth', - aol: 'https://api.screenname.aol.com/auth/openidServer', - verisign: 'https://pip.verisignlabs.com/server' -} \ No newline at end of file diff --git a/samples/xml/openid/src/main/webapp/js/openid-client/openid-client.js b/samples/xml/openid/src/main/webapp/js/openid-client/openid-client.js deleted file mode 100644 index 63f4f75bf6b..00000000000 --- a/samples/xml/openid/src/main/webapp/js/openid-client/openid-client.js +++ /dev/null @@ -1,63 +0,0 @@ -var claimedID; -var providerID; -var openIDPopup; - -function OpenID_iframe_then_popup_handler(provider, claimed) { - providerID = provider; - claimedID = claimed; - var immediateiframe = document.getElementById("openid_immediate_iframe"); - - var iframeurl = getBaseOpenIDProviderURL(providerID, claimedID, true); - - immediateiframe.innerHTML = "<iframe frameborder='1' src='" + iframeurl + "'></iframe>"; -} - -function processOpenIDImmediateResponse(responseURL) { - var immediateiframe = document.getElementById("openid_immediate_iframe"); - immediateiframe.innerHTML = responseURL; - - var failure = new RegExp("openid.mode=setup_needed"); - if(failure.test(responseURL)) { - var popupurl = getBaseOpenIDProviderURL(providerID, claimedID, false); - openIDPopup = window.open(popupurl, "OpenIDPopup"); - } else { - alert("Success without popup!"); - } -} - -function processOpenIDSetupResponse(responseURL) { - openIDPopup.close(); - - var results = ""; - var responseQuery = $.query.load(responseURL); - $.each(responseQuery.get(), function(key, value) { - results += "<br/>" + key + "=" + value; - }); - - document.getElementById("openid_status").innerHTML = "<br/>Result of authentication is: " + results; -} - -function getBaseOpenIDProviderURL(provider, claimed, immediate) { - var providerEndpoint = providers_endpoint[provider]; - var providerURL = providerEndpoint; //From previous discovery - providerURL += "?"; - providerURL += "openid.ns=" + encodeURIComponent("https://specs.openid.net/auth/2.0"); - if(providers[provider].label) { - providerURL += "&openid.claimed_id=" + encodeURIComponent(claimed); - providerURL += "&openid.identity=" + encodeURIComponent(claimed); - } - else { - providerURL += "&openid.claimed_id=" + encodeURIComponent("https://specs.openid.net/auth/2.0/identifier_select"); - providerURL += "&openid.identity=" + encodeURIComponent("https://specs.openid.net/auth/2.0/identifier_select"); - } - if(immediate) { - providerURL += "&openid.return_to=" + encodeURIComponent(server_root + "openid-client/checkid_immediate_response.html"); - providerURL += "&openid.realm=" + encodeURIComponent(server_root + "openid-client/checkid_immediate_response.html"); - providerURL += "&openid.mode=checkid_immediate"; - } else { - providerURL += "&openid.return_to=" + encodeURIComponent(server_root + "openid-client/checkid_setup_response.html"); - providerURL += "&openid.realm=" + encodeURIComponent(server_root + "openid-client/checkid_setup_response.html"); - providerURL += "&openid.mode=checkid_setup"; - } - return providerURL; -} \ No newline at end of file diff --git a/samples/xml/openid/src/main/webapp/js/openid-jquery.js b/samples/xml/openid/src/main/webapp/js/openid-jquery.js deleted file mode 100644 index e64a7ea90af..00000000000 --- a/samples/xml/openid/src/main/webapp/js/openid-jquery.js +++ /dev/null @@ -1,240 +0,0 @@ -/* -Simple OpenID Plugin -https://code.google.com/p/openid-selector/ - -This code is licenced under the New BSD License. -*/ - -var providers_large = { - google: { - name: 'Google', - url: 'https://www.google.com/accounts/o8/id' - }, - yahoo: { - name: 'Yahoo', - url: 'https://me.yahoo.com/' - }, - aol: { - name: 'AOL', - label: 'Enter your AOL screenname.', - url: 'https://openid.aol.com/{username}' - }, - verisign: { - name: 'Verisign', - label: 'Your Verisign username', - url: 'http://{username}.pip.verisignlabs.com/' - }, - openid: { - name: 'OpenID', - label: 'Enter your OpenID.', - url: null - } -}; -var providers_small = { - myopenid: { - name: 'MyOpenID', - label: 'Enter your MyOpenID username.', - url: 'http://{username}.myopenid.com/' - }, - livejournal: { - name: 'LiveJournal', - label: 'Enter your Livejournal username.', - url: 'http://{username}.livejournal.com/' - }, - flickr: { - name: 'Flickr', - label: 'Enter your Flickr username.', - url: 'https://flickr.com/{username}/' - }, - technorati: { - name: 'Technorati', - label: 'Enter your Technorati username.', - url: 'https://technorati.com/people/technorati/{username}/' - }, - wordpress: { - name: 'Wordpress', - label: 'Enter your Wordpress.com username.', - url: 'http://{username}.wordpress.com/' - }, - blogger: { - name: 'Blogger', - label: 'Your Blogger account', - url: 'http://{username}.blogspot.com/' - }, - vidoop: { - name: 'Vidoop', - label: 'Your Vidoop username', - url: 'http://{username}.myvidoop.com/' - }, - claimid: { - name: 'ClaimID', - label: 'Your ClaimID username', - url: 'https://claimid.com/{username}' - } -}; -var providers = $.extend({}, providers_large, providers_small); - -var openid = { - - demo: false, - ajaxHandler: null, - cookie_expires: 6*30, // 6 months. - cookie_name: 'openid_provider', - cookie_path: '/', - - img_path: 'images/', - - input_id: null, - provider_url: null, - provider_id: null, - - init: function(input_id) { - - var openid_btns = $('#openid_btns'); - - this.input_id = input_id; - - $('#openid_choice').show(); - $('#openid_input_area').empty(); - - // add box for each provider - for (id in providers_large) { - - openid_btns.append(this.getBoxHTML(providers_large[id], 'large', '.gif')); - } - if (providers_small) { - openid_btns.append('<br/>'); - - for (id in providers_small) { - - openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.ico')); - } - } - - $('#openid_form').submit(this.submit); - - var box_id = this.readCookie(); - if (box_id) { - this.signin(box_id, true); - } - }, - getBoxHTML: function(provider, box_size, image_ext) { - - var box_id = provider["name"].toLowerCase(); - return '<a title="'+provider["name"]+'" href="javascript: openid.signin(\''+ box_id +'\');"' + - ' style="background: #FFF url(' + this.img_path + box_id + image_ext+') no-repeat center center" ' + - 'class="' + box_id + ' openid_' + box_size + '_btn"></a>'; - - }, - /* Provider image click */ - signin: function(box_id, onload) { - - var provider = providers[box_id]; - if (! provider) { - return; - } - - this.highlight(box_id); - this.setCookie(box_id); - - this.provider_id = box_id; - this.provider_url = provider['url']; - - // prompt user for input? - if (provider['label']) { - this.useInputBox(provider); - } else { - $('#openid_input_area').empty(); - if (! onload) { - $('#openid_form').submit(); - } - } - }, - /* Sign-in button click */ - submit: function() { - - var url = openid.provider_url; - if (url) { - url = url.replace('{username}', $('#openid_username').val()); - openid.setOpenIdUrl(url); - } - if(openid.ajaxHandler) { - openid.ajaxHandler(openid.provider_id, document.getElementById(openid.input_id).value); - return false; - } - if(openid.demo) { - alert("In client demo mode. Normally would have submitted OpenID:\r\n" + document.getElementById(openid.input_id).value); - return false; - } - return true; - }, - setOpenIdUrl: function (url) { - - var hidden = document.getElementById(this.input_id); - if (hidden != null) { - hidden.value = url; - } else { - $('#openid_form').append('<input type="hidden" id="' + this.input_id + '" name="' + this.input_id + '" value="'+url+'"/>'); - } - }, - highlight: function (box_id) { - - // remove previous highlight. - var highlight = $('#openid_highlight'); - if (highlight) { - highlight.replaceWith($('#openid_highlight a')[0]); - } - // add new highlight. - $('.'+box_id).wrap('<div id="openid_highlight"></div>'); - }, - setCookie: function (value) { - - var date = new Date(); - date.setTime(date.getTime()+(this.cookie_expires*24*60*60*1000)); - var expires = "; expires="+date.toGMTString(); - - document.cookie = this.cookie_name+"="+value+expires+"; path=" + this.cookie_path; - }, - readCookie: function () { - var nameEQ = this.cookie_name + "="; - var ca = document.cookie.split(';'); - for(var i=0;i < ca.length;i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1,c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); - } - return null; - }, - useInputBox: function (provider) { - - var input_area = $('#openid_input_area'); - - var html = ''; - var id = 'openid_username'; - var value = ''; - var label = provider['label']; - var style = ''; - - if (label) { - html = '<p>' + label + '</p>'; - } - if (provider['name'] == 'OpenID') { - id = this.input_id; - value = 'http://'; - style = 'background:#FFF url('+this.img_path+'openid-inputicon.gif) no-repeat scroll 0 50%; padding-left:18px;'; - } - html += '<input id="'+id+'" type="text" style="'+style+'" name="'+id+'" value="'+value+'" />' + - '<input id="openid_submit" type="submit" value="Sign-In"/>'; - - input_area.empty(); - input_area.append(html); - - $('#'+id).focus(); - }, - setDemoMode: function (demoMode) { - this.demo = demoMode; - }, - setAjaxHandler: function (ajaxFunction) { - this.ajaxHandler = ajaxFunction; - } -}; diff --git a/samples/xml/openid/src/main/webapp/openidlogin.jsp b/samples/xml/openid/src/main/webapp/openidlogin.jsp deleted file mode 100644 index 6f719ca9b9a..00000000000 --- a/samples/xml/openid/src/main/webapp/openidlogin.jsp +++ /dev/null @@ -1,71 +0,0 @@ -<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core' %> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<?xml version="1.0" encoding="utf-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> - <title>OpenID Login</title> - - <!-- Simple OpenID Selector --> - <link rel="stylesheet" href="<c:url value='/css/openid.css'/>" /> - <script type="text/javascript" src="<c:url value='/js/jquery-3.5.1.min.js'/>"></script> - <script type="text/javascript" src="<c:url value='/js/openid-jquery.js'/>"></script> - - <script type="text/javascript"> - $(document).ready(function() { - openid.init('openid_identifier'); - // openid.setDemoMode(true); Stops form submission for client javascript-only test purposes - }); - </script> - <!-- /Simple OpenID Selector --> - - <style type="text/css"> - /* Basic page formatting. */ - body { - font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; - } - </style> -</head> - -<body> - -<p><strong> -NOTE: The OpenID 1.0 and 2.0 protocols have been deprecated and users are -<a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to migrate</a> -to <a href="https://openid.net/connect/">OpenID Connect</a>, which is supported by <code>spring-security-oauth2</code>. -</strong></p> - -<c:if test="${not empty param.login_error}"> - <font color="red"> - Your login attempt was not successful, try again.<br/><br/> - Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>. - </font> -</c:if> - -<!-- Simple OpenID Selector --> -<form:form action="login/openid" method="post" id="openid_form"> - <input type="hidden" name="action" value="verify" /> - - <fieldset> - <legend>Sign-in or Create New Account</legend> - - <div id="openid_choice"> - <p>Please click your account provider:</p> - <div id="openid_btns"></div> - - </div> - - <div id="openid_input_area"> - <input id="openid_identifier" name="openid_identifier" type="text" value="http://" /> - <input id="openid_submit" type="submit" value="Sign-In"/> - </div> - <noscript> - <p>OpenID is a service that allows you to log-on to many different websites using a single identity. - Find out <a href="https://openid.net/what/">more about OpenID</a> and <a href="https://openid.net/get/">how to get an OpenID enabled account</a>.</p> - </noscript> - </fieldset> -</form:form> -<!-- /Simple OpenID Selector --> - -</body> -</html> diff --git a/samples/xml/preauth/realm.properties b/samples/xml/preauth/realm.properties deleted file mode 100644 index 91ec2184ec5..00000000000 --- a/samples/xml/preauth/realm.properties +++ /dev/null @@ -1,3 +0,0 @@ -rod: koala,ROLE_SUPERVISOR,ROLE_USER -bob: bobspassword,ROLE_USER -user: password \ No newline at end of file diff --git a/samples/xml/preauth/spring-security-samples-xml-preauth.gradle b/samples/xml/preauth/spring-security-samples-xml-preauth.gradle deleted file mode 100644 index 6645d9e6055..00000000000 --- a/samples/xml/preauth/spring-security-samples-xml-preauth.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-web') - runtime slf4jDependencies - - testCompile project(':spring-security-config') - testCompile project(':spring-security-web') -} - -//jettyRun { -// userRealms = [jettyRun.class.classLoader.loadClass('org.mortbay.jetty.security.HashUserRealm').newInstance('Preauth Realm' '$projectDir/realm.properties')] -//} diff --git a/samples/xml/preauth/src/main/resources/logback.xml b/samples/xml/preauth/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/preauth/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index b9d5ed061d0..00000000000 --- a/samples/xml/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - - Sample namespace-based configuration - - - --> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:sec="http://www.springframework.org/schema/security" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <sec:http> - <sec:intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/> - <sec:intercept-url pattern="/secure/**" access="hasRole('ROLE_USER')"/> - <sec:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/> - - <sec:jee mappable-roles="ROLE_USER,ROLE_SUPERVISOR" /> - - <sec:logout /> - </sec:http> - - <sec:authentication-manager /> - -</beans> diff --git a/samples/xml/preauth/src/main/webapp/WEB-INF/web.xml b/samples/xml/preauth/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 5bac977c9fa..00000000000 --- a/samples/xml/preauth/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <display-name>Spring Security Preauthentication Demo Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <filter> - <filter-name>filterChainProxy</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>filterChainProxy</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <login-config> - <auth-method>BASIC</auth-method> - <realm-name>Preauth Realm</realm-name> - </login-config> - - <security-role> - <role-name>ROLE_USER</role-name> - </security-role> - <security-role> - <role-name>ROLE_SUPERVISOR</role-name> - </security-role> - <security-constraint> - <web-resource-collection> - <web-resource-name>All areas</web-resource-name> - <url-pattern>/*</url-pattern> - </web-resource-collection> - <auth-constraint> - <role-name>ROLE_USER</role-name> - </auth-constraint> - </security-constraint> - -</web-app> diff --git a/samples/xml/preauth/src/main/webapp/index.jsp b/samples/xml/preauth/src/main/webapp/index.jsp deleted file mode 100644 index c815d484a30..00000000000 --- a/samples/xml/preauth/src/main/webapp/index.jsp +++ /dev/null @@ -1,11 +0,0 @@ -<html> -<body> -<h1>Home Page</h1> -<p>Anyone can view this page.</p> - -<p>Your principal object is....: <%= request.getUserPrincipal() %></p> - -<p><a href="secure/index.jsp">Secure page</a></p> -<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/preauth/src/main/webapp/secure/extreme/index.jsp b/samples/xml/preauth/src/main/webapp/secure/extreme/index.jsp deleted file mode 100644 index a2c81f204fd..00000000000 --- a/samples/xml/preauth/src/main/webapp/secure/extreme/index.jsp +++ /dev/null @@ -1,10 +0,0 @@ - -<html> -<body> -<h1>VERY Secure Page</h1> -This is a protected page. You can only see me if you are a supervisor. - -<p><a href="../../">Home</a> -<p><a href="../../logout">Logout</a> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/preauth/src/main/webapp/secure/index.jsp b/samples/xml/preauth/src/main/webapp/secure/index.jsp deleted file mode 100644 index 4fc9f78277e..00000000000 --- a/samples/xml/preauth/src/main/webapp/secure/index.jsp +++ /dev/null @@ -1,15 +0,0 @@ -<html> -<body> -<h1>Secure Page</h1> -This is a protected page. You can get to me if you've been remembered, -or if you've authenticated this session.<br><br> - -<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %> - You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br> -<% } %> - - -<p><a href="../">Home</a> -<p><a href="../logout">Logout</a> -</body> -</html> \ No newline at end of file diff --git a/samples/xml/preauth/src/test/java/sample/PreAuthXmlTests.java b/samples/xml/preauth/src/test/java/sample/PreAuthXmlTests.java deleted file mode 100644 index d5875080c6d..00000000000 --- a/samples/xml/preauth/src/test/java/sample/PreAuthXmlTests.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.web.FilterChainProxy; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; - -/** - * @author Rob Winch - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext-security.xml") -@WebAppConfiguration -public class PreAuthXmlTests { - @Autowired - FilterChainProxy filterChainProxy; - - @Test - public void configLoads() {} -} diff --git a/samples/xml/preauth/src/test/resources/logback-test.xml b/samples/xml/preauth/src/test/resources/logback-test.xml deleted file mode 100644 index 2d51ba4180a..00000000000 --- a/samples/xml/preauth/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <logger name="org.springframework.security" level="${sec.log.level:-WARN}"/> - - - <root level="${root.level:-WARN}"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/servletapi/spring-security-samples-xml-servletapi.gradle b/samples/xml/servletapi/spring-security-samples-xml-servletapi.gradle deleted file mode 100644 index e3a9e76e1d3..00000000000 --- a/samples/xml/servletapi/spring-security-samples-xml-servletapi.gradle +++ /dev/null @@ -1,21 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-core') - compile project(':spring-security-web') - compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-web' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-taglibs') - runtime jstlDependencies - runtime slf4jDependencies - runtime 'org.springframework:spring-context-support' -} - -eclipse.wtp.component.contextPath = 'servletapi' diff --git a/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/LoginForm.java b/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/LoginForm.java deleted file mode 100644 index f635fb2f256..00000000000 --- a/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/LoginForm.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.servletapi.mvc; - -/** - * - * @author Rob Winch - * - */ -public class LoginForm { - private String username; - private String password; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} \ No newline at end of file diff --git a/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/ServletApiController.java b/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/ServletApiController.java deleted file mode 100644 index bdfd1a69883..00000000000 --- a/samples/xml/servletapi/src/main/java/org/springframework/security/samples/servletapi/mvc/ServletApiController.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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.springframework.security.samples.servletapi.mvc; - -import java.io.IOException; -import java.security.Principal; - -import javax.naming.AuthenticationException; -import javax.servlet.AsyncContext; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Controller; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.servlet.mvc.support.RedirectAttributes; - -/** - * A Spring MVC Controller that demonstrates Spring Security's integration with the - * standard Servlet API's. Specifically it demonstrates the following: - * <ul> - * <li>{@link #authenticate(HttpServletRequest, HttpServletResponse)} - Integration with - * {@link HttpServletRequest#authenticate(HttpServletResponse)}</li> - * <li>{@link #login(HttpServletRequest, HttpServletResponse, LoginForm, BindingResult)} - - * Integration with {@link HttpServletRequest#login(String, String)}</li> - * <li>{@link #logout(HttpServletRequest, HttpServletResponse, RedirectAttributes)} - Integration with - * {@link HttpServletRequest#logout()}</li> - * <li>{@link #remoteUser(HttpServletRequest)} - Integration with - * {@link HttpServletRequest#getRemoteUser()}</li> - * <li>{@link #userPrincipal(HttpServletRequest)} - Integration with - * {@link HttpServletRequest#getUserPrincipal()}</li> - * <li>{@link #authentication(Authentication)} - Spring MVC's ability to resolve the - * {@link Authentication} since it is found on - * {@link HttpServletRequest#getUserPrincipal()}</li> - * </ul> - * - * @author Rob Winch - * - */ -@Controller -public class ServletApiController { - /** - * Demonstrates that {@link HttpServletRequest#authenticate(HttpServletResponse)} will - * send the user to the log in page configured within Spring Security if the user is - * not already authenticated. - * - * @param request - * @param response - * @return - * @throws ServletException - * @throws IOException - */ - @RequestMapping("/authenticate") - public String authenticate(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - boolean authenticate = request.authenticate(response); - return authenticate ? "index" : null; - } - - /** - * Demonstrates that you can authenticate with Spring Security using - * {@link HttpServletRequest#login(String, String)}. - * - * <p> - * If we fail to authenticate, a {@link ServletException} is thrown that wraps the - * original {@link AuthenticationException} from Spring Security. This means we can - * catch the {@link ServletException} to display the error message. Alternatively, we - * could allow the {@link ServletException} to propegate and Spring Security's - * {@link ExceptionTranslationFilter} would catch it and process it appropriately. - * </p> - * <p> - * In this method we choose to use Spring MVC's {@link ModelAttribute} to make things - * easier for our form. However, this is not necessary. We could have just as easily - * obtained the request parameters from the {@link HttpServletRequest} object. - * Remember all of these examples would work in a standard {@link Servlet} or anything - * with access to the {@link HttpServletRequest} and {@link HttpServletResponse}. - * </p> - * - * @param request - * @param response - * @param loginForm - * @param result - * @return - */ - @RequestMapping(value = "/login", method = RequestMethod.POST) - public String login(HttpServletRequest request, HttpServletResponse response, - @ModelAttribute LoginForm loginForm, BindingResult result) { - try { - request.login(loginForm.getUsername(), loginForm.getPassword()); - } - catch (ServletException authenticationFailed) { - result.rejectValue(null, "authentication.failed", - authenticationFailed.getMessage()); - return "login"; - } - return "redirect:/"; - } - - /** - * Demonstrates that invoking {@link HttpServletRequest#logout()} will log the user - * out. Note that the response does not get processed, so you need to write something - * to the response. - * @param request - * @param response - * @param redirect - * @return - * @throws ServletException - */ - @RequestMapping("/logout") - public String logout(HttpServletRequest request, HttpServletResponse response, - RedirectAttributes redirect) throws ServletException { - request.logout(); - return "redirect:/"; - } - - /** - * Demonstrates Spring Security with {@link AsyncContext#start(Runnable)}. Spring - * Security will automatically transfer the {@link SecurityContext} from the thread - * that {@link AsyncContext#start(Runnable)} is invoked to the new Thread that invokes - * the {@link Runnable}. - * @param request - * @param response - */ - @RequestMapping("/async") - public void asynch(HttpServletRequest request, HttpServletResponse response) { - final AsyncContext async = request.startAsync(); - async.start(() -> { - Authentication authentication = SecurityContextHolder.getContext() - .getAuthentication(); - try { - final HttpServletResponse asyncResponse = (HttpServletResponse) async - .getResponse(); - asyncResponse.setStatus(HttpServletResponse.SC_OK); - asyncResponse.getWriter().write(String.valueOf(authentication)); - async.complete(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - - /** - * Demonstrates that Spring Security automatically populates - * {@link HttpServletRequest#getRemoteUser()} with the current username. - * @param request - * @return - */ - @ModelAttribute("remoteUser") - public String remoteUser(HttpServletRequest request) { - return request.getRemoteUser(); - } - - /** - * Demonstrates that Spring Security automatically populates - * {@link HttpServletRequest#getUserPrincipal()} with the {@link Authentication} that - * is present on {@link SecurityContextHolder#getContext()} - * @param request - * @return - */ - @ModelAttribute("userPrincipal") - public Principal userPrincipal(HttpServletRequest request) { - return request.getUserPrincipal(); - } - - /** - * Spring MVC will automatically resolve any object that implements {@link Principal} - * using {@link HttpServletRequest#getUserPrincipal()}. This means you can easily - * resolve the {@link Authentication} just by adding it as an argument to your MVC - * controller. Alternatively, you could also have an argument of type - * {@link Principal} which would not couple your controller to Spring Security. - * @param authentication - * @return - */ - @ModelAttribute - public Authentication authentication(Authentication authentication) { - return authentication; - } - - @RequestMapping("/") - public String welcome() { - return "index"; - } - - @RequestMapping(value = "/login", method = RequestMethod.GET) - public String login(@ModelAttribute LoginForm loginForm) { - return "login"; - } -} diff --git a/samples/xml/servletapi/src/main/resources/applicationContext-security.xml b/samples/xml/servletapi/src/main/resources/applicationContext-security.xml deleted file mode 100644 index cdaef7ea0fa..00000000000 --- a/samples/xml/servletapi/src/main/resources/applicationContext-security.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<b:beans xmlns:b="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://www.springframework.org/schema/security" - xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - <http auto-config="true"> - <intercept-url pattern="/**" access="permitAll"/> - </http> - - <authentication-manager> - <authentication-provider> - <user-service> - <user name="user" password="password" authorities="ROLE_USER" /> - <user name="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN"/> - </user-service> - </authentication-provider> - </authentication-manager> -</b:beans> diff --git a/samples/xml/servletapi/src/main/resources/logback.xml b/samples/xml/servletapi/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/servletapi/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/servletapi/src/main/webapp/WEB-INF/spring-servlet.xml b/samples/xml/servletapi/src/main/webapp/WEB-INF/spring-servlet.xml deleted file mode 100644 index c8962958fb4..00000000000 --- a/samples/xml/servletapi/src/main/webapp/WEB-INF/spring-servlet.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<b:beans xmlns="http://www.springframework.org/schema/mvc" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:b="http://www.springframework.org/schema/beans" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> - - <context:component-scan base-package="org.springframework.security.samples.servletapi.mvc" /> - - <!-- Enables the Spring MVC @Controller programming model --> - <annotation-driven/> - - <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory --> - <resources mapping="/resources/**" location="/resources/" /> - - <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> - <b:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" - p:prefix="/WEB-INF/views/" - p:suffix=".jsp" /> - -</b:beans> diff --git a/samples/xml/servletapi/src/main/webapp/WEB-INF/views/index.jsp b/samples/xml/servletapi/src/main/webapp/WEB-INF/views/index.jsp deleted file mode 100644 index 8d829bf930e..00000000000 --- a/samples/xml/servletapi/src/main/webapp/WEB-INF/views/index.jsp +++ /dev/null @@ -1,44 +0,0 @@ -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<html> -<head> -<title>Welcome</title> -</head> -<body> - <h1>Home Page</h1> - <p> - Anyone can view this page. - </p> - - <sec:authorize access="authenticated" var="authenticated"/> - <c:if test="${authenticated}"> - <p>You are currently authenticated</p> - <dl> - <dt>HttpServletRequest.getRemoteUser()</dt> - <dd><c:out value="${remoteUser}"/></dd> - <dt>HttpServletRequest.getUserPrincipal()</dt> - <dd><c:out value="${userPrincipal}"/></dd> - <dt>Authentication</dt> - <dd><c:out value="${authentication}"/></dd> - </dl> - </c:if> - <ul> - <li> - <a href="<c:url value="/authenticate"/>">HttpServletRequest.authenticate(HttpServletResponse)</a> - - if you are authenticated already will simply return true. Otherwise, will redirect you to the log in page configured in your Spring Security configuration. - </li> - <li> - <a href="<c:url value="/async"/>">AsyncContext.start(Runnable)</a> - - will automatically transfer the current SecurityContext to the new Thread - </li> - <c:choose> - <c:when test="${authenticated}"> - <li><a href="<c:url value="/logout"/>">HttpServletRequest.logout()</a></li> - </c:when> - <c:otherwise> - <li><a href="<c:url value="/login"/>">Fill out log in form</a> - allows the user to invoke HttpServletRequest.login(String,String)</li> - </c:otherwise> - </c:choose> - </ul> -</body> -</html> diff --git a/samples/xml/servletapi/src/main/webapp/WEB-INF/views/login.jsp b/samples/xml/servletapi/src/main/webapp/WEB-INF/views/login.jsp deleted file mode 100644 index 671a34634e0..00000000000 --- a/samples/xml/servletapi/src/main/webapp/WEB-INF/views/login.jsp +++ /dev/null @@ -1,26 +0,0 @@ -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<html> -<head> -<title>Please Log In</title> -</head> -<body> - <h1>Please Log In</h1> - <p>This page demonstrates the use of HttpServletRequest.login(String,String) integration with Spring Security.</p> - <form:form action="./login" method="post" modelAttribute="loginForm"> - <form:errors/> - <p> - <label for="username">Username</label> - <form:input path="username"/> - </p> - <p> - <label for="password">Password</label> - <form:password path="password"/> - </p> - <p> - <input type="submit" value="Log In"/> - </p> - </form:form> -</body> -</html> diff --git a/samples/xml/servletapi/src/main/webapp/WEB-INF/web.xml b/samples/xml/servletapi/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index f9baaac4d9c..00000000000 --- a/samples/xml/servletapi/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<web-app xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" - version="3.0"> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:applicationContext-security.xml - </param-value> - </context-param> - - <!-- Nothing below here needs to be modified --> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>servletapi.root</param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - <async-supported>true</async-supported> - </filter> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - <dispatcher>REQUEST</dispatcher> - <dispatcher>ASYNC</dispatcher> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - - The application context is then available via - - WebApplicationContextUtils.getWebApplicationContext(servletContext). - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <servlet> - <servlet-name>spring</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>1</load-on-startup> - <async-supported>true</async-supported> - </servlet> - <servlet-mapping> - <servlet-name>spring</servlet-name> - <url-pattern>/</url-pattern> - </servlet-mapping> -</web-app> diff --git a/samples/xml/tutorial/readme.txt b/samples/xml/tutorial/readme.txt deleted file mode 100644 index bfa6a48f5d4..00000000000 --- a/samples/xml/tutorial/readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -This is the most basic sample web application for Spring Security. - -If you have gradle installed, you can run the application directly from the checked out source tree, using the command - -gradle jettyRun - -This will start jetty on port 8080, with SSL support on port 8443. - -The 'scripts' directory contains simple Unix command-line scripts for exercising the application and displaying the results. - diff --git a/samples/xml/tutorial/scripts/exec-list-as-peter b/samples/xml/tutorial/scripts/exec-list-as-peter deleted file mode 100755 index 04e85f122fc..00000000000 --- a/samples/xml/tutorial/scripts/exec-list-as-peter +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=peter --http-password=opal --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/listAccounts.html -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-list-as-rod b/samples/xml/tutorial/scripts/exec-list-as-rod deleted file mode 100755 index 84d5549cee5..00000000000 --- a/samples/xml/tutorial/scripts/exec-list-as-rod +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=rod --http-password=koala --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/listAccounts.html -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-list-no-auth b/samples/xml/tutorial/scripts/exec-list-no-auth deleted file mode 100755 index 343bcfcf6c6..00000000000 --- a/samples/xml/tutorial/scripts/exec-list-no-auth +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/listAccounts.html -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-list-wrong-password b/samples/xml/tutorial/scripts/exec-list-wrong-password deleted file mode 100755 index 426f98db576..00000000000 --- a/samples/xml/tutorial/scripts/exec-list-wrong-password +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=rod --http-password=WRONG-PASSWORD --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/listAccounts.html -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-post-as-peter b/samples/xml/tutorial/scripts/exec-post-as-peter deleted file mode 100755 index ab4b7f03b3a..00000000000 --- a/samples/xml/tutorial/scripts/exec-post-as-peter +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=peter --http-password=opal --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/post.html?id=1\&amount=5.00 -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-post-as-rod b/samples/xml/tutorial/scripts/exec-post-as-rod deleted file mode 100755 index 7a9ce7f379b..00000000000 --- a/samples/xml/tutorial/scripts/exec-post-as-rod +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=rod --http-password=koala --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/post.html?id=1\&amount=5.00 -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-post-no-auth b/samples/xml/tutorial/scripts/exec-post-no-auth deleted file mode 100755 index 7b702cb0a23..00000000000 --- a/samples/xml/tutorial/scripts/exec-post-no-auth +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/post.html?id=1\&amount=5.00 -cat -n work/* | less diff --git a/samples/xml/tutorial/scripts/exec-post-wrong-password b/samples/xml/tutorial/scripts/exec-post-wrong-password deleted file mode 100755 index 17b64aa0951..00000000000 --- a/samples/xml/tutorial/scripts/exec-post-wrong-password +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -rm -rf work -mkdir work -wget --http-user=rod --http-password=WRONG_PASSWORD --directory-prefix=work --output-file=work/log.txt http://localhost:8080/spring-security-samples-tutorial/post.html?id=1\&amount=5.00 -cat -n work/* | less diff --git a/samples/xml/tutorial/spring-security-samples-xml-tutorial.gradle b/samples/xml/tutorial/spring-security-samples-xml-tutorial.gradle deleted file mode 100644 index 8c520fdb2b1..00000000000 --- a/samples/xml/tutorial/spring-security-samples-xml-tutorial.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'io.spring.convention.spring-sample-war' - -dependencies { - compile project(':spring-security-core') - compile slf4jDependencies - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-web' - compile 'org.springframework:spring-webmvc' - - providedCompile 'javax.servlet:javax.servlet-api' - - runtime project(':spring-security-config') - runtime project(':spring-security-taglibs') - runtime project(':spring-security-web') - runtime jstlDependencies -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/Account.java b/samples/xml/tutorial/src/main/java/bigbank/Account.java deleted file mode 100644 index 438df6aa77a..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/Account.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -/** - * Note this class does not represent best practice, as we are failing to encapsulate - * business logic (methods) and state in the domain object. Nevertheless, this demo is - * intended to reflect what people usually do, as opposed to what they ideally would be - * doing. - * - * @author Ben Alex - */ -public class Account { - private long id = -1; - private String holder; - private double balance; - private double overdraft = 100.00; - - public Account(String holder) { - this.holder = holder; - } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getHolder() { - return holder; - } - - public void setHolder(String holder) { - this.holder = holder; - } - - public double getBalance() { - return balance; - } - - public void setBalance(double balance) { - this.balance = balance; - } - - public double getOverdraft() { - return overdraft; - } - - public void setOverdraft(double overdraft) { - this.overdraft = overdraft; - } - - @Override - public String toString() { - return "Account[id=" + id + ",balance=" + balance + ",holder=" + holder - + ", overdraft=" + overdraft + "]"; - } -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/BankDao.java b/samples/xml/tutorial/src/main/java/bigbank/BankDao.java deleted file mode 100644 index 84ebc6c0774..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/BankDao.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -public interface BankDao { - Account readAccount(Long id); - - void createOrUpdateAccount(Account account); - - Account[] findAccounts(); -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/BankDaoStub.java b/samples/xml/tutorial/src/main/java/bigbank/BankDaoStub.java deleted file mode 100644 index 0a782379fd3..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/BankDaoStub.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -import java.util.HashMap; -import java.util.Map; - -public class BankDaoStub implements BankDao { - private long id = 0; - private final Map<Long, Account> accounts = new HashMap<>(); - - public void createOrUpdateAccount(Account account) { - if (account.getId() == -1) { - id++; - account.setId(id); - } - accounts.put(account.getId(), account); - System.out.println("SAVE: " + account); - } - - public Account[] findAccounts() { - Account[] accounts = this.accounts.values().toArray(new Account[] {}); - System.out.println("Returning " + accounts.length + " account(s):"); - for (Account account : accounts) { - System.out.println(" > " + account); - } - return accounts; - } - - public Account readAccount(Long id) { - return accounts.get(id); - } - -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/BankService.java b/samples/xml/tutorial/src/main/java/bigbank/BankService.java deleted file mode 100644 index 5f76bb98f32..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/BankService.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -import org.springframework.security.access.prepost.PreAuthorize; - -public interface BankService { - - Account readAccount(Long id); - - Account[] findAccounts(); - - @PreAuthorize("hasAuthority('supervisor') or " - + "hasAuthority('teller') and (#account.balance + #amount >= -#account.overdraft)") - Account post(Account account, double amount); -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/BankServiceImpl.java b/samples/xml/tutorial/src/main/java/bigbank/BankServiceImpl.java deleted file mode 100644 index e12f22845af..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/BankServiceImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -import org.springframework.util.Assert; - -public class BankServiceImpl implements BankService { - private final BankDao bankDao; - - public BankServiceImpl(BankDao bankDao) { - Assert.notNull(bankDao, "bankDao cannot be null"); - this.bankDao = bankDao; - } - - public Account[] findAccounts() { - return this.bankDao.findAccounts(); - } - - public Account post(Account account, double amount) { - Assert.notNull(account, "account cannot be null"); - - // We read account back from DAO so it reflects the latest balance - Account a = bankDao.readAccount(account.getId()); - if (a == null) { - throw new IllegalArgumentException("Couldn't find requested account"); - } - - a.setBalance(a.getBalance() + amount); - bankDao.createOrUpdateAccount(a); - return a; - } - - public Account readAccount(Long id) { - return bankDao.readAccount(id); - } -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/SeedData.java b/samples/xml/tutorial/src/main/java/bigbank/SeedData.java deleted file mode 100644 index e4b15389e98..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/SeedData.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - -public class SeedData implements InitializingBean { - private BankDao bankDao; - - public void afterPropertiesSet() { - Assert.notNull(bankDao, "bankDao cannot be null"); - bankDao.createOrUpdateAccount(new Account("rod")); - bankDao.createOrUpdateAccount(new Account("dianne")); - bankDao.createOrUpdateAccount(new Account("scott")); - bankDao.createOrUpdateAccount(new Account("peter")); - } - - public void setBankDao(BankDao bankDao) { - this.bankDao = bankDao; - } - -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/web/ListAccounts.java b/samples/xml/tutorial/src/main/java/bigbank/web/ListAccounts.java deleted file mode 100644 index 7fd239b1400..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/web/ListAccounts.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank.web; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.util.Assert; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.Controller; - -import bigbank.BankService; - -public class ListAccounts implements Controller { - - private final BankService bankService; - - public ListAccounts(BankService bankService) { - Assert.notNull(bankService, "bankService cannot be null"); - this.bankService = bankService; - } - - public ModelAndView handleRequest(HttpServletRequest request, - HttpServletResponse response) { - // Security check (this is unnecessary if Spring Security is performing the - // authorization) - // if (request.getUserPrincipal() == null) { - // throw new - // AuthenticationCredentialsNotFoundException("You must login to view the account list (Spring Security message)"); - // // only for Spring Security managed authentication - // } - - // Actual business logic - ModelAndView mav = new ModelAndView("listAccounts"); - mav.addObject("accounts", bankService.findAccounts()); - return mav; - } - -} diff --git a/samples/xml/tutorial/src/main/java/bigbank/web/PostAccounts.java b/samples/xml/tutorial/src/main/java/bigbank/web/PostAccounts.java deleted file mode 100644 index e32ea4a0b75..00000000000 --- a/samples/xml/tutorial/src/main/java/bigbank/web/PostAccounts.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * 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 - * - * https://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 bigbank.web; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.util.Assert; -import org.springframework.web.bind.ServletRequestUtils; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.Controller; - -import bigbank.Account; -import bigbank.BankService; - -public class PostAccounts implements Controller { - - private final BankService bankService; - - public PostAccounts(BankService bankService) { - Assert.notNull(bankService, "bankService cannot be null"); - this.bankService = bankService; - } - - public ModelAndView handleRequest(HttpServletRequest request, - HttpServletResponse response) throws Exception { - // Security check (this is unnecessary if Spring Security is performing the - // authorization) - // if (!request.isUserInRole("ROLE_TELLER")) { - // throw new - // AccessDeniedException("You must be a teller to post transactions (Spring Security message)"); - // } - - // Actual business logic - Long id = ServletRequestUtils.getRequiredLongParameter(request, "id"); - Double amount = ServletRequestUtils.getRequiredDoubleParameter(request, "amount"); - Account a = bankService.readAccount(id); - bankService.post(a, amount); - - return new ModelAndView("redirect:listAccounts.html"); - } - -} diff --git a/samples/xml/tutorial/src/main/resources/applicationContext-business.xml b/samples/xml/tutorial/src/main/resources/applicationContext-business.xml deleted file mode 100644 index 34bc4502ade..00000000000 --- a/samples/xml/tutorial/src/main/resources/applicationContext-business.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:security="http://www.springframework.org/schema/security" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd -http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <bean id="bankDao" class="bigbank.BankDaoStub"/> - - <bean id="seedData" class="bigbank.SeedData"> - <property name="bankDao" ref="bankDao"/> - </bean> - - <bean id="bankService" class="bigbank.BankServiceImpl"> - <constructor-arg ref="bankDao"/> - <!-- This will add a security interceptor to the bean - <security:intercept-methods> - <security:protect method="bigbank.BankService.*" access="IS_AUTHENTICATED_REMEMBERED" /> - <security:protect method="bigbank.BankService.post" access="ROLE_TELLER" /> - </security:intercept-methods> --> - </bean> - -</beans> diff --git a/samples/xml/tutorial/src/main/resources/logback.xml b/samples/xml/tutorial/src/main/resources/logback.xml deleted file mode 100644 index 3ebbcc0ddd6..00000000000 --- a/samples/xml/tutorial/src/main/resources/logback.xml +++ /dev/null @@ -1,12 +0,0 @@ -<configuration> - <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> - <encoder> - <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> - </encoder> - </appender> - - <root level="WARN"> - <appender-ref ref="STDOUT" /> - </root> - -</configuration> diff --git a/samples/xml/tutorial/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/xml/tutorial/src/main/webapp/WEB-INF/applicationContext-security.xml deleted file mode 100644 index 295a81d73b7..00000000000 --- a/samples/xml/tutorial/src/main/webapp/WEB-INF/applicationContext-security.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- -- Sample namespace-based configuration -- ---> - -<beans:beans xmlns="http://www.springframework.org/schema/security" - xmlns:beans="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd"> - - <debug /> - - <global-method-security pre-post-annotations="enabled" /> - - <http pattern="/static/**" security="none"/> - <http pattern="/loggedout.jsp" security="none"/> - - <http> - <intercept-url pattern="/secure/extreme/**" access="hasAuthority('supervisor')"/> - <intercept-url pattern="/secure/**" access="authenticated" /> - <!-- - Allow all other requests. In a real application you should - adopt a whitelisting approach where access is not allowed by default - --> - <intercept-url pattern="/**" access="permitAll" /> - <form-login /> - <logout logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID"/> - <remember-me /> -<!-- - Uncomment to enable X509 client authentication support - <x509 /> ---> - <!-- Uncomment to limit the number of sessions a user can have --> - <session-management invalid-session-url="/timeout.jsp"> - <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> - </session-management> - - </http> - - <!-- - Usernames/Passwords are - rod/koala - dianne/emu - scott/wombat - peter/opal - --> - <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> - - <authentication-manager> - <authentication-provider> - <password-encoder ref="encoder"/> - <user-service> - <user name="rod" password="$2a$10$75pBjapg4Nl8Pzd.3JRnUe7PDJmk9qBGwNEJDAlA3V.dEJxcDKn5O" authorities="supervisor, user, teller" /> - <user name="dianne" password="$2a$04$bCMEyxrdF/7sgfUiUJ6Ose2vh9DAMaVBldS1Bw2fhi1jgutZrr9zm" authorities="user,teller" /> - <user name="scott" password="$2a$06$eChwvzAu3TSexnC3ynw4LOSw1qiEbtNItNeYv5uI40w1i3paoSfLu" authorities="user" /> - <user name="peter" password="$2a$04$8.H8bCMROLF4CIgd7IpeQ.tcBXLP5w8iplO0n.kCIkISwrIgX28Ii" authorities="user" /> - </user-service> - </authentication-provider> - </authentication-manager> - -</beans:beans> diff --git a/samples/xml/tutorial/src/main/webapp/WEB-INF/bank-servlet.xml b/samples/xml/tutorial/src/main/webapp/WEB-INF/bank-servlet.xml deleted file mode 100644 index 32ba61339dd..00000000000 --- a/samples/xml/tutorial/src/main/webapp/WEB-INF/bank-servlet.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - <bean name="/listAccounts.html" class="bigbank.web.ListAccounts"> - <constructor-arg ref="bankService"/> - </bean> - - <bean name="/post.html" class="bigbank.web.PostAccounts"> - <constructor-arg ref="bankService"/> - </bean> - - <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> - <property name="prefix" value="/WEB-INF/jsp/"/> - <property name="suffix" value=".jsp"/> - </bean> - -</beans> diff --git a/samples/xml/tutorial/src/main/webapp/WEB-INF/jsp/listAccounts.jsp b/samples/xml/tutorial/src/main/webapp/WEB-INF/jsp/listAccounts.jsp deleted file mode 100644 index 74eaf5d3f71..00000000000 --- a/samples/xml/tutorial/src/main/webapp/WEB-INF/jsp/listAccounts.jsp +++ /dev/null @@ -1,58 +0,0 @@ -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> -<head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Accounts</title> -</head> -<body> -<div id="content"> - -<h1>Accounts</h1> -<p> -Anyone can view this page, but posting to an Account requires login and must be authorized. Below are some users to try posting to Accounts with. -</p> -<ul> -<li>rod/koala - can post to any Account</li> -<li>dianne/emu - can post to Accounts as long as the balance remains above the overdraft amount</li> -<li>scott/wombat - cannot post to any Accounts</li> -</ul> - -<a href="index.jsp">Home</a><br><br> - -<table> -<tr> -<td><b>ID</b></td> -<td><b>Holder</b></td> -<td><b>Balance</b></td> -<td><b>Overdraft</b></td> -<td><b>Operations</b></td> -</tr> -<c:forEach var="account" items="${accounts}"> -<tr> -<td>${account.id}</td> -<td>${account.holder}</td> -<td>${account.balance}</td> -<td>${account.overdraft}</td> -<td> - <a href="post.html?id=${account.id}&amount=-20.00">-$20</a> - <a href="post.html?id=${account.id}&amount=-5.00">-$5</a> - <a href="post.html?id=${account.id}&amount=5.00">+$5</a> - <a href="post.html?id=${account.id}&amount=20.00">+$20</a> -</td> -</tr> -</c:forEach> -</table> - -<p> -<form action="logout" method="post"> - <sec:csrfInput /> - <input type="submit" value="Logout"/> -</form> -</div> -</body> -</html> diff --git a/samples/xml/tutorial/src/main/webapp/WEB-INF/web.xml b/samples/xml/tutorial/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index adeb2030bd3..00000000000 --- a/samples/xml/tutorial/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Tutorial web application - - - --> - -<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> - - <display-name>Spring Security Tutorial Application</display-name> - - <!-- - - Location of the XML file that defines the root application context - - Applied by ContextLoaderListener. - --> - <context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:applicationContext-business.xml - /WEB-INF/applicationContext-security.xml - </param-value> - </context-param> - - <context-param> - <param-name>webAppRootKey</param-name> - <param-value>tutorial.root</param-value> - </context-param> - - <filter> - <filter-name>springSecurityFilterChain</filter-name> - <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> - </filter> - - <filter-mapping> - <filter-name>springSecurityFilterChain</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> - - <!-- - - Loads the root application context of this web app at startup. - --> - <listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> - </listener> - - <!-- - - Publishes events for session creation and destruction through the application - - context. Optional unless concurrent session control is being used. - --> - <listener> - <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> - </listener> - - <!-- - - Provides core MVC application controller. See bank-servlet.xml. - --> - <servlet> - <servlet-name>bank</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>1</load-on-startup> - </servlet> - - <servlet-mapping> - <servlet-name>bank</servlet-name> - <url-pattern>*.html</url-pattern> - </servlet-mapping> - - <welcome-file-list> - <welcome-file>index.jsp</welcome-file> - </welcome-file-list> - -</web-app> diff --git a/samples/xml/tutorial/src/main/webapp/index.jsp b/samples/xml/tutorial/src/main/webapp/index.jsp deleted file mode 100644 index cf102f3111e..00000000000 --- a/samples/xml/tutorial/src/main/webapp/index.jsp +++ /dev/null @@ -1,42 +0,0 @@ -<%@ page session="false" %> -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Home Page</title> - </head> -<body> -<div id="content"> -<h1>Home Page</h1> -<p> -Anyone can view this page. -</p> -<p> -While anyone can also view the <a href="listAccounts.html">list accounts</a> page, you must be authorized to post to an Account from the list accounts page. -</p> -<p> -Your principal object is....: <%= request.getUserPrincipal() %> -</p> -<sec:authorize url='/secure/index.jsp'> -<p> -You can currently access "/secure" URLs. -</p> -</sec:authorize> -<sec:authorize url='/secure/extreme/index.jsp'> -<p> -You can currently access "/secure/extreme" URLs. -</p> -</sec:authorize> - -<p> -<a href="secure/index.jsp">Secure page</a></p> -<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p> -</div> -</body> -</html> diff --git a/samples/xml/tutorial/src/main/webapp/loggedout.jsp b/samples/xml/tutorial/src/main/webapp/loggedout.jsp deleted file mode 100644 index 14063b04e16..00000000000 --- a/samples/xml/tutorial/src/main/webapp/loggedout.jsp +++ /dev/null @@ -1,20 +0,0 @@ -<%@page session="false" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Logged Out</title> - </head> -<body> -<div id="content"> -<h2>Logged Out</h2> -<p> -You have been logged out. <a href="<c:url value='/'/>">Start again</a>. -</p> -</div> -</body> -</html> diff --git a/samples/xml/tutorial/src/main/webapp/secure/extreme/index.jsp b/samples/xml/tutorial/src/main/webapp/secure/extreme/index.jsp deleted file mode 100644 index 6553fe6c846..00000000000 --- a/samples/xml/tutorial/src/main/webapp/secure/extreme/index.jsp +++ /dev/null @@ -1,29 +0,0 @@ -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> -<head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Secure Page</title> -</head> -<body> -<div id="content"> -<h1>VERY Secure Page</h1> -This is a protected page. You can only see me if you are a supervisor. - -<sec:authorize access="hasAuthority('supervisor')"> -You have authority "supervisor" (this text is surrounded by <sec:authorize> tags). -</sec:authorize> - -<p><a href="../../">Home</a></p> - -<form action="../../logout" method="post"> - <sec:csrfInput /> - <input type="submit" value="Logout"/> -</form> -</div> -</body> -</html> diff --git a/samples/xml/tutorial/src/main/webapp/secure/index.jsp b/samples/xml/tutorial/src/main/webapp/secure/index.jsp deleted file mode 100644 index e41003a808a..00000000000 --- a/samples/xml/tutorial/src/main/webapp/secure/index.jsp +++ /dev/null @@ -1,53 +0,0 @@ -<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> -<head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Secure Page</title> -</head> -<body> -<div id="content"> - -<h1>Secure Page</h1> -<p> -This is a protected page. You can get to me if you've been remembered, -or if you've authenticated this session. -</p> -<p> -<sec:authorize access="hasRole('supervisor')"> - You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br/><br/> -</sec:authorize> -</p> -<h3>Properties obtained using <sec:authentication /> tag</h3> -<table border="1"> -<tr><th>Tag</th><th>Value</th></tr> -<tr> -<td><sec:authentication property='name' /></td><td><sec:authentication property="name"/></td> -</tr> -<sec:authorize access="isAuthenticated()"> -<tr> -<td><sec:authentication property='principal.username' /></td><td><sec:authentication property="principal.username"/></td> -</tr> -<tr> -<td><sec:authentication property='principal.enabled' /></td><td><sec:authentication property="principal.enabled"/></td> -</tr> -<tr> -<td><sec:authentication property='principal.accountNonLocked' /></td><td><sec:authentication property="principal.accountNonLocked"/></td> -</tr> -</sec:authorize> -</table> - - -<p><a href="../">Home</a></p> - -<form action="../logout" method="post"> - <sec:csrfInput /> - <input type="submit" value="Logout"/> -</form> -</div> -</body> -</html> diff --git a/samples/xml/tutorial/src/main/webapp/static/css/tutorial.css b/samples/xml/tutorial/src/main/webapp/static/css/tutorial.css deleted file mode 100644 index 4d98a2a9da2..00000000000 --- a/samples/xml/tutorial/src/main/webapp/static/css/tutorial.css +++ /dev/null @@ -1,13 +0,0 @@ - -body { - font-family:"Palatino Linotype","Book Antiqua",Palatino,serif; -} - -#content { - margin: 5em auto; - width: 40em; -} - -.securityHiddenUI, .securityHiddenUI * { - background-color: #ff4500; -} diff --git a/samples/xml/tutorial/src/main/webapp/timeout.jsp b/samples/xml/tutorial/src/main/webapp/timeout.jsp deleted file mode 100644 index e699a3b2bc2..00000000000 --- a/samples/xml/tutorial/src/main/webapp/timeout.jsp +++ /dev/null @@ -1,20 +0,0 @@ -<%@page session="false" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html> - <head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <link rel="stylesheet" href="<c:url value='/static/css/tutorial.css'/>" type="text/css" /> - <title>Session Timeout</title> - </head> -<body> -<div id="content"> -<h2>Invalid Session</h2> - -<p> -Your session appears to have timed out. Please <a href="<c:url value='/'/>">start again</a>. -</p> -</div> -</body> -</html> diff --git a/settings.gradle b/settings.gradle index 917dc5ef131..8100db4449e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,12 @@ plugins { enableFeaturePreview("VERSION_ORDERING_V2") +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} + rootProject.name = 'spring-security' FileTree buildFiles = fileTree(rootDir) { diff --git a/taglibs/spring-security-taglibs.gradle b/taglibs/spring-security-taglibs.gradle index 2a6896fb6f6..c272ff3c3cd 100644 --- a/taglibs/spring-security-taglibs.gradle +++ b/taglibs/spring-security-taglibs.gradle @@ -1,20 +1,21 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-acl') - compile project(':spring-security-core') - compile project(':spring-security-web') - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-expression' - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-acl') + api project(':spring-security-core') + api project(':spring-security-web') + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-core' + api 'org.springframework:spring-expression' + api 'org.springframework:spring-web' provided 'javax.servlet.jsp:javax.servlet.jsp-api' provided 'javax.servlet:javax.servlet-api' - testRuntime 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' + testRuntimeOnly 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' } configure(project.tasks.withType(Test)) { diff --git a/test/spring-security-test.gradle b/test/spring-security-test.gradle index 6d41565ad46..ea756801edd 100644 --- a/test/spring-security-test.gradle +++ b/test/spring-security-test.gradle @@ -1,10 +1,11 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile project(':spring-security-web') - compile 'org.springframework:spring-core' - compile 'org.springframework:spring-test' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api project(':spring-security-web') + api 'org.springframework:spring-core' + api 'org.springframework:spring-test' optional project(':spring-security-config') optional project(':spring-security-oauth2-client') @@ -16,13 +17,13 @@ dependencies { provided 'javax.servlet:javax.servlet-api' - testCompile project(path : ':spring-security-config', configuration : 'tests') - testCompile 'com.fasterxml.jackson.core:jackson-databind' - testCompile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - testCompile 'io.projectreactor:reactor-test' - testCompile 'javax.xml.bind:jaxb-api' - testCompile 'org.skyscreamer:jsonassert' - testCompile 'org.springframework:spring-webmvc' - testCompile 'org.springframework:spring-tx' - testCompile powerMock2Dependencies + testImplementation project(path : ':spring-security-config', configuration : 'tests') + testImplementation 'com.fasterxml.jackson.core:jackson-databind' + testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'javax.xml.bind:jaxb-api' + testImplementation 'org.skyscreamer:jsonassert' + testImplementation 'org.springframework:spring-webmvc' + testImplementation 'org.springframework:spring-tx' + testImplementation powerMock2Dependencies } diff --git a/web/spring-security-web.gradle b/web/spring-security-web.gradle index 8a4e8efd8cd..58c34092162 100644 --- a/web/spring-security-web.gradle +++ b/web/spring-security-web.gradle @@ -1,13 +1,14 @@ apply plugin: 'io.spring.convention.spring-module' dependencies { - compile project(':spring-security-core') - compile springCoreDependency - compile 'org.springframework:spring-aop' - compile 'org.springframework:spring-beans' - compile 'org.springframework:spring-context' - compile 'org.springframework:spring-expression' - compile 'org.springframework:spring-web' + management platform(project(":spring-security-dependencies")) + api project(':spring-security-core') + api springCoreDependency + api 'org.springframework:spring-aop' + api 'org.springframework:spring-beans' + api 'org.springframework:spring-context' + api 'org.springframework:spring-expression' + api 'org.springframework:spring-web' optional 'com.fasterxml.jackson.core:jackson-databind' optional 'io.projectreactor:reactor-core' @@ -18,14 +19,14 @@ dependencies { provided 'javax.servlet:javax.servlet-api' - testCompile project(path: ':spring-security-core', configuration: 'tests') - testCompile 'commons-codec:commons-codec' - testCompile 'io.projectreactor:reactor-test' - testCompile 'javax.xml.bind:jaxb-api' - testCompile 'org.skyscreamer:jsonassert' - testCompile 'org.springframework:spring-webflux' - testCompile 'org.synchronoss.cloud:nio-multipart-parser' - testCompile powerMock2Dependencies + testImplementation project(path: ':spring-security-core', configuration: 'tests') + testImplementation 'commons-codec:commons-codec' + testImplementation 'io.projectreactor:reactor-test' + testImplementation 'javax.xml.bind:jaxb-api' + testImplementation 'org.skyscreamer:jsonassert' + testImplementation 'org.springframework:spring-webflux' + testImplementation 'org.synchronoss.cloud:nio-multipart-parser' + testImplementation powerMock2Dependencies - testRuntime 'org.hsqldb:hsqldb' + testRuntimeOnly 'org.hsqldb:hsqldb' } diff --git a/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java b/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java index be42b9a89ca..ce1002591f1 100644 --- a/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java +++ b/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java @@ -136,12 +136,7 @@ public void saveContext(SecurityContext context, HttpServletRequest request, Htt SaveContextOnUpdateOrErrorResponseWrapper.class); Assert.state(responseWrapper != null, () -> "Cannot invoke saveContext on response " + response + ". You must use the HttpRequestResponseHolder.response after invoking loadContext"); - // saveContext() might already be called by the response wrapper if something in - // the chain called sendError() or sendRedirect(). This ensures we only call it - // once per request. - if (!responseWrapper.isContextSaved()) { - responseWrapper.saveContext(context); - } + responseWrapper.saveContext(context); } @Override @@ -296,6 +291,8 @@ final class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorRespo private final Authentication authBeforeExecution; + private boolean isSaveContextInvoked; + /** * Takes the parameters required to call <code>saveContext()</code> successfully * in addition to the request and the response object we are wrapping. @@ -339,6 +336,7 @@ protected void saveContext(SecurityContext context) { // SEC-1587 A non-anonymous context may still be in the session // SEC-1735 remove if the contextBeforeExecution was not anonymous httpSession.removeAttribute(springSecurityContextKey); + this.isSaveContextInvoked = true; } if (this.logger.isDebugEnabled()) { if (authentication == null) { @@ -358,6 +356,7 @@ protected void saveContext(SecurityContext context) { // is set SEC-1561 if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) { httpSession.setAttribute(springSecurityContextKey, context); + this.isSaveContextInvoked = true; if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, httpSession)); } @@ -366,7 +365,8 @@ protected void saveContext(SecurityContext context) { } private boolean contextChanged(SecurityContext context) { - return context != this.contextBeforeExecution || context.getAuthentication() != this.authBeforeExecution; + return this.isSaveContextInvoked || context != this.contextBeforeExecution + || context.getAuthentication() != this.authBeforeExecution; } private HttpSession createNewSessionIfAllowed(SecurityContext context) { diff --git a/web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java b/web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java index 6cbdb0c2d21..304d16e8240 100644 --- a/web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java +++ b/web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java @@ -98,6 +98,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m StandardEvaluationContext context = new StandardEvaluationContext(); context.setRootObject(securityContext); context.setVariable("this", securityContext); + context.setBeanResolver(this.beanResolver); Expression expression = this.parser.parseExpression(expressionToParse); securityContextResult = expression.getValue(context); } diff --git a/web/src/test/java/org/springframework/security/web/context/HttpSessionSecurityContextRepositoryTests.java b/web/src/test/java/org/springframework/security/web/context/HttpSessionSecurityContextRepositoryTests.java index aea6eca5029..d122376b4f4 100644 --- a/web/src/test/java/org/springframework/security/web/context/HttpSessionSecurityContextRepositoryTests.java +++ b/web/src/test/java/org/springframework/security/web/context/HttpSessionSecurityContextRepositoryTests.java @@ -16,12 +16,16 @@ package org.springframework.security.web.context; +import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.servlet.Filter; +import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; @@ -31,6 +35,7 @@ import org.junit.After; import org.junit.Test; +import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; @@ -38,10 +43,14 @@ import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Transient; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -162,6 +171,48 @@ public void saveContextCallsSetAttributeIfContextIsModifiedDirectlyDuringRequest verify(session).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, ctx); } + @Test + public void saveContextWhenSaveNewContextThenOriginalContextThenOriginalContextSaved() throws Exception { + HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository(); + SecurityContextPersistenceFilter securityContextPersistenceFilter = new SecurityContextPersistenceFilter( + repository); + + UserDetails original = User.withUsername("user").password("password").roles("USER").build(); + SecurityContext originalContext = createSecurityContext(original); + UserDetails impersonate = User.withUserDetails(original).username("impersonate").build(); + SecurityContext impersonateContext = createSecurityContext(impersonate); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + + Filter saveImpersonateContext = (request, response, chain) -> { + SecurityContextHolder.setContext(impersonateContext); + // ensure the response is committed to trigger save + response.flushBuffer(); + chain.doFilter(request, response); + }; + Filter saveOriginalContext = (request, response, chain) -> { + SecurityContextHolder.setContext(originalContext); + chain.doFilter(request, response); + }; + HttpServlet servlet = new HttpServlet() { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.getWriter().write("Hi"); + } + }; + + SecurityContextHolder.setContext(originalContext); + MockFilterChain chain = new MockFilterChain(servlet, saveImpersonateContext, saveOriginalContext); + + securityContextPersistenceFilter.doFilter(mockRequest, mockResponse, chain); + + assertThat( + mockRequest.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)) + .isEqualTo(originalContext); + } + @Test public void nonSecurityContextInSessionIsIgnored() { HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository(); @@ -577,6 +628,13 @@ public void saveContextWhenTransientAuthenticationWithCustomAnnotationThenSkippe assertThat(session).isNull(); } + private SecurityContext createSecurityContext(UserDetails userDetails) { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, + userDetails.getPassword(), userDetails.getAuthorities()); + SecurityContext securityContext = new SecurityContextImpl(token); + return securityContext; + } + @Transient private static class SomeTransientAuthentication extends AbstractAuthenticationToken { diff --git a/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java b/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java index dfd3d504ca7..a9bb9d009b0 100644 --- a/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java +++ b/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.expression.BeanResolver; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.AuthorityUtils; @@ -37,6 +38,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; +import static org.mockito.BDDMockito.verify; /** * @author Rob Winch @@ -44,13 +50,17 @@ */ public class AuthenticationPrincipalArgumentResolverTests { + private BeanResolver beanResolver; + private Object expectedPrincipal; private AuthenticationPrincipalArgumentResolver resolver; @Before public void setup() { + this.beanResolver = mock(BeanResolver.class); this.resolver = new AuthenticationPrincipalArgumentResolver(); + this.resolver.setBeanResolver(this.beanResolver); } @After @@ -127,6 +137,17 @@ public void resolveArgumentSpel() throws Exception { assertThat(this.resolver.resolveArgument(showUserSpel(), null, null, null)).isEqualTo(this.expectedPrincipal); } + @Test + public void resolveArgumentSpelBean() throws Exception { + CustomUserPrincipal principal = new CustomUserPrincipal(); + setAuthenticationPrincipal(principal); + given(this.beanResolver.resolve(any(), eq("test"))).willReturn(principal.property); + this.expectedPrincipal = principal.property; + assertThat(this.resolver.resolveArgument(showUserSpelBean(), null, null, null)) + .isEqualTo(this.expectedPrincipal); + verify(this.beanResolver).resolve(any(), eq("test")); + } + @Test public void resolveArgumentSpelCopy() throws Exception { CopyUserPrincipal principal = new CopyUserPrincipal("property"); @@ -195,6 +216,10 @@ private MethodParameter showUserSpel() { return getMethodParameter("showUserSpel", String.class); } + private MethodParameter showUserSpelBean() { + return getMethodParameter("showUserSpelBean", String.class); + } + private MethodParameter showUserSpelCopy() { return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class); } @@ -258,6 +283,9 @@ public void showUserAnnotation(@AuthenticationPrincipal Object user) { public void showUserSpel(@AuthenticationPrincipal(expression = "property") String user) { } + public void showUserSpelBean(@AuthenticationPrincipal(expression = "@test") String user) { + } + public void showUserSpelCopy(@AuthenticationPrincipal( expression = "new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) { } diff --git a/web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java b/web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java index 71d6687f9d1..2a2a17178af 100644 --- a/web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java +++ b/web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.expression.BeanResolver; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; @@ -39,6 +40,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; +import static org.mockito.BDDMockito.verify; /** * @author Dan Zheng @@ -47,11 +53,15 @@ */ public class CurrentSecurityContextArgumentResolverTests { + private BeanResolver beanResolver; + private CurrentSecurityContextArgumentResolver resolver; @Before public void setup() { + this.beanResolver = mock(BeanResolver.class); this.resolver = new CurrentSecurityContextArgumentResolver(); + this.resolver.setBeanResolver(this.beanResolver); } @After @@ -106,6 +116,15 @@ public void resolveArgumentWithAuthentication() { assertThat(auth1.getPrincipal()).isEqualTo(principal); } + @Test + public void resolveArgumentWithAuthenticationWithBean() throws Exception { + String principal = "john"; + given(this.beanResolver.resolve(any(), eq("test"))).willReturn(principal); + assertThat(this.resolver.resolveArgument(showSecurityContextAuthenticationWithBean(), null, null, null)) + .isEqualTo(principal); + verify(this.beanResolver).resolve(any(), eq("test")); + } + @Test public void resolveArgumentWithNullAuthentication() { SecurityContext context = SecurityContextHolder.getContext(); @@ -213,6 +232,10 @@ private MethodParameter showSecurityContextAuthenticationAnnotation() { return getMethodParameter("showSecurityContextAuthenticationAnnotation", Authentication.class); } + public MethodParameter showSecurityContextAuthenticationWithBean() { + return getMethodParameter("showSecurityContextAuthenticationWithBean", String.class); + } + private MethodParameter showSecurityContextAuthenticationWithOptionalPrincipal() { return getMethodParameter("showSecurityContextAuthenticationWithOptionalPrincipal", Object.class); } @@ -294,6 +317,10 @@ public void showSecurityContextAuthenticationAnnotation( @CurrentSecurityContext(expression = "authentication") Authentication authentication) { } + public void showSecurityContextAuthenticationWithBean( + @CurrentSecurityContext(expression = "@test") String name) { + } + public void showSecurityContextAuthenticationWithOptionalPrincipal( @CurrentSecurityContext(expression = "authentication?.principal") Object principal) { }