Merge bf5b229c00
into a5a16f27c7
commit
a4d4a3a335
|
@ -0,0 +1,14 @@
|
||||||
|
# How to make changes?
|
||||||
|
##### Install the CircleCI CLI:
|
||||||
|
https://circleci.com/docs/2.0/local-cli/#installation
|
||||||
|
|
||||||
|
##### Making a change
|
||||||
|
Change the areas of the .circleci/config.yml file that need to be edited
|
||||||
|
|
||||||
|
##### To verify your changes
|
||||||
|
Any config can be verified, to ensure your changes are valid against the yaml and orb schemas,
|
||||||
|
from the root of the project, run: `circleci config validate .circleci/config.yml --org-slug gh/gresham-computing --token $CIRCLE_TOKEN`
|
||||||
|
|
||||||
|
##### Possible errors:
|
||||||
|
- Your file must be encoded in UTF-8 (powershell defaulted to UTF-16)
|
||||||
|
- Must use Unix style line endings (LF, not CRLF)
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
REPOSITORY=https://github.com/gresham-computing/openid-connect-server
|
||||||
|
MASTER_BRANCH=1.3.x
|
||||||
|
|
||||||
|
function get_version {
|
||||||
|
local currentVersion=$(mvn -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec -q)
|
||||||
|
IFS='-' read -r -a parts <<< "$currentVersion"
|
||||||
|
|
||||||
|
local NEXT_NUMBER="$((${parts[1]} + 1))"
|
||||||
|
RELEASE_VERSION="${parts[0]}"-"${parts[1]}"
|
||||||
|
NEXT_SNAPSHOT_VERSION="${parts[0]}"-$NEXT_NUMBER-SNAPSHOT
|
||||||
|
}
|
||||||
|
|
||||||
|
function bump_to_release {
|
||||||
|
mvn -s gresham-nexus-settings/ctc.plugins.settings.xml versions:set -DnewVersion=$RELEASE_VERSION
|
||||||
|
git tag v$RELEASE_VERSION
|
||||||
|
echo -e "\nopenid-connect-server release: $RELEASE_VERSION\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function bump_to_next_snapshot {
|
||||||
|
mvn -s gresham-nexus-settings/ctc.plugins.settings.xml versions:set -DnewVersion=$NEXT_SNAPSHOT_VERSION
|
||||||
|
echo -e "\nopenid-connect-server snapshot: $NEXT_SNAPSHOT_VERSION\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function commit_changes {
|
||||||
|
git commit -a -m "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function push_changes {
|
||||||
|
git push $REPOSITORY $MASTER_BRANCH --tags
|
||||||
|
}
|
||||||
|
|
||||||
|
get_version
|
||||||
|
bump_to_release
|
||||||
|
commit_changes "New openid-connect-server release: ${RELEASE_VERSION}"
|
||||||
|
push_changes
|
||||||
|
bump_to_next_snapshot
|
||||||
|
commit_changes "Next openid-connect-server snapshot: $NEXT_SNAPSHOT_VERSION"
|
||||||
|
push_changes
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/bash
|
||||||
|
HOME=~/project
|
||||||
|
DOWNLOAD_PAGE=$HOME/download.html
|
||||||
|
LOG=$HOME/mavenOutput.log
|
||||||
|
SEARCH_TERMS=(openid-connect uma)
|
||||||
|
|
||||||
|
function generate_artifact_links {
|
||||||
|
EXTENSION=$1
|
||||||
|
echo "<!DOCTYPE html><html><body><h2>Last Deployed Artifacts</h2>" >> $DOWNLOAD_PAGE
|
||||||
|
|
||||||
|
for searchTerm in ${SEARCH_TERMS[@]}; do
|
||||||
|
jarUrls+=($(grep -Eo '(http|https).*'${searchTerm}'.*[^-sources].'${EXTENSION}' | sort --unique' $LOG))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -z $jarUrls ]]; then
|
||||||
|
echo "<ul>" >> $DOWNLOAD_PAGE
|
||||||
|
|
||||||
|
for jarUrl in "${jarUrls[@]}"; do
|
||||||
|
jarName=$(basename $jarUrl)
|
||||||
|
echo "<li><a href="$jarUrl">$jarName</a></li>" >> $DOWNLOAD_PAGE
|
||||||
|
done
|
||||||
|
echo "</ul>" >> $DOWNLOAD_PAGE
|
||||||
|
else
|
||||||
|
echo "No uploaded artifacts found." >> $DOWNLOAD_PAGE
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "<h2>Last Deployed Sources</h2>" >> $DOWNLOAD_PAGE
|
||||||
|
|
||||||
|
# get all sources upload URLs into an array.
|
||||||
|
for searchTerm in ${SEARCH_TERMS[@]}; do
|
||||||
|
sourceUrls+=($(grep -Eo '(http|https).*'${searchTerm}'.*[-sources].'${EXTENSION}' | sort --unique' $LOG))
|
||||||
|
done
|
||||||
|
|
||||||
|
#if download links are found
|
||||||
|
if [[ ! -z $sourceUrls ]]; then
|
||||||
|
echo "<ul>" >> $DOWNLOAD_PAGE
|
||||||
|
|
||||||
|
# write each array entry as a list item URL
|
||||||
|
for sourceUrl in "${sourceUrls[@]}"
|
||||||
|
do
|
||||||
|
sourceName=$(basename $sourceUrl)
|
||||||
|
echo "<li><a href="$sourceUrl">$sourceName</a></li>" >> $DOWNLOAD_PAGE
|
||||||
|
done
|
||||||
|
echo "</ul>" >> $DOWNLOAD_PAGE
|
||||||
|
else
|
||||||
|
echo "No uploaded artifacts found." >> $DOWNLOAD_PAGE
|
||||||
|
fi
|
||||||
|
echo "</body></html>" >> $DOWNLOAD_PAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_artifact_links $@
|
|
@ -0,0 +1,223 @@
|
||||||
|
version: 2.1
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
release:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
semgrep_scan:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
orbs:
|
||||||
|
gresham: gresham-computing/gresham-orb@5.8.0
|
||||||
|
|
||||||
|
executors:
|
||||||
|
docker-executor:
|
||||||
|
docker:
|
||||||
|
- image: 399104266609.dkr.ecr.eu-west-1.amazonaws.com/circleci-build-images:corretto-8u382
|
||||||
|
aws_auth:
|
||||||
|
aws_access_key_id: $GIS_PRD_ECR_INT_BUILD_ACCESS_KEY
|
||||||
|
aws_secret_access_key: $GIS_PRD_ECR_INT_BUILD_SECRET_ACCESS_KEY
|
||||||
|
|
||||||
|
linux-machine:
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2204:2023.10.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
executor: docker-executor
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- get-maven-settings-file
|
||||||
|
- restore-cache
|
||||||
|
- gresham/get-whitelister
|
||||||
|
- gresham/whitelist-add:
|
||||||
|
pattern: OpenId
|
||||||
|
- run:
|
||||||
|
name: "Setting Maven version"
|
||||||
|
command: |
|
||||||
|
MASTER_BRANCH=1.3.x
|
||||||
|
VERSION=$(mvn -s gresham-nexus-settings/ctc.plugins.settings.xml -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec -q)
|
||||||
|
if [[ "${CIRCLE_BRANCH}" != "${MASTER_BRANCH}" && "${VERSION}" == *-SNAPSHOT ]]; then
|
||||||
|
mvn -s gresham-nexus-settings/ctc.plugins.settings.xml versions:set -DnewVersion=${CIRCLE_BRANCH}.GRESHAM-SNAPSHOT -B
|
||||||
|
fi
|
||||||
|
- run:
|
||||||
|
name: "Running Maven build and deploy"
|
||||||
|
command: |
|
||||||
|
mvn -s gresham-nexus-settings/ctc.plugins.settings.xml clean deploy \
|
||||||
|
-B -V -U -DskipTests -DskipITs \
|
||||||
|
-DaltSnapshotDeploymentRepository=snapshots::default::https://nexus.greshamtech.com/repository/thirdparty-maven-snapshots/ \
|
||||||
|
-DaltReleaseDeploymentRepository=releases::default::https://nexus.greshamtech.com/repository/thirdparty-maven-releases/ \
|
||||||
|
|& tee -a /home/circleci/project/mavenOutput.log
|
||||||
|
- generate-download-urls:
|
||||||
|
extension: jar
|
||||||
|
- save-cache
|
||||||
|
- gresham/whitelist-remove:
|
||||||
|
pattern: OpenId
|
||||||
|
- persist-workspace
|
||||||
|
|
||||||
|
test:
|
||||||
|
executor: docker-executor
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- restore-cache
|
||||||
|
- gresham/get-whitelister
|
||||||
|
- gresham/whitelist-add:
|
||||||
|
pattern: OpenId
|
||||||
|
- run:
|
||||||
|
name: "Running tests"
|
||||||
|
command: mvn -fae -s gresham-nexus-settings/ctc.plugins.settings.xml test -B -V -U
|
||||||
|
- save-test-results
|
||||||
|
- save-cache
|
||||||
|
- persist-workspace
|
||||||
|
- gresham/whitelist-remove:
|
||||||
|
pattern: OpenId
|
||||||
|
|
||||||
|
release:
|
||||||
|
executor: docker-executor
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- get-maven-settings-file
|
||||||
|
- gresham/get-whitelister
|
||||||
|
- gresham/whitelist-add:
|
||||||
|
pattern: OpenId
|
||||||
|
- restore-cache
|
||||||
|
- run:
|
||||||
|
name: Creating openid-connect-server release and next snapshot
|
||||||
|
command: chmod +x .circleci/cci_create_release_and_snapshot.sh && .circleci/cci_create_release_and_snapshot.sh
|
||||||
|
- save-cache
|
||||||
|
- gresham/whitelist-remove:
|
||||||
|
pattern: OpenId
|
||||||
|
|
||||||
|
semgrep-scan:
|
||||||
|
executor: linux-machine
|
||||||
|
resource_class: medium
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- gresham/get-whitelister
|
||||||
|
- gresham/whitelist-add:
|
||||||
|
pattern: OpenId
|
||||||
|
kondukto: true
|
||||||
|
- gresham/semgrep-scan:
|
||||||
|
kondukto: true
|
||||||
|
konduktoProject: "openid-connect-server"
|
||||||
|
konduktoBranch: "${CIRCLE_BRANCH}"
|
||||||
|
- gresham/whitelist-remove:
|
||||||
|
pattern: OpenId
|
||||||
|
kondukto: true
|
||||||
|
workflows:
|
||||||
|
build-and-test:
|
||||||
|
unless:
|
||||||
|
or:
|
||||||
|
- << pipeline.parameters.release >>
|
||||||
|
- << pipeline.parameters.semgrep_scan >>
|
||||||
|
jobs:
|
||||||
|
- build-and-deploy:
|
||||||
|
context:
|
||||||
|
- gresham-aws
|
||||||
|
- CTC
|
||||||
|
- CircleCi-Gresham-Credentials
|
||||||
|
- test:
|
||||||
|
requires:
|
||||||
|
- build-and-deploy
|
||||||
|
context:
|
||||||
|
- gresham-aws
|
||||||
|
- CTC
|
||||||
|
- CircleCi-Gresham-Credentials
|
||||||
|
|
||||||
|
build-release:
|
||||||
|
when: << pipeline.parameters.release >>
|
||||||
|
jobs:
|
||||||
|
- release:
|
||||||
|
context:
|
||||||
|
- gresham-aws
|
||||||
|
- CTC
|
||||||
|
- CircleCi-Gresham-Credentials
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: 1.3.x
|
||||||
|
|
||||||
|
semgrep-scan:
|
||||||
|
when: << pipeline.parameters.semgrep_scan >>
|
||||||
|
jobs:
|
||||||
|
- semgrep-scan:
|
||||||
|
context:
|
||||||
|
- gresham-aws
|
||||||
|
- CircleCi-Gresham-Credentials
|
||||||
|
|
||||||
|
scheduled-security-scan:
|
||||||
|
triggers:
|
||||||
|
- schedule:
|
||||||
|
cron: 0 4 * * 1
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: 1.3.x
|
||||||
|
jobs:
|
||||||
|
- semgrep-scan:
|
||||||
|
name: Semgrep Scan
|
||||||
|
context:
|
||||||
|
- gresham-aws
|
||||||
|
- CircleCi-Gresham-Credentials
|
||||||
|
|
||||||
|
commands:
|
||||||
|
setup-git-credentials:
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Setting up Git credentials
|
||||||
|
command: |
|
||||||
|
git config --global user.name "CircleCI"
|
||||||
|
git config --global user.email "$GITHUB_GRESHAM_USER"
|
||||||
|
|
||||||
|
get-maven-settings-file:
|
||||||
|
steps:
|
||||||
|
- setup-git-credentials
|
||||||
|
- run:
|
||||||
|
name: Getting Maven settings file
|
||||||
|
command: |
|
||||||
|
git config --global url."https://api:${GITHUB_GRESHAM_PW}@github.com/".insteadOf "https://github.com/"
|
||||||
|
git clone https://github.com/gresham-computing/gresham-nexus-settings
|
||||||
|
|
||||||
|
save-cache:
|
||||||
|
steps:
|
||||||
|
- save_cache:
|
||||||
|
paths:
|
||||||
|
- ~/.m2
|
||||||
|
key: v1-m2-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "pom.xml" }}
|
||||||
|
|
||||||
|
restore-cache:
|
||||||
|
steps:
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- v1-m2-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "pom.xml" }}
|
||||||
|
- v1-m2-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}
|
||||||
|
- v1-m2-
|
||||||
|
|
||||||
|
persist-workspace:
|
||||||
|
steps:
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: .
|
||||||
|
paths:
|
||||||
|
- .
|
||||||
|
|
||||||
|
generate-download-urls:
|
||||||
|
parameters:
|
||||||
|
extension:
|
||||||
|
type: string
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: "Generating artifact download URLs"
|
||||||
|
command: chmod +x .circleci/cci_generate_artifact_links.sh && .circleci/cci_generate_artifact_links.sh << parameters.extension >>
|
||||||
|
- store_artifacts:
|
||||||
|
path: download.html
|
||||||
|
|
||||||
|
save-test-results:
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Save test results
|
||||||
|
command: |
|
||||||
|
mkdir -p ~/test-results/junit/
|
||||||
|
find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/test-results/junit/ \;
|
||||||
|
when: always
|
||||||
|
- store_test_results:
|
||||||
|
path: ~/test-results
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [[ -z "${CIRCLE_TOKEN}" ]]; then
|
||||||
|
echo Cannot trigger release workflow. CircleCI user token not found.
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRANCH=1.3.x
|
||||||
|
|
||||||
|
echo -e "\nTriggering release workflow on branch: ${BRANCH}.\n"
|
||||||
|
|
||||||
|
status_code=$(curl --request POST \
|
||||||
|
--url https://circleci.com/api/v2/project/github/gresham-computing/openid-connect-server/pipeline \
|
||||||
|
--header 'Circle-Token: '${CIRCLE_TOKEN}'' \
|
||||||
|
--header 'content-type: application/json' \
|
||||||
|
--data '{"branch":"'${BRANCH}'","parameters":{"release": true}}' \
|
||||||
|
-o response.json \
|
||||||
|
-w "%{http_code}")
|
||||||
|
|
||||||
|
if [ "${status_code}" -ge "200" ] && [ "${status_code}" -lt "300" ]; then
|
||||||
|
echo -e "\nAPI call succeeded [${status_code}]. Response:\n"
|
||||||
|
cat response.json
|
||||||
|
rm response.json
|
||||||
|
else
|
||||||
|
echo -e "\nAPI call failed [${status_code}]. Response:\n"
|
||||||
|
cat response.json
|
||||||
|
rm response.json
|
||||||
|
exit 1
|
||||||
|
fi
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -1,5 +1,21 @@
|
||||||
Unreleased:
|
Unreleased:
|
||||||
|
|
||||||
|
- Updated JDK to Corretto 1.8.342
|
||||||
|
- Upgraded Jackson Components to 2.15.2
|
||||||
|
|
||||||
|
|
||||||
|
*1.3.3-GRESHAM-28:
|
||||||
|
- Updated JDK to Corretto 1.8.332
|
||||||
|
- Upgraded Jackson Components to 2.13.3
|
||||||
|
|
||||||
|
*1.3.3-GRESHAM:
|
||||||
|
- Upgraded libraries with known vulnerabilities
|
||||||
|
- Added a Gresham specific Jenkinsfile
|
||||||
|
- Added a password encoder to the client entity service
|
||||||
|
- Fixes a bug by specifying the name of the scope columnn
|
||||||
|
- Removed functionality that passed the client secret down to the UI
|
||||||
|
- Updated JDK to Corretto 1.8.252
|
||||||
|
|
||||||
*1.3.2:
|
*1.3.2:
|
||||||
- Added changelog
|
- Added changelog
|
||||||
- Set default redirect URI resolver strict matching to true
|
- Set default redirect URI resolver strict matching to true
|
||||||
|
|
|
@ -29,3 +29,9 @@ The authors and key contributors of the project include:
|
||||||
|
|
||||||
|
|
||||||
Copyright ©2017, [MIT Internet Trust Consortium](http://www.trust.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`.
|
Copyright ©2017, [MIT Internet Trust Consortium](http://www.trust.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`.
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
Here at Gresham, we use this component for a base for the auth server, our developing branch is 1.3.x and any feature branches should be made off of that branch.
|
||||||
|
|
||||||
|
A release build can be invoked by running .circleci/run_release_workflow.sh shell script. It uses CircleCI API to trigger the release workflow and it requires a CIRCLE_TOKEN environment variable with a personal CircleCI API token to be set. Once triggered, the build will bump appropriate versions to release and then proceed to bump them to next snapshot.
|
|
@ -22,7 +22,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>openid-connect-client</artifactId>
|
<artifactId>openid-connect-client</artifactId>
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
<target>${java-version}</target>
|
<target>${java-version}</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- BUILD SOURCE FILES -->
|
<!--<!– BUILD SOURCE FILES –>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- BUILD JavaDoc FILES -->
|
<!– BUILD JavaDoc FILES –>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>-->
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -21,12 +21,7 @@ import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Calendar;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
@ -41,6 +36,7 @@ import org.springframework.http.client.ClientHttpRequest;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||||
|
@ -237,10 +233,15 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
|
||||||
Map<String, String> parameters = new HashMap<>();
|
Map<String, String> parameters = new HashMap<>();
|
||||||
parameters.put("client_id", clientId);
|
parameters.put("client_id", clientId);
|
||||||
parameters.put("scope", OAuth2Utils.formatParameterList(scopes));
|
parameters.put("scope", OAuth2Utils.formatParameterList(scopes));
|
||||||
OAuth2Request storedRequest = new OAuth2Request(parameters, clientId, null, true, scopes, null, null, null, null);
|
OAuth2Request storedRequest = new OAuth2Request(parameters, clientId, parseClientAuthorities(token), true, scopes, null, null, null, null);
|
||||||
return storedRequest;
|
return storedRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Added the protected method to allow custom behaviour
|
||||||
|
protected Collection<? extends GrantedAuthority> parseClientAuthorities(JsonObject token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Authentication createUserAuthentication(JsonObject token) {
|
private Authentication createUserAuthentication(JsonObject token) {
|
||||||
JsonElement userId = token.get("user_id");
|
JsonElement userId = token.get("user_id");
|
||||||
if(userId == null) {
|
if(userId == null) {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>openid-connect-common</artifactId>
|
<artifactId>openid-connect-common</artifactId>
|
||||||
|
@ -59,6 +59,10 @@
|
||||||
<groupId>org.springframework.security.oauth</groupId>
|
<groupId>org.springframework.security.oauth</groupId>
|
||||||
<artifactId>spring-security-oauth2</artifactId>
|
<artifactId>spring-security-oauth2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.minidev</groupId>
|
||||||
|
<artifactId>json-smart</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nimbusds</groupId>
|
<groupId>com.nimbusds</groupId>
|
||||||
<artifactId>nimbus-jose-jwt</artifactId>
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
|
@ -85,7 +89,20 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>groovy</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spockframework</groupId>
|
||||||
|
<artifactId>spock-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -101,7 +118,38 @@
|
||||||
<target>${java-version}</target>
|
<target>${java-version}</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- BUILD SOURCE FILES -->
|
<plugin>
|
||||||
|
<groupId>org.codehaus.gmavenplus</groupId>
|
||||||
|
<artifactId>gmavenplus-plugin</artifactId>
|
||||||
|
<version>1.8.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>addTestStubSources</goal>
|
||||||
|
<goal>compileTests</goal>
|
||||||
|
<goal>removeTestStubs</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<stubsOutputDirectory>${project.build.directory}/generated-groovy-stubs</stubsOutputDirectory>
|
||||||
|
<testStubsOutputDirectory>${project.build.directory}/generated-groovy-test-stubs</testStubsOutputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.2</version>
|
||||||
|
<configuration>
|
||||||
|
<excludedGroups combine.self="override"/>
|
||||||
|
<testClassesDirectory>${project.build.testOutputDirectory}</testClassesDirectory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Test.java</include>
|
||||||
|
<include>**/*Spec.java</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!--<!– BUILD SOURCE FILES –>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
@ -114,19 +162,19 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- BUILD JavaDoc FILES -->
|
<!– BUILD JavaDoc FILES –>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>attach-sources</id>
|
<id>attach-javadocs</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>jar</goal>
|
<goal>jar</goal>
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>-->
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -242,6 +242,7 @@ public class OAuth2AccessTokenEntity implements OAuth2AccessToken {
|
||||||
joinColumns=@JoinColumn(name="owner_id"),
|
joinColumns=@JoinColumn(name="owner_id"),
|
||||||
name="token_scope"
|
name="token_scope"
|
||||||
)
|
)
|
||||||
|
@Column(name="scope")
|
||||||
public Set<String> getScope() {
|
public Set<String> getScope() {
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ import java.lang.reflect.Type;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.safety.Safelist;
|
||||||
|
import org.mitre.openid.connect.model.Address;
|
||||||
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
|
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
|
||||||
import org.mitre.openid.connect.model.UserInfo;
|
import org.mitre.openid.connect.model.UserInfo;
|
||||||
import org.mitre.openid.connect.service.UserInfoService;
|
import org.mitre.openid.connect.service.UserInfoService;
|
||||||
|
@ -51,6 +54,8 @@ import com.google.gson.JsonSerializer;
|
||||||
*/
|
*/
|
||||||
public class UserInfoInterceptor extends HandlerInterceptorAdapter {
|
public class UserInfoInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
private final Safelist safelist = Safelist.none();
|
||||||
|
|
||||||
private Gson gson = new GsonBuilder()
|
private Gson gson = new GsonBuilder()
|
||||||
.registerTypeHierarchyAdapter(GrantedAuthority.class, new JsonSerializer<GrantedAuthority>() {
|
.registerTypeHierarchyAdapter(GrantedAuthority.class, new JsonSerializer<GrantedAuthority>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,9 +83,11 @@ public class UserInfoInterceptor extends HandlerInterceptorAdapter {
|
||||||
if (auth instanceof OIDCAuthenticationToken) {
|
if (auth instanceof OIDCAuthenticationToken) {
|
||||||
// if they're logging into this server from a remote OIDC server, pass through their user info
|
// if they're logging into this server from a remote OIDC server, pass through their user info
|
||||||
OIDCAuthenticationToken oidc = (OIDCAuthenticationToken) auth;
|
OIDCAuthenticationToken oidc = (OIDCAuthenticationToken) auth;
|
||||||
if (oidc.getUserInfo() != null) {
|
UserInfo userInfo = oidc.getUserInfo();
|
||||||
request.setAttribute("userInfo", oidc.getUserInfo());
|
if (userInfo != null) {
|
||||||
request.setAttribute("userInfoJson", oidc.getUserInfo().toJson());
|
sanitiseUserInfo(userInfo);
|
||||||
|
request.setAttribute("userInfo", userInfo);
|
||||||
|
request.setAttribute("userInfoJson", userInfo.toJson());
|
||||||
} else {
|
} else {
|
||||||
request.setAttribute("userInfo", null);
|
request.setAttribute("userInfo", null);
|
||||||
request.setAttribute("userInfoJson", "null");
|
request.setAttribute("userInfoJson", "null");
|
||||||
|
@ -94,6 +101,7 @@ public class UserInfoInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
// if we have one, inject it so views can use it
|
// if we have one, inject it so views can use it
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
|
sanitiseUserInfo(user);
|
||||||
request.setAttribute("userInfo", user);
|
request.setAttribute("userInfo", user);
|
||||||
request.setAttribute("userInfoJson", user.toJson());
|
request.setAttribute("userInfoJson", user.toJson());
|
||||||
}
|
}
|
||||||
|
@ -104,4 +112,42 @@ public class UserInfoInterceptor extends HandlerInterceptorAdapter {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sanitiseUserInfo(final UserInfo userInfo) {
|
||||||
|
userInfo.setSub(sanitise(userInfo.getSub()));
|
||||||
|
userInfo.setPreferredUsername(sanitise(userInfo.getPreferredUsername()));
|
||||||
|
userInfo.setName(sanitise(userInfo.getName()));
|
||||||
|
userInfo.setGivenName(sanitise(userInfo.getGivenName()));
|
||||||
|
userInfo.setFamilyName(sanitise(userInfo.getFamilyName()));
|
||||||
|
userInfo.setMiddleName(sanitise(userInfo.getMiddleName()));
|
||||||
|
userInfo.setNickname(sanitise(userInfo.getNickname()));
|
||||||
|
userInfo.setProfile(sanitise(userInfo.getProfile()));
|
||||||
|
userInfo.setPicture(sanitise(userInfo.getPicture()));
|
||||||
|
userInfo.setWebsite(sanitise(userInfo.getWebsite()));
|
||||||
|
userInfo.setEmail(sanitise(userInfo.getEmail()));
|
||||||
|
userInfo.setGender(sanitise(userInfo.getGender()));
|
||||||
|
userInfo.setLocale(sanitise(userInfo.getLocale()));
|
||||||
|
userInfo.setPhoneNumber(sanitise(userInfo.getPhoneNumber()));
|
||||||
|
userInfo.setUpdatedTime(sanitise(userInfo.getUpdatedTime()));
|
||||||
|
userInfo.setBirthdate(sanitise(userInfo.getBirthdate()));
|
||||||
|
|
||||||
|
Address userInfoAddress = userInfo.getAddress();
|
||||||
|
if (userInfoAddress != null) {
|
||||||
|
userInfoAddress.setFormatted(sanitise(userInfoAddress.getFormatted()));
|
||||||
|
userInfoAddress.setStreetAddress(sanitise(userInfoAddress.getStreetAddress()));
|
||||||
|
userInfoAddress.setLocality(sanitise(userInfoAddress.getLocality()));
|
||||||
|
userInfoAddress.setRegion(sanitise(userInfoAddress.getRegion()));
|
||||||
|
userInfoAddress.setPostalCode(sanitise(userInfoAddress.getPostalCode()));
|
||||||
|
userInfoAddress.setCountry(sanitise(userInfoAddress.getCountry()));
|
||||||
|
userInfo.setAddress(userInfoAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sanitise(String elementToClean) {
|
||||||
|
if (elementToClean != null) {
|
||||||
|
return Jsoup.clean(elementToClean, safelist);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.mitre.openid.connect.web
|
||||||
|
|
||||||
|
import org.mitre.openid.connect.model.DefaultUserInfo
|
||||||
|
import org.mitre.openid.connect.model.UserInfo
|
||||||
|
import spock.lang.Specification
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class UserInfoInterceptorSpec extends Specification {
|
||||||
|
|
||||||
|
private def userInfoInterceptor = new UserInfoInterceptor()
|
||||||
|
|
||||||
|
// CVE-2020-5497 -> https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/issues/1521
|
||||||
|
@Unroll
|
||||||
|
def 'User Info is sanitised before making it back to the webpage with payload #payload'() {
|
||||||
|
given: 'A user name with a malicious payload'
|
||||||
|
|
||||||
|
UserInfo userInfo = new DefaultUserInfo()
|
||||||
|
userInfo.setSub('12318767')
|
||||||
|
userInfo.setName("Test" + payload + " Test")
|
||||||
|
userInfo.setPreferredUsername('Test')
|
||||||
|
userInfo.setGivenName("Test" + payload)
|
||||||
|
userInfo.setFamilyName('Test')
|
||||||
|
userInfo.setEmail('test@test.com')
|
||||||
|
userInfo.setEmailVerified(true)
|
||||||
|
|
||||||
|
when: 'The user info object is passed through the sanitise method'
|
||||||
|
|
||||||
|
userInfoInterceptor.sanitiseUserInfo(userInfo)
|
||||||
|
|
||||||
|
then: 'The malicious names have been sanitised'
|
||||||
|
|
||||||
|
userInfo.getName() == 'Test Test'
|
||||||
|
userInfo.getGivenName() == 'Test'
|
||||||
|
|
||||||
|
and: 'The non malicious elements have been unaffected'
|
||||||
|
|
||||||
|
userInfo.getSub() == '12318767'
|
||||||
|
userInfo.getPreferredUsername() == 'Test'
|
||||||
|
userInfo.getFamilyName() == 'Test'
|
||||||
|
userInfo.getEmail() == 'test@test.com'
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
payload | _
|
||||||
|
"</script><script>alert(1)</script>" | _
|
||||||
|
"<body src=1 href=1 onerror=\"javascript:alert(1)\"></body>" | _
|
||||||
|
"<html onMouseWheel html onMouseWheel=\"javascript:javascript:alert(1)\"></html onMouseWheel>" | _
|
||||||
|
"<IMG SRC=`javascript:javascript:alert(1)`>" | _
|
||||||
|
"<script ~~~>alert(0%0)</script ~~~>" | _
|
||||||
|
"<IMG SRC=x onload=\"alert(String.fromCharCode(88,83,83))\">" | _
|
||||||
|
"<div STYLE=\"background-image: url(javascript:document.vulnerable=true;)\">" | _
|
||||||
|
"<BODY ONLOAD=javascript:alert(1)>" | _
|
||||||
|
"<iframe src=\"vbscript:document.vulnerable=true;\">" | _
|
||||||
|
"<br SIZE=\"&{document.vulnerable=true}\">" | _
|
||||||
|
"<img src=\"Mario Heiderich says that svg SHOULD not be executed trough image tags\" onerror=\"javascript:document.write('\\u003c\\u0069\\u0066\\u0072\\u0061\\u006d\\u0065\\u0020\\u0073\\u0072\\u0063\\u003d\\u0022\\u0064\\u0061\\u0074\\u0061\\u003a\\u0069\\u006d\\u0061\\u0067\\u0065\\u002f\\u0073\\u0076\\u0067\\u002b\\u0078\\u006d\\u006c\\u003b\\u0062\\u0061\\u0073\\u0065\\u0036\\u0034\\u002c\\u0050\\u0048\\u004e\\u0032\\u005a\\u0079\\u0042\\u0034\\u0062\\u0057\\u0078\\u0075\\u0063\\u007a\\u0030\\u0069\\u0061\\u0048\\u0052\\u0030\\u0063\\u0044\\u006f\\u0076\\u004c\\u0033\\u0064\\u0033\\u0064\\u0079\\u0035\\u0033\\u004d\\u0079\\u0035\\u0076\\u0063\\u006d\\u0063\\u0076\\u004d\\u006a\\u0041\\u0077\\u004d\\u0043\\u0039\\u007a\\u0064\\u006d\\u0063\\u0069\\u0050\\u0069\\u0041\\u0067\\u0043\\u0069\\u0041\\u0067\\u0049\\u0044\\u0078\\u0070\\u0062\\u0057\\u0046\\u006e\\u005a\\u0053\\u0042\\u0076\\u0062\\u006d\\u0078\\u0076\\u0059\\u0057\\u0051\\u0039\\u0049\\u006d\\u0046\\u0073\\u005a\\u0058\\u004a\\u0030\\u004b\\u0044\\u0045\\u0070\\u0049\\u006a\\u0034\\u0038\\u004c\\u0032\\u006c\\u0074\\u0059\\u0057\\u0064\\u006c\\u0050\\u0069\\u0041\\u0067\\u0043\\u0069\\u0041\\u0067\\u0049\\u0044\\u0078\\u007a\\u0064\\u006d\\u0063\\u0067\\u0062\\u0032\\u0035\\u0073\\u0062\\u0032\\u0046\\u006b\\u0050\\u0053\\u004a\\u0068\\u0062\\u0047\\u0056\\u0079\\u0064\\u0043\\u0067\\u0079\\u004b\\u0053\\u0049\\u002b\\u0050\\u0043\\u0039\\u007a\\u0064\\u006d\\u0063\\u002b\\u0049\\u0043\\u0041\\u004b\\u0049\\u0043\\u0041\\u0067\\u0050\\u0048\\u004e\\u006a\\u0063\\u006d\\u006c\\u0077\\u0064\\u0044\\u0035\\u0068\\u0062\\u0047\\u0056\\u0079\\u0064\\u0043\\u0067\\u007a\\u004b\\u0054\\u0077\\u0076\\u0063\\u0032\\u004e\\u0079\\u0061\\u0058\\u0042\\u0030\\u0050\\u0069\\u0041\\u0067\\u0043\\u0069\\u0041\\u0067\\u0049\\u0044\\u0078\\u006b\\u005a\\u0057\\u005a\\u007a\\u0049\\u0047\\u0039\\u0075\\u0062\\u0047\\u0039\\u0068\\u005a\\u0044\\u0030\\u0069\\u0059\\u0057\\u0078\\u006c\\u0063\\u006e\\u0051\\u006f\\u004e\\u0043\\u006b\\u0069\\u0050\\u006a\\u0077\\u0076\\u005a\\u0047\\u0056\\u006d\\u0063\\u007a\\u0034\\u0067\\u0049\\u0041\\u006f\\u0067\\u0049\\u0043\\u0041\\u0038\\u005a\\u0079\\u0042\\u0076\\u0062\\u006d\\u0078\\u0076\\u0059\\u0057\\u0051\\u0039\\u0049\\u006d\\u0046\\u0073\\u005a\\u0058\\u004a\\u0030\\u004b\\u0044\\u0055\\u0070\\u0049\\u006a\\u0034\\u0067\\u0049\\u0041\\u006f\\u0067\\u0049\\u0043\\u0041\\u0067\\u0049\\u0043\\u0041\\u0067\\u0050\\u0047\\u004e\\u0070\\u0063\\u006d\\u004e\\u0073\\u005a\\u0053\\u0042\\u0076\\u0062\\u006d\\u0078\\u0076\\u0059\\u0057\\u0051\\u0039\\u0049\\u006d\\u0046\\u0073\\u005a\\u0058\\u004a\\u0030\\u004b\\u0044\\u0059\\u0070\\u0049\\u0069\\u0041\\u0076\\u0050\\u0069\\u0041\\u0067\\u0043\\u0069\\u0041\\u0067\\u0049\\u0043\\u0041\\u0067\\u0049\\u0043\\u0041\\u0038\\u0064\\u0047\\u0056\\u0034\\u0064\\u0043\\u0042\\u0076\\u0062\\u006d\\u0078\\u0076\\u0059\\u0057\\u0051\\u0039\\u0049\\u006d\\u0046\\u0073\\u005a\\u0058\\u004a\\u0030\\u004b\\u0044\\u0063\\u0070\\u0049\\u006a\\u0034\\u0038\\u004c\\u0033\\u0052\\u006c\\u0065\\u0048\\u0051\\u002b\\u0049\\u0043\\u0041\\u004b\\u0049\\u0043\\u0041\\u0067\\u0050\\u0043\\u0039\\u006e\\u0050\\u0069\\u0041\\u0067\\u0043\\u006a\\u0077\\u0076\\u0063\\u0033\\u005a\\u006e\\u0050\\u0069\\u0041\\u0067\\u0022\\u003e\\u003c\\u002f\\u0069\\u0066\\u0072\\u0061\\u006d\\u0065\\u003e');\"></img>" | _
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def 'User Info is santised to an extent to not produce an XSS with payload #payload'() {
|
||||||
|
given: 'A user name with a malicious payload'
|
||||||
|
|
||||||
|
UserInfo userInfo = new DefaultUserInfo()
|
||||||
|
userInfo.setSub('12318767')
|
||||||
|
userInfo.setName("Test" + payload + " Test")
|
||||||
|
userInfo.setPreferredUsername('Test')
|
||||||
|
userInfo.setGivenName("Test" + payload)
|
||||||
|
userInfo.setFamilyName('Test')
|
||||||
|
userInfo.setEmail('test@test.com')
|
||||||
|
userInfo.setEmailVerified(true)
|
||||||
|
|
||||||
|
when: 'The user info object is passed through the sanitise method'
|
||||||
|
|
||||||
|
userInfoInterceptor.sanitiseUserInfo(userInfo)
|
||||||
|
|
||||||
|
then: 'The malicious names have been sanitised'
|
||||||
|
|
||||||
|
userInfo.getName() == 'Test' + expectedResponse + ' Test'
|
||||||
|
userInfo.getGivenName() == 'Test' + expectedResponse
|
||||||
|
|
||||||
|
and: 'The non malicious elements have been unaffected'
|
||||||
|
|
||||||
|
userInfo.getSub() == '12318767'
|
||||||
|
userInfo.getPreferredUsername() == 'Test'
|
||||||
|
userInfo.getFamilyName() == 'Test'
|
||||||
|
userInfo.getEmail() == 'test@test.com'
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
payload | expectedResponse
|
||||||
|
"'\"></title><script>alert(1111)</script>" | "'\">"
|
||||||
|
"'>//\\\\,<'>\">\">\"*\"" | "'>//\\\\,<'>\">\">\"*\""
|
||||||
|
"'\"\"><script language=\"JavaScript\"> alert('X \\nS \\nS');</script>" | "'\"\">"
|
||||||
|
"!--\" /><script>alert('xss');</script>" | "!--\" />"
|
||||||
|
"\">/XaDoS/><script>alert(document.cookie)</script><script src=\"http://www.site.com/XSS.js\"></script>" | "\">/XaDoS/>"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>openid-connect-server-webapp</artifactId>
|
<artifactId>openid-connect-server-webapp</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
|
|
|
@ -85,7 +85,9 @@
|
||||||
<c:if test="${ not empty client.logoUri }">
|
<c:if test="${ not empty client.logoUri }">
|
||||||
<ul class="thumbnails">
|
<ul class="thumbnails">
|
||||||
<li class="span5">
|
<li class="span5">
|
||||||
<a class="thumbnail" data-toggle="modal" data-target="#logoModal"><img src="api/clients/${ client.id }/logo" /></a>
|
<a class="thumbnail" data-toggle="modal" data-target="#logoModal">
|
||||||
|
<img src="<c:out value="${ client.logoUri }" />" referrerpolicy="no-referrer" />
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
|
@ -104,7 +106,7 @@
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<img src="api/clients/${ client.id }/logo" />
|
<img src="<c:out value="${ client.logoUri }" />" referrerpolicy="no-referrer" />
|
||||||
<c:if test="${ not empty client.clientUri }">
|
<c:if test="${ not empty client.clientUri }">
|
||||||
<a href="<c:out value="${ client.clientUri }" />"><c:out value="${ client.clientUri }" /></a>
|
<a href="<c:out value="${ client.clientUri }" />"><c:out value="${ client.clientUri }" /></a>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
|
@ -85,7 +85,9 @@
|
||||||
<c:if test="${ not empty client.logoUri }">
|
<c:if test="${ not empty client.logoUri }">
|
||||||
<ul class="thumbnails">
|
<ul class="thumbnails">
|
||||||
<li class="span5">
|
<li class="span5">
|
||||||
<a class="thumbnail" data-toggle="modal" data-target="#logoModal"><img src="api/clients/${ client.id }/logo" /></a>
|
<a class="thumbnail" data-toggle="modal" data-target="#logoModal">
|
||||||
|
<img src="<c:out value="${ client.logoUri }" />" referrerpolicy="no-referrer" />
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
|
@ -104,7 +106,7 @@
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<img src="api/clients/${ client.id }/logo" />
|
<img src="<c:out value="${ client.logoUri }" />" referrerpolicy="no-referrer" />
|
||||||
<c:if test="${ not empty client.clientUri }">
|
<c:if test="${ not empty client.clientUri }">
|
||||||
<a href="<c:out value="${ client.clientUri }" />"><c:out value="${ client.clientUri }" /></a>
|
<a href="<c:out value="${ client.clientUri }" />"><c:out value="${ client.clientUri }" /></a>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
|
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<% if (client.logoUri) { %>
|
<% if (client.logoUri) { %>
|
||||||
<span class="pull-left"><img class="media-object client-logo" src="api/clients/<%- client.id %>/logo"></span>
|
<span class="pull-left">
|
||||||
|
<img class="media-object client-logo" src="<%- client.logoUri %>" referrerpolicy="no-referrer" />
|
||||||
|
</span>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<build>
|
<build>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -32,7 +32,7 @@ import com.google.common.base.Strings;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* A redirect resolver that knows how to check against the blacklisted URIs
|
* A redirect resolver that knows how to check against the blacklisted URIs
|
||||||
* for forbidden values. Can be configured to do strict string matching also.
|
* for forbidden values.
|
||||||
*
|
*
|
||||||
* @author jricher
|
* @author jricher
|
||||||
*
|
*
|
||||||
|
@ -46,8 +46,6 @@ public class BlacklistAwareRedirectResolver extends DefaultRedirectResolver {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConfigurationPropertiesBean config;
|
private ConfigurationPropertiesBean config;
|
||||||
|
|
||||||
private boolean strictMatch = true;
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.security.oauth2.provider.endpoint.RedirectResolver#resolveRedirect(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails)
|
* @see org.springframework.security.oauth2.provider.endpoint.RedirectResolver#resolveRedirect(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails)
|
||||||
*/
|
*/
|
||||||
|
@ -62,44 +60,4 @@ public class BlacklistAwareRedirectResolver extends DefaultRedirectResolver {
|
||||||
return redirect;
|
return redirect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.security.oauth2.provider.endpoint.DefaultRedirectResolver#redirectMatches(java.lang.String, java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected boolean redirectMatches(String requestedRedirect, String redirectUri) {
|
|
||||||
|
|
||||||
if (isStrictMatch()) {
|
|
||||||
// we're doing a strict string match for all clients
|
|
||||||
return Strings.nullToEmpty(requestedRedirect).equals(redirectUri);
|
|
||||||
} else {
|
|
||||||
// otherwise do the prefix-match from the library
|
|
||||||
return super.redirectMatches(requestedRedirect, redirectUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the strictMatch
|
|
||||||
*/
|
|
||||||
public boolean isStrictMatch() {
|
|
||||||
if (config.isHeartMode()) {
|
|
||||||
// HEART mode enforces strict matching
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return strictMatch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this to true to require exact string matches for all redirect URIs. (Default is false)
|
|
||||||
*
|
|
||||||
* @param strictMatch the strictMatch to set
|
|
||||||
*/
|
|
||||||
public void setStrictMatch(boolean strictMatch) {
|
|
||||||
this.strictMatch = strictMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
||||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -100,7 +101,11 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConfigurationPropertiesBean config;
|
private ConfigurationPropertiesBean config;
|
||||||
|
|
||||||
// map of sector URI -> list of redirect URIs
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
|
||||||
|
// map of sector URI -> list of redirect URIs
|
||||||
private LoadingCache<String, List<String>> sectorRedirects = CacheBuilder.newBuilder()
|
private LoadingCache<String, List<String>> sectorRedirects = CacheBuilder.newBuilder()
|
||||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||||
.maximumSize(100)
|
.maximumSize(100)
|
||||||
|
@ -145,8 +150,16 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
|
||||||
|
|
||||||
ensureNoReservedScopes(client);
|
ensureNoReservedScopes(client);
|
||||||
|
|
||||||
|
String plaintextSecret = client.getClientSecret();
|
||||||
|
|
||||||
|
if(!Strings.isNullOrEmpty(client.getClientSecret())) {
|
||||||
|
client.setClientSecret(this.passwordEncoder.encode(client.getClientSecret()));
|
||||||
|
}
|
||||||
|
|
||||||
ClientDetailsEntity c = clientRepository.saveClient(client);
|
ClientDetailsEntity c = clientRepository.saveClient(client);
|
||||||
|
|
||||||
|
c.setClientSecret(plaintextSecret);
|
||||||
|
|
||||||
statsService.resetCache();
|
statsService.resetCache();
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
@ -423,6 +436,12 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
|
||||||
// make sure a client doesn't get any special system scopes
|
// make sure a client doesn't get any special system scopes
|
||||||
ensureNoReservedScopes(newClient);
|
ensureNoReservedScopes(newClient);
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(newClient.getClientSecret())){
|
||||||
|
newClient.setClientSecret(oldClient.getClientSecret());
|
||||||
|
}else{
|
||||||
|
newClient.setClientSecret(this.passwordEncoder.encode(newClient.getClientSecret()));
|
||||||
|
}
|
||||||
|
|
||||||
return clientRepository.updateClient(oldClient.getId(), newClient);
|
return clientRepository.updateClient(oldClient.getId(), newClient);
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Neither old client or new client can be null!");
|
throw new IllegalArgumentException("Neither old client or new client can be null!");
|
||||||
|
|
|
@ -103,9 +103,9 @@ public class OAuthConfirmationController {
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_USER')")
|
@PreAuthorize("hasRole('ROLE_USER')")
|
||||||
@RequestMapping("/oauth/confirm_access")
|
@RequestMapping("/oauth/confirm_access")
|
||||||
public String confimAccess(Map<String, Object> model, @ModelAttribute("authorizationRequest") AuthorizationRequest authRequest,
|
public String confirmAccess(Map<String, Object> model, Principal p) {
|
||||||
Principal p) {
|
|
||||||
|
|
||||||
|
AuthorizationRequest authRequest = (AuthorizationRequest) model.get("authorizationRequest");
|
||||||
// Check the "prompt" parameter to see if we need to do special processing
|
// Check the "prompt" parameter to see if we need to do special processing
|
||||||
|
|
||||||
String prompt = (String)authRequest.getExtensions().get(PROMPT);
|
String prompt = (String)authRequest.getExtensions().get(PROMPT);
|
||||||
|
|
|
@ -21,9 +21,11 @@
|
||||||
package org.mitre.oauth2.web;
|
package org.mitre.oauth2.web;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.mitre.oauth2.model.SystemScope;
|
import org.mitre.oauth2.model.SystemScope;
|
||||||
import org.mitre.oauth2.service.SystemScopeService;
|
import org.mitre.oauth2.service.SystemScopeService;
|
||||||
|
import org.mitre.openid.connect.exception.ScopeException;
|
||||||
import org.mitre.openid.connect.view.HttpCodeView;
|
import org.mitre.openid.connect.view.HttpCodeView;
|
||||||
import org.mitre.openid.connect.view.JsonEntityView;
|
import org.mitre.openid.connect.view.JsonEntityView;
|
||||||
import org.mitre.openid.connect.view.JsonErrorView;
|
import org.mitre.openid.connect.view.JsonErrorView;
|
||||||
|
@ -33,6 +35,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.access.method.P;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
|
@ -54,6 +57,8 @@ public class ScopeAPI {
|
||||||
|
|
||||||
public static final String URL = RootController.API_URL + "/scopes";
|
public static final String URL = RootController.API_URL + "/scopes";
|
||||||
|
|
||||||
|
private static final String characterMatcher = "[a-zA-Z]+";
|
||||||
|
private static final Pattern pattern = Pattern.compile(characterMatcher);
|
||||||
@Autowired
|
@Autowired
|
||||||
private SystemScopeService scopeService;
|
private SystemScopeService scopeService;
|
||||||
|
|
||||||
|
@ -101,7 +106,14 @@ public class ScopeAPI {
|
||||||
SystemScope existing = scopeService.getById(id);
|
SystemScope existing = scopeService.getById(id);
|
||||||
|
|
||||||
SystemScope scope = gson.fromJson(json, SystemScope.class);
|
SystemScope scope = gson.fromJson(json, SystemScope.class);
|
||||||
|
try {
|
||||||
|
validateScope(scope);
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("updateScope failed due to ScopeException", e);
|
||||||
|
m.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.put(JsonErrorView.ERROR_MESSAGE, "Could not update scope. The server encountered a scope exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
|
}
|
||||||
if (existing != null && scope != null) {
|
if (existing != null && scope != null) {
|
||||||
|
|
||||||
if (existing.getId().equals(scope.getId())) {
|
if (existing.getId().equals(scope.getId())) {
|
||||||
|
@ -138,6 +150,14 @@ public class ScopeAPI {
|
||||||
SystemScope scope = gson.fromJson(json, SystemScope.class);
|
SystemScope scope = gson.fromJson(json, SystemScope.class);
|
||||||
|
|
||||||
SystemScope alreadyExists = scopeService.getByValue(scope.getValue());
|
SystemScope alreadyExists = scopeService.getByValue(scope.getValue());
|
||||||
|
try {
|
||||||
|
validateScope(scope);
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("createScope failed due to ScopeException", e);
|
||||||
|
m.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.put(JsonErrorView.ERROR_MESSAGE, "Could not create scope. The server encountered a scope exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
|
}
|
||||||
if (alreadyExists != null) {
|
if (alreadyExists != null) {
|
||||||
//Error, cannot save a scope with the same value as an existing one
|
//Error, cannot save a scope with the same value as an existing one
|
||||||
logger.error("Error: attempting to save a scope with a value that already exists: " + scope.getValue());
|
logger.error("Error: attempting to save a scope with a value that already exists: " + scope.getValue());
|
||||||
|
@ -163,6 +183,12 @@ public class ScopeAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateScope(SystemScope scope) throws ScopeException {
|
||||||
|
if (!pattern.matcher(scope.getValue()).matches()) {
|
||||||
|
throw new ScopeException(scope.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
|
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
|
||||||
public String deleteScope(@PathVariable("id") Long id, ModelMap m) {
|
public String deleteScope(@PathVariable("id") Long id, ModelMap m) {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* <copyright>
|
||||||
|
* <p>
|
||||||
|
* Copyright (c) 2010-2023 Gresham Technologies plc. All rights reserved.
|
||||||
|
*
|
||||||
|
* </copyright>
|
||||||
|
*/
|
||||||
|
package org.mitre.openid.connect.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hwsmith
|
||||||
|
*/
|
||||||
|
public class ScopeException extends Exception {
|
||||||
|
|
||||||
|
private final String invalidScope;
|
||||||
|
|
||||||
|
public ScopeException(String invalidScope) {
|
||||||
|
this.invalidScope = invalidScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return "The scope " + invalidScope + " is invalid as it contains non-alphabet characters";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ import java.lang.reflect.Type;
|
||||||
import java.sql.SQLIntegrityConstraintViolationException;
|
import java.sql.SQLIntegrityConstraintViolationException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.persistence.PersistenceException;
|
import javax.persistence.PersistenceException;
|
||||||
|
|
||||||
|
@ -33,9 +35,8 @@ import org.mitre.oauth2.model.ClientDetailsEntity.SubjectType;
|
||||||
import org.mitre.oauth2.model.PKCEAlgorithm;
|
import org.mitre.oauth2.model.PKCEAlgorithm;
|
||||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||||
import org.mitre.oauth2.web.AuthenticationUtilities;
|
import org.mitre.oauth2.web.AuthenticationUtilities;
|
||||||
|
import org.mitre.openid.connect.exception.ScopeException;
|
||||||
import org.mitre.openid.connect.exception.ValidationException;
|
import org.mitre.openid.connect.exception.ValidationException;
|
||||||
import org.mitre.openid.connect.model.CachedImage;
|
|
||||||
import org.mitre.openid.connect.service.ClientLogoLoadingService;
|
|
||||||
import org.mitre.openid.connect.view.ClientEntityViewForAdmins;
|
import org.mitre.openid.connect.view.ClientEntityViewForAdmins;
|
||||||
import org.mitre.openid.connect.view.ClientEntityViewForUsers;
|
import org.mitre.openid.connect.view.ClientEntityViewForUsers;
|
||||||
import org.mitre.openid.connect.view.HttpCodeView;
|
import org.mitre.openid.connect.view.HttpCodeView;
|
||||||
|
@ -45,10 +46,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||||
|
@ -130,11 +129,11 @@ public class ClientAPI {
|
||||||
|
|
||||||
public static final String URL = RootController.API_URL + "/clients";
|
public static final String URL = RootController.API_URL + "/clients";
|
||||||
|
|
||||||
@Autowired
|
private static final String characterMatcher = "[a-zA-Z]+";
|
||||||
private ClientDetailsEntityService clientService;
|
private static final Pattern pattern = Pattern.compile(characterMatcher);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ClientLogoLoadingService clientLogoLoadingService;
|
private ClientDetailsEntityService clientService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("clientAssertionValidator")
|
@Qualifier("clientAssertionValidator")
|
||||||
|
@ -229,6 +228,9 @@ public class ClientAPI {
|
||||||
public String apiGetAllClients(Model model, Authentication auth) {
|
public String apiGetAllClients(Model model, Authentication auth) {
|
||||||
|
|
||||||
Collection<ClientDetailsEntity> clients = clientService.getAllClients();
|
Collection<ClientDetailsEntity> clients = clientService.getAllClients();
|
||||||
|
|
||||||
|
clients.forEach(client -> client.setClientSecret(null));
|
||||||
|
|
||||||
model.addAttribute(JsonEntityView.ENTITY, clients);
|
model.addAttribute(JsonEntityView.ENTITY, clients);
|
||||||
|
|
||||||
if (AuthenticationUtilities.isAdmin(auth)) {
|
if (AuthenticationUtilities.isAdmin(auth)) {
|
||||||
|
@ -256,6 +258,12 @@ public class ClientAPI {
|
||||||
json = parser.parse(jsonString).getAsJsonObject();
|
json = parser.parse(jsonString).getAsJsonObject();
|
||||||
client = gson.fromJson(json, ClientDetailsEntity.class);
|
client = gson.fromJson(json, ClientDetailsEntity.class);
|
||||||
client = validateSoftwareStatement(client);
|
client = validateSoftwareStatement(client);
|
||||||
|
validateScopes(client.getScope());
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("apiAddClient failed due to ScopeException", e);
|
||||||
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.addAttribute(JsonErrorView.ERROR_MESSAGE, "Could not save new client. The server encountered a scope exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
} catch (JsonSyntaxException e) {
|
} catch (JsonSyntaxException e) {
|
||||||
logger.error("apiAddClient failed due to JsonSyntaxException", e);
|
logger.error("apiAddClient failed due to JsonSyntaxException", e);
|
||||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
@ -320,6 +328,8 @@ public class ClientAPI {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClientDetailsEntity newClient = clientService.saveNewClient(client);
|
ClientDetailsEntity newClient = clientService.saveNewClient(client);
|
||||||
|
|
||||||
|
//Set the client secret to the plaintext from the request
|
||||||
m.addAttribute(JsonEntityView.ENTITY, newClient);
|
m.addAttribute(JsonEntityView.ENTITY, newClient);
|
||||||
|
|
||||||
if (AuthenticationUtilities.isAdmin(auth)) {
|
if (AuthenticationUtilities.isAdmin(auth)) {
|
||||||
|
@ -367,6 +377,12 @@ public class ClientAPI {
|
||||||
json = parser.parse(jsonString).getAsJsonObject();
|
json = parser.parse(jsonString).getAsJsonObject();
|
||||||
client = gson.fromJson(json, ClientDetailsEntity.class);
|
client = gson.fromJson(json, ClientDetailsEntity.class);
|
||||||
client = validateSoftwareStatement(client);
|
client = validateSoftwareStatement(client);
|
||||||
|
validateScopes(client.getScope());
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("apiUpdateClient failed due to ScopeException", e);
|
||||||
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.addAttribute(JsonErrorView.ERROR_MESSAGE, "Could not update client. The server encountered a scope exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
} catch (JsonSyntaxException e) {
|
} catch (JsonSyntaxException e) {
|
||||||
logger.error("apiUpdateClient failed due to JsonSyntaxException", e);
|
logger.error("apiUpdateClient failed due to JsonSyntaxException", e);
|
||||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
@ -385,6 +401,7 @@ public class ClientAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientDetailsEntity oldClient = clientService.getClientById(id);
|
ClientDetailsEntity oldClient = clientService.getClientById(id);
|
||||||
|
String plaintextSecret = client.getClientSecret();
|
||||||
|
|
||||||
if (oldClient == null) {
|
if (oldClient == null) {
|
||||||
logger.error("apiUpdateClient failed; client with id " + id + " could not be found.");
|
logger.error("apiUpdateClient failed; client with id " + id + " could not be found.");
|
||||||
|
@ -408,10 +425,10 @@ public class ClientAPI {
|
||||||
|| client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)
|
|| client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)
|
||||||
|| client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT)) {
|
|| client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT)) {
|
||||||
|
|
||||||
// if they've asked for us to generate a client secret (or they left it blank but require one), do so here
|
// Once a client has been created, we only update the secret when asked to
|
||||||
if (json.has("generateClientSecret") && json.get("generateClientSecret").getAsBoolean()
|
if (json.has("generateClientSecret") && json.get("generateClientSecret").getAsBoolean()) {
|
||||||
|| Strings.isNullOrEmpty(client.getClientSecret())) {
|
|
||||||
client = clientService.generateClientSecret(client);
|
client = clientService.generateClientSecret(client);
|
||||||
|
plaintextSecret = client.getClientSecret();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) {
|
} else if (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) {
|
||||||
|
@ -438,6 +455,10 @@ public class ClientAPI {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClientDetailsEntity newClient = clientService.updateClient(oldClient, client);
|
ClientDetailsEntity newClient = clientService.updateClient(oldClient, client);
|
||||||
|
|
||||||
|
//Set the client secret to the plaintext from the request
|
||||||
|
newClient.setClientSecret(plaintextSecret);
|
||||||
|
|
||||||
m.addAttribute(JsonEntityView.ENTITY, newClient);
|
m.addAttribute(JsonEntityView.ENTITY, newClient);
|
||||||
|
|
||||||
if (AuthenticationUtilities.isAdmin(auth)) {
|
if (AuthenticationUtilities.isAdmin(auth)) {
|
||||||
|
@ -453,6 +474,14 @@ public class ClientAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateScopes(Set<String> scopes) throws ScopeException {
|
||||||
|
for (String s : scopes) {
|
||||||
|
if (!pattern.matcher(s).matches()) {
|
||||||
|
throw new ScopeException(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a client
|
* Delete a client
|
||||||
* @param id
|
* @param id
|
||||||
|
@ -497,6 +526,9 @@ public class ClientAPI {
|
||||||
return JsonErrorView.VIEWNAME;
|
return JsonErrorView.VIEWNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//We don't want the UI to get the secret
|
||||||
|
client.setClientSecret(null);
|
||||||
|
|
||||||
model.addAttribute(JsonEntityView.ENTITY, client);
|
model.addAttribute(JsonEntityView.ENTITY, client);
|
||||||
|
|
||||||
if (AuthenticationUtilities.isAdmin(auth)) {
|
if (AuthenticationUtilities.isAdmin(auth)) {
|
||||||
|
@ -506,31 +538,6 @@ public class ClientAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the logo image for a client
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
@RequestMapping(value = "/{id}/logo", method=RequestMethod.GET, produces = { MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE })
|
|
||||||
public ResponseEntity<byte[]> getClientLogo(@PathVariable("id") Long id, Model model) {
|
|
||||||
|
|
||||||
ClientDetailsEntity client = clientService.getClientById(id);
|
|
||||||
|
|
||||||
if (client == null) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
|
||||||
} else if (Strings.isNullOrEmpty(client.getLogoUri())) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
|
||||||
} else {
|
|
||||||
// get the image from cache
|
|
||||||
CachedImage image = clientLogoLoadingService.getLogo(client);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(MediaType.parseMediaType(image.getContentType()));
|
|
||||||
headers.setContentLength(image.getLength());
|
|
||||||
|
|
||||||
return new ResponseEntity<>(image.getData(), headers, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newClient) throws ValidationException {
|
private ClientDetailsEntity validateSoftwareStatement(ClientDetailsEntity newClient) throws ValidationException {
|
||||||
if (newClient.getSoftwareStatement() != null) {
|
if (newClient.getSoftwareStatement() != null) {
|
||||||
if (assertionValidator.isValid(newClient.getSoftwareStatement())) {
|
if (assertionValidator.isValid(newClient.getSoftwareStatement())) {
|
||||||
|
|
|
@ -150,6 +150,7 @@ public class DynamicClientRegistrationEndpoint {
|
||||||
* @param p
|
* @param p
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@PreAuthorize("hasRole('ROLE_USER')")
|
||||||
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public String registerNewClient(@RequestBody String jsonString, Model m) {
|
public String registerNewClient(@RequestBody String jsonString, Model m) {
|
||||||
|
|
||||||
|
@ -242,6 +243,7 @@ public class DynamicClientRegistrationEndpoint {
|
||||||
// send it all out to the view
|
// send it all out to the view
|
||||||
|
|
||||||
RegisteredClient registered = new RegisteredClient(savedClient, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(savedClient.getClientId(), "UTF-8"));
|
RegisteredClient registered = new RegisteredClient(savedClient, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(savedClient.getClientId(), "UTF-8"));
|
||||||
|
|
||||||
m.addAttribute("client", registered);
|
m.addAttribute("client", registered);
|
||||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.CREATED); // http 201
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.CREATED); // http 201
|
||||||
|
|
||||||
|
@ -377,6 +379,9 @@ public class DynamicClientRegistrationEndpoint {
|
||||||
|
|
||||||
RegisteredClient registered = new RegisteredClient(savedClient, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(savedClient.getClientId(), "UTF-8"));
|
RegisteredClient registered = new RegisteredClient(savedClient, token.getValue(), config.getIssuer() + "register/" + UriUtils.encodePathSegment(savedClient.getClientId(), "UTF-8"));
|
||||||
|
|
||||||
|
// We don't want the UI to receive the client secret
|
||||||
|
registered.setClientSecret(null);
|
||||||
|
|
||||||
// send it all out to the view
|
// send it all out to the view
|
||||||
m.addAttribute("client", registered);
|
m.addAttribute("client", registered);
|
||||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200
|
||||||
|
|
|
@ -91,6 +91,7 @@ public class ProtectedResourceRegistrationEndpoint {
|
||||||
* @param p
|
* @param p
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@PreAuthorize("hasRole('ROLE_USER')")
|
||||||
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public String registerNewProtectedResource(@RequestBody String jsonString, Model m) {
|
public String registerNewProtectedResource(@RequestBody String jsonString, Model m) {
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,10 @@ package org.mitre.openid.connect.web;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.mitre.openid.connect.exception.ScopeException;
|
||||||
import org.mitre.openid.connect.model.WhitelistedSite;
|
import org.mitre.openid.connect.model.WhitelistedSite;
|
||||||
import org.mitre.openid.connect.service.WhitelistedSiteService;
|
import org.mitre.openid.connect.service.WhitelistedSiteService;
|
||||||
import org.mitre.openid.connect.view.HttpCodeView;
|
import org.mitre.openid.connect.view.HttpCodeView;
|
||||||
|
@ -56,6 +59,8 @@ import com.google.gson.JsonParser;
|
||||||
public class WhitelistAPI {
|
public class WhitelistAPI {
|
||||||
|
|
||||||
public static final String URL = RootController.API_URL + "/whitelist";
|
public static final String URL = RootController.API_URL + "/whitelist";
|
||||||
|
private static final String characterMatcher = "[a-zA-Z]+";
|
||||||
|
private static final Pattern pattern = Pattern.compile(characterMatcher);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WhitelistedSiteService whitelistService;
|
private WhitelistedSiteService whitelistService;
|
||||||
|
@ -100,7 +105,12 @@ public class WhitelistAPI {
|
||||||
try {
|
try {
|
||||||
json = parser.parse(jsonString).getAsJsonObject();
|
json = parser.parse(jsonString).getAsJsonObject();
|
||||||
whitelist = gson.fromJson(json, WhitelistedSite.class);
|
whitelist = gson.fromJson(json, WhitelistedSite.class);
|
||||||
|
validateWhitelistScopes(whitelist.getAllowedScopes());
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("addNewWhitelistedSite failed due to ScopeException", e);
|
||||||
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.addAttribute(JsonErrorView.ERROR_MESSAGE, "Could not save new whitelisted site. The server encountered a scopes exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
logger.error("addNewWhitelistedSite failed due to JsonParseException", e);
|
logger.error("addNewWhitelistedSite failed due to JsonParseException", e);
|
||||||
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
@ -137,7 +147,12 @@ public class WhitelistAPI {
|
||||||
try {
|
try {
|
||||||
json = parser.parse(jsonString).getAsJsonObject();
|
json = parser.parse(jsonString).getAsJsonObject();
|
||||||
whitelist = gson.fromJson(json, WhitelistedSite.class);
|
whitelist = gson.fromJson(json, WhitelistedSite.class);
|
||||||
|
validateWhitelistScopes(whitelist.getAllowedScopes());
|
||||||
|
} catch (ScopeException e) {
|
||||||
|
logger.error("updateWhitelistedSite failed due to ScopeException", e);
|
||||||
|
m.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
m.put(JsonErrorView.ERROR_MESSAGE, "Could not update whitelisted site. The server encountered a scope exception. Contact a system administrator for assistance.");
|
||||||
|
return JsonErrorView.VIEWNAME;
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
logger.error("updateWhitelistedSite failed due to JsonParseException", e);
|
logger.error("updateWhitelistedSite failed due to JsonParseException", e);
|
||||||
m.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
m.put(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
|
||||||
|
@ -167,6 +182,14 @@ public class WhitelistAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateWhitelistScopes(Set<String> scopes) throws ScopeException {
|
||||||
|
for (String s : scopes) {
|
||||||
|
if (!pattern.matcher(s).matches()) {
|
||||||
|
throw new ScopeException(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a whitelisted site
|
* Delete a whitelisted site
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
|
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
|
||||||
|
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
|
||||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -86,16 +87,12 @@ public class TestBlacklistAwareRedirectResolver {
|
||||||
String res1 = resolver.resolveRedirect(goodUri, client);
|
String res1 = resolver.resolveRedirect(goodUri, client);
|
||||||
|
|
||||||
assertThat(res1, is(equalTo(goodUri)));
|
assertThat(res1, is(equalTo(goodUri)));
|
||||||
|
|
||||||
// set the resolver to non-strict and test the path-based redirect resolution
|
|
||||||
|
|
||||||
resolver.setStrictMatch(false);
|
|
||||||
|
|
||||||
String res2 = resolver.resolveRedirect(pathUri, client);
|
|
||||||
|
|
||||||
assertThat(res2, is(equalTo(pathUri)));
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=RedirectMismatchException.class)
|
||||||
|
public void testResovleRedirect_incorrect() {
|
||||||
|
resolver.resolveRedirect(pathUri, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidRequestException.class)
|
@Test(expected = InvalidRequestException.class)
|
||||||
|
@ -106,7 +103,7 @@ public class TestBlacklistAwareRedirectResolver {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
/*@Test
|
||||||
public void testRedirectMatches_default() {
|
public void testRedirectMatches_default() {
|
||||||
|
|
||||||
// this is not an exact match
|
// this is not an exact match
|
||||||
|
@ -153,5 +150,5 @@ public class TestBlacklistAwareRedirectResolver {
|
||||||
|
|
||||||
assertThat(res2, is(true));
|
assertThat(res2, is(true));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
99
pom.xml
99
pom.xml
|
@ -20,7 +20,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<name>MITREid Connect</name>
|
<name>MITREid Connect</name>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<parent>
|
<parent>
|
||||||
|
@ -72,6 +72,7 @@
|
||||||
<properties>
|
<properties>
|
||||||
<java-version>1.8</java-version>
|
<java-version>1.8</java-version>
|
||||||
<org.slf4j-version>1.7.25</org.slf4j-version>
|
<org.slf4j-version>1.7.25</org.slf4j-version>
|
||||||
|
<generateBackupPoms>false</generateBackupPoms>
|
||||||
</properties>
|
</properties>
|
||||||
<description>A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library.</description>
|
<description>A reference implementation of OpenID Connect (http://openid.net/connect/), OAuth 2.0, and UMA built on top of Java, Spring, and Spring Security. The project contains a fully functioning server, client, and utility library.</description>
|
||||||
<url>https://github.com/mitreid-connect</url>
|
<url>https://github.com/mitreid-connect</url>
|
||||||
|
@ -93,11 +94,6 @@
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>2.10.4</version>
|
<version>2.10.4</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>0.7.9</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
@ -261,24 +257,6 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<goals>
|
|
||||||
<goal>prepare-agent</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>report</id>
|
|
||||||
<phase>test</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>report</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-site-plugin</artifactId>
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
|
@ -341,10 +319,6 @@
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</reportPlugins>
|
</reportPlugins>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -365,7 +339,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-framework-bom</artifactId>
|
<artifactId>spring-framework-bom</artifactId>
|
||||||
<version>4.3.7.RELEASE</version>
|
<version>4.3.29.RELEASE</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -374,26 +348,26 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.9.0.pr2</version>
|
<version>2.15.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-annotations</artifactId>
|
<artifactId>jackson-annotations</artifactId>
|
||||||
<version>2.9.0.pr2</version>
|
<version>2.15.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Spring Security -->
|
<!-- Spring Security -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
<artifactId>spring-security-bom</artifactId>
|
<artifactId>spring-security-bom</artifactId>
|
||||||
<version>4.2.4.RELEASE</version>
|
<version>4.2.19.RELEASE</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security.oauth</groupId>
|
<groupId>org.springframework.security.oauth</groupId>
|
||||||
<artifactId>spring-security-oauth2</artifactId>
|
<artifactId>spring-security-oauth2</artifactId>
|
||||||
<version>2.1.0.RELEASE</version>
|
<version>2.1.4.RELEASE</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Servlet -->
|
<!-- Servlet -->
|
||||||
|
@ -429,7 +403,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<version>42.0.0.jre7</version>
|
<version>42.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.oracle</groupId>
|
<groupId>com.oracle</groupId>
|
||||||
|
@ -505,7 +479,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.12</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -520,6 +494,24 @@
|
||||||
<version>1.9.5</version>
|
<version>1.9.5</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>groovy</artifactId>
|
||||||
|
<version>2.5.9</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spockframework</groupId>
|
||||||
|
<artifactId>spock-core</artifactId>
|
||||||
|
<version>1.3-groovy-2.5</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
<!-- MITREid Connect components -->
|
<!-- MITREid Connect components -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
|
@ -564,17 +556,17 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>21.0</version>
|
<version>29.0-jre</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<version>2.8.0</version>
|
<version>2.8.9</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
<version>4.5.3</version>
|
<version>4.5.13</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>commons-logging</groupId>
|
<groupId>commons-logging</groupId>
|
||||||
|
@ -582,15 +574,26 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.minidev</groupId>
|
||||||
|
<artifactId>json-smart</artifactId>
|
||||||
|
<version>2.4.11</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.nimbusds</groupId>
|
<groupId>com.nimbusds</groupId>
|
||||||
<artifactId>nimbus-jose-jwt</artifactId>
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
<version>5.4</version>
|
<version>7.9</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>net.minidev</groupId>
|
||||||
|
<artifactId>json-smart</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
<version>[1.52,)</version>
|
<version>1.76</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.persistence</groupId>
|
<groupId>org.eclipse.persistence</groupId>
|
||||||
|
@ -598,15 +601,25 @@
|
||||||
<version>2.5.1</version>
|
<version>2.5.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>1.3.2</version>
|
<version>2.8.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ro.isdc.wro4j</groupId>
|
<groupId>ro.isdc.wro4j</groupId>
|
||||||
<artifactId>wro4j-extensions</artifactId>
|
<artifactId>wro4j-extensions</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.8.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.15.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.14</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>uma-server-webapp</artifactId>
|
<artifactId>uma-server-webapp</artifactId>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.mitre</groupId>
|
<groupId>org.mitre</groupId>
|
||||||
<artifactId>openid-connect-parent</artifactId>
|
<artifactId>openid-connect-parent</artifactId>
|
||||||
<version>1.3.3-SNAPSHOT</version>
|
<version>1.3.3.GRESHAM-30-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>uma-server</artifactId>
|
<artifactId>uma-server</artifactId>
|
||||||
|
|
Loading…
Reference in New Issue