├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pom.xml ├── settings.gradle └── src ├── main └── java │ └── org │ └── cloudfoundry │ └── identity │ └── uaa │ └── api │ ├── UaaConnectionFactory.java │ ├── client │ ├── UaaClientOperations.java │ └── impl │ │ └── UaaClientOperationsImpl.java │ ├── common │ ├── UaaConnection.java │ ├── impl │ │ ├── UaaConnectionHelper.java │ │ └── UaaConnectionImpl.java │ └── model │ │ ├── UaaTokenGrantType.java │ │ ├── WrappedSearchResults.java │ │ └── expr │ │ ├── AndOperator.java │ │ ├── BinaryOperation.java │ │ ├── ContainsOperator.java │ │ ├── EqualsOperation.java │ │ ├── FilterRequest.java │ │ ├── FilterRequestBuilder.java │ │ ├── GreaterEqualOperation.java │ │ ├── GreaterThanOperation.java │ │ ├── LessEqualOperation.java │ │ ├── LessThanOperation.java │ │ ├── NullOperation.java │ │ ├── Operation.java │ │ ├── OrOperator.java │ │ ├── PrecedenceOperator.java │ │ ├── PresentOperator.java │ │ ├── StartsWithOperator.java │ │ └── UnaryOperation.java │ ├── group │ ├── UaaGroupOperations.java │ └── impl │ │ └── UaaGroupOperationsImpl.java │ └── user │ ├── UaaUserOperations.java │ └── impl │ └── UaaUserOperationsImpl.java └── test └── java └── org └── cloudfoundry └── identity └── uaa └── api └── client └── test ├── AbstractOperationTest.java ├── FilterRequestBuilderTest.java ├── UaaClientOperationTest.java ├── UaaGroupOperationTest.java ├── UaaServerAvailable.java └── UaaUserOperationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | .project 4 | .classpath 5 | .settings/ 6 | .gradle/ 7 | build/ 8 | uaa-java-client.iml 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | script: 3 | - ./gradlew test 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Client for Cloud Foundry UAA 2 | 3 | The Cloud Foundry User Account and Authentication (UAA) service is a set of JSON 4 | APIs to manage user accounts, user groups, and OAuth2 clients. This project aims 5 | to be an easy-to-use and feature complete Java library for these APIs. 6 | 7 | ## Quick Start 8 | 9 | Note: this library only works with UAA 2.1.0 and later. Versions prior to 2.1.0 10 | should use the `1.0.0-RELEASE` tag, which has been tested with 2.0 and later, but 11 | should work with 1.9 and later. Be warned that the object model has changed 12 | significantly from the `1.0.0-RELEASE` tag to use existing objects rather than 13 | custom POJOs. 14 | 15 | ``` 16 | $ git clone https://github.com/cloudfoundry-incubator/uaa-java-client.git 17 | $ mvn package 18 | ``` 19 | 20 | ## Running Tests 21 | 22 | While the `test` goal of the Maven project should always complete successfully, 23 | certain tests do require a local UAA server to be running for them to pass. If 24 | a UAA server is not running on port 8080, the tests will be skipped. To run a 25 | copy of the UAA on port 8080, follow the [Quick Start](https://github.com/cloudfoundry/uaa/blob/master/README.md) guide for the 26 | UAA. The tests expect a clean UAA database as bootstrapped by the server, so if 27 | a test fails, simply restart the UAA server and you should be back in a clean 28 | state. 29 | 30 | ## Getting Started in code 31 | 32 | ```java 33 | import java.net.URL; 34 | 35 | import org.cloudfoundry.identity.uaa.api.UaaConnectionFactory; 36 | import org.cloudfoundry.identity.uaa.api.common.UaaConnection; 37 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 38 | import org.cloudfoundry.identity.uaa.api.user.UaaUserOperations; 39 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 40 | import org.cloudfoundry.identity.uaa.scim.ScimUser; 41 | import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; 42 | import org.springframework.security.oauth2.common.AuthenticationScheme; 43 | 44 | public class GetUsersNamedJohn { 45 | public static void main(String[] args) throws Exception { 46 | 47 | ResourceOwnerPasswordResourceDetails credentials = new ResourceOwnerPasswordResourceDetails(); 48 | credentials.setAccessTokenUri("http://localhost:8080/uaa/oauth/token"); 49 | credentials.setClientAuthenticationScheme(AuthenticationScheme.header); 50 | credentials.setClientId("app"); 51 | credentials.setClientSecret("appclientsecret"); 52 | credentials.setUsername("myuser"); 53 | credentials.setPassword("mypassword"); 54 | 55 | URL uaaHost = new URL("http://localhost:8080/uaa"); 56 | UaaConnection connection = UaaConnectionFactory.getConnection(uaaHost, credentials); 57 | UaaUserOperations operations = connection.userOperations(); 58 | 59 | FilterRequestBuilder builder = new FilterRequestBuilder(); 60 | builder.startsWith("username", "john."); 61 | 62 | SearchResults results = operations.getUsers(builder.build()); 63 | for (ScimUser user : results.getResources()) { 64 | System.out.println(user.getId()); 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | ## More Information 71 | 72 | Javadoc is available by building the code (or by running `mvn javadoc:javadoc`). 73 | Authoratative information about the UAA is located [here](https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst) 74 | 75 | ## Contributing 76 | 77 | Pull requests are welcome! Current to-dos including writing test cases for group 78 | mapping APIs and making it easier for test cases to point to an existing UAA server. 79 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'maven' 3 | 4 | group = 'org.cloudfoundry' 5 | version = '1.0.1-SNAPSHOT' 6 | 7 | description = """Java UAA API Client""" 8 | 9 | sourceCompatibility = 1.7 10 | targetCompatibility = 1.7 11 | 12 | task wrapper(type: Wrapper) { 13 | gradleVersion = "2.5" 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | maven { url "http://repo.spring.io/libs-release-local" } 19 | } 20 | dependencies { 21 | compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version:'2.0.7.RELEASE' 22 | compile group: 'org.cloudfoundry.identity', name: 'cloudfoundry-identity-scim', version: '2.4.0' 23 | 24 | testCompile group: 'junit', name: 'junit', version:'4.12' 25 | } 26 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfoundry-incubator/uaa-java-client/a5715699ce8b5e1da25f811827283b2fa30a0b48/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 13 09:46:43 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.cloudfoundry 5 | uaa-java-client 6 | Java UAA API Client 7 | 8 | 9 | Spring 10 | http://repo.spring.io/libs-release-local 11 | 12 | 13 | 14 | 15 | org.springframework.security.oauth 16 | spring-security-oauth2 17 | 2.0.7.RELEASE 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.12 24 | test 25 | 26 | 27 | org.cloudfoundry.identity 28 | cloudfoundry-identity-scim 29 | 2.4.0 30 | 31 | 32 | 33 | 34 | 35 | maven-compiler-plugin 36 | 37 | 1.7 38 | 1.7 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-source-plugin 44 | 45 | 46 | attach-sources 47 | 48 | jar 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-javadoc-plugin 56 | 57 | 58 | attach-javadocs 59 | 60 | jar 61 | 62 | 63 | 64 | 65 | 66 | 67 | 1.0.2-SNAPSHOT 68 | 69 | UTF-8 70 | 71 | 72 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'uaa-java-client' 2 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/UaaConnectionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api; 15 | 16 | import java.net.URL; 17 | 18 | import org.cloudfoundry.identity.uaa.api.common.UaaConnection; 19 | import org.cloudfoundry.identity.uaa.api.common.impl.UaaConnectionHelper; 20 | import org.cloudfoundry.identity.uaa.api.common.impl.UaaConnectionImpl; 21 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; 22 | 23 | /** 24 | * The initial entry point for the API classes 25 | * 26 | * @author Josh Ghiloni 27 | * 28 | */ 29 | public final class UaaConnectionFactory { 30 | private UaaConnectionFactory() { 31 | 32 | } 33 | 34 | /** 35 | * Get a connection object for the given UAA server, from which you can get access to different API operations. 36 | * 37 | * @param uaaUrl the base {@link URL} of the UAA server. May have a path prefix (for example, 38 | * http://localhost:8080/uaa) 39 | * @param credentials the {@link OAuth2ProtectedResourceDetails} representing the current user. May be client-only 40 | * @return the connection entry point 41 | */ 42 | public static UaaConnection getConnection(URL uaaUrl, OAuth2ProtectedResourceDetails credentials) { 43 | UaaConnectionHelper helper = new UaaConnectionHelper(uaaUrl, credentials); 44 | return new UaaConnectionImpl(helper); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/client/UaaClientOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client; 15 | 16 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 17 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 18 | import org.springframework.security.oauth2.provider.client.BaseClientDetails; 19 | 20 | /** 21 | * Provides endpoints to the UAA client APIs specified here 24 | * 25 | * @author Josh Ghiloni 26 | * 27 | */ 28 | public interface UaaClientOperations { 29 | /** 30 | * Create a new UAA Client 31 | * 32 | * @param client the new client 33 | * @return the newly created client 34 | */ 35 | public BaseClientDetails create(BaseClientDetails client); 36 | 37 | /** 38 | * Find a given client by its ID 39 | * @param clientId the client ID 40 | * @return the client, or null if not found 41 | */ 42 | public BaseClientDetails findById(String clientId); 43 | 44 | /** 45 | * Update the client. Secrets cannot be changed in this method. 46 | * 47 | * @param updated the client with new data 48 | * @return the client returned from the API 49 | * @see #changeClientSecret(String, String, String) 50 | */ 51 | public BaseClientDetails update(BaseClientDetails updated); 52 | 53 | /** 54 | * Delete the client with the given ID 55 | * 56 | * @param clientId 57 | * @return 58 | */ 59 | public BaseClientDetails delete(String clientId); 60 | 61 | /** 62 | * Get clients based on the given SCIM filter. 63 | * 64 | * @param request 65 | * @return the clients 66 | * @see org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder 67 | */ 68 | public SearchResults getClients(FilterRequest request); 69 | 70 | /** 71 | * Change a client's secret. Note that you MUST have the existing secret, as the APIs require it. 72 | * 73 | * @param clientId The client ID whose secret should be changed 74 | * @param oldSecret The existing secret 75 | * @param newSecret The new secret 76 | * @return true if the change was successful, false otherwise 77 | */ 78 | public boolean changeClientSecret(String clientId, String oldSecret, String newSecret); 79 | } -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/client/impl/UaaClientOperationsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.impl; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | import org.apache.commons.logging.Log; 20 | import org.apache.commons.logging.LogFactory; 21 | import org.cloudfoundry.identity.uaa.api.client.UaaClientOperations; 22 | import org.cloudfoundry.identity.uaa.api.common.impl.UaaConnectionHelper; 23 | import org.cloudfoundry.identity.uaa.api.common.model.WrappedSearchResults; 24 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 25 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 26 | import org.springframework.core.ParameterizedTypeReference; 27 | import org.springframework.security.oauth2.provider.client.BaseClientDetails; 28 | import org.springframework.util.Assert; 29 | 30 | /** 31 | * @see UaaClientOperations 32 | * @author Josh Ghiloni 33 | */ 34 | public class UaaClientOperationsImpl implements UaaClientOperations { 35 | 36 | private static final ParameterizedTypeReference STRING_REF = new ParameterizedTypeReference() {}; 37 | 38 | private static final ParameterizedTypeReference CLIENT_REF = new ParameterizedTypeReference() {}; 39 | 40 | private static final ParameterizedTypeReference> CLIENTS_REF = new ParameterizedTypeReference>() {}; 41 | 42 | private static final Log log = LogFactory.getLog(UaaClientOperationsImpl.class); 43 | 44 | private UaaConnectionHelper helper; 45 | 46 | public UaaClientOperationsImpl(UaaConnectionHelper helper) { 47 | this.helper = helper; 48 | } 49 | 50 | public BaseClientDetails create(BaseClientDetails client) { 51 | Assert.notNull(client); 52 | Assert.hasText(client.getClientId()); 53 | 54 | return helper.post("/oauth/clients", client, CLIENT_REF); 55 | } 56 | 57 | public BaseClientDetails findById(String clientId) { 58 | Assert.hasText(clientId); 59 | return helper.get("/oauth/clients/{id}", CLIENT_REF, clientId); 60 | } 61 | 62 | public BaseClientDetails update(BaseClientDetails client) { 63 | Assert.notNull(client); 64 | Assert.hasText(client.getClientId()); 65 | 66 | return helper.put("/oauth/clients/{id}", client, CLIENT_REF, client.getClientId()); 67 | } 68 | 69 | public BaseClientDetails delete(String clientId) { 70 | Assert.hasText(clientId); 71 | return helper.delete("/oauth/clients/{id}", CLIENT_REF, clientId); 72 | } 73 | 74 | public SearchResults getClients(FilterRequest request) { 75 | Assert.notNull(request); 76 | 77 | return helper.get(helper.buildScimFilterUrl("/oauth/clients", request), CLIENTS_REF); 78 | } 79 | 80 | public boolean changeClientSecret(String clientId, String oldSecret, String newSecret) { 81 | Map body = new HashMap(2); 82 | body.put("oldSecret", oldSecret); 83 | body.put("secret", newSecret); 84 | 85 | String result = helper.put("/oauth/clients/{id}/secret", body, STRING_REF, clientId); 86 | if (log.isDebugEnabled()) { 87 | log.debug(result); 88 | } 89 | 90 | return (result != null); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/UaaConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common; 15 | 16 | import org.cloudfoundry.identity.uaa.api.client.UaaClientOperations; 17 | import org.cloudfoundry.identity.uaa.api.group.UaaGroupOperations; 18 | import org.cloudfoundry.identity.uaa.api.user.UaaUserOperations; 19 | 20 | /** 21 | * A collection of objects to access different parts of the API 22 | * 23 | * @author Josh Ghiloni 24 | * 25 | */ 26 | public interface UaaConnection { 27 | /** 28 | * @return an entry point for client APIs 29 | */ 30 | public UaaClientOperations clientOperations(); 31 | 32 | /** 33 | * @return an entry point for group APIS 34 | */ 35 | public UaaGroupOperations groupOperations(); 36 | 37 | /** 38 | * @return an entry point for user APIs 39 | */ 40 | public UaaUserOperations userOperations(); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/impl/UaaConnectionHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.impl; 15 | 16 | import java.io.IOException; 17 | import java.net.URL; 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.List; 23 | 24 | import org.apache.commons.logging.Log; 25 | import org.apache.commons.logging.LogFactory; 26 | import org.cloudfoundry.identity.uaa.api.common.model.WrappedSearchResults; 27 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 28 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 29 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 30 | import org.cloudfoundry.identity.uaa.scim.ScimCore; 31 | import org.cloudfoundry.identity.uaa.scim.ScimUser; 32 | import org.springframework.core.ParameterizedTypeReference; 33 | import org.springframework.http.HttpEntity; 34 | import org.springframework.http.HttpHeaders; 35 | import org.springframework.http.HttpMethod; 36 | import org.springframework.http.HttpRequest; 37 | import org.springframework.http.HttpStatus; 38 | import org.springframework.http.MediaType; 39 | import org.springframework.http.ResponseEntity; 40 | import org.springframework.http.client.ClientHttpRequestExecution; 41 | import org.springframework.http.client.ClientHttpRequestInterceptor; 42 | import org.springframework.http.client.ClientHttpResponse; 43 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; 44 | import org.springframework.security.oauth2.client.token.AccessTokenProvider; 45 | import org.springframework.security.oauth2.client.token.AccessTokenProviderChain; 46 | import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; 47 | import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider; 48 | import org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider; 49 | import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider; 50 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 51 | import org.springframework.util.Assert; 52 | import org.springframework.util.CollectionUtils; 53 | import org.springframework.util.StringUtils; 54 | import org.springframework.web.client.RestTemplate; 55 | 56 | /** 57 | * A helper clas used by the various *Operations implementations to handle JSON HTTP communications with 58 | * the UAA server 59 | * 60 | * @author Josh Ghiloni 61 | * 62 | */ 63 | public class UaaConnectionHelper { 64 | private static final AccessTokenProviderChain CHAIN = new AccessTokenProviderChain( 65 | Arrays. asList(new ClientCredentialsAccessTokenProvider(), 66 | new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider())); 67 | 68 | private OAuth2AccessToken token; 69 | 70 | private URL url; 71 | 72 | private OAuth2ProtectedResourceDetails creds; 73 | 74 | private static final Log log = LogFactory.getLog(UaaConnectionHelper.class); 75 | 76 | /** 77 | * Establish connectivity information for this session. 78 | * 79 | * @param url 80 | * @param creds 81 | * @see org.cloudfoundry.identity.uaa.api.UaaConnectionFactory#getConnection(URL, OAuth2ProtectedResourceDetails) 82 | */ 83 | public UaaConnectionHelper(URL url, OAuth2ProtectedResourceDetails creds) { 84 | this.url = url; 85 | this.creds = creds; 86 | } 87 | 88 | /** 89 | * Do an HTTP GET 90 | * 91 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 92 | * @param responseType the object type to be returned 93 | * @param uriVariables any uri variables 94 | * @return the response body 95 | * @see #exchange(HttpMethod, Object, String, ParameterizedTypeReference, Object...) 96 | */ 97 | public ResponseType get(String uri, ParameterizedTypeReference responseType, 98 | Object... uriVariables) { 99 | return exchange(HttpMethod.GET, null, uri, responseType, uriVariables); 100 | } 101 | 102 | /** 103 | * Do an HTTP DELETE 104 | * 105 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 106 | * @param responseType the object type to be returned 107 | * @param uriVariables any uri variables 108 | * @return the response body 109 | * @see #exchange(HttpMethod, Object, String, ParameterizedTypeReference, Object...) 110 | */ 111 | public ResponseType delete(String uri, ParameterizedTypeReference responseType, 112 | Object... uriVariables) { 113 | return exchange(HttpMethod.DELETE, null, uri, responseType, uriVariables); 114 | } 115 | 116 | /** 117 | * Do an HTTP POST 118 | * 119 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 120 | * @param body the request body 121 | * @param responseType the object type to be returned 122 | * @param uriVariables any uri variables 123 | * @return the response body 124 | * @see #exchange(HttpMethod, Object, String, ParameterizedTypeReference, Object...) 125 | */ 126 | public ResponseType post(String uri, RequestType body, 127 | ParameterizedTypeReference responseType, Object... uriVariables) { 128 | return exchange(HttpMethod.POST, body, uri, responseType, uriVariables); 129 | } 130 | 131 | /** 132 | * Do an HTTP PUT 133 | * 134 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 135 | * @param body the request body 136 | * @param responseType the object type to be returned 137 | * @param uriVariables any uri variables 138 | * @return the response body 139 | * @see #exchange(HttpMethod, Object, String, ParameterizedTypeReference, Object...) 140 | */ 141 | public ResponseType put(String uri, RequestType body, 142 | ParameterizedTypeReference responseType, Object... uriVariables) { 143 | return exchange(HttpMethod.PUT, body, uri, responseType, uriVariables); 144 | } 145 | 146 | /** 147 | * Do an HTTP PUT with SCIM features. SCIM requires PUT requests of a SCIM object have the version of the object set 148 | * as the If-Match request header. 149 | * 150 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 151 | * @param body the request body 152 | * @param responseType the object type to be returned 153 | * @param uriVariables any uri variables 154 | * @return the response body 155 | * @see #exchange(HttpMethod, HttpHeaders, Object, String, ParameterizedTypeReference, Object...) 156 | */ 157 | public ResponseType putScimObject(String uri, RequestType body, 158 | ParameterizedTypeReference responseType, Object... uriVariables) { 159 | HttpHeaders headers = new HttpHeaders(); 160 | headers.set("if-match", String.valueOf(body.getMeta().getVersion())); 161 | 162 | return exchange(HttpMethod.PUT, headers, body, uri, responseType, uriVariables); 163 | } 164 | 165 | /** 166 | * Convenience method to get a user ID for a given username. Equivalent to calling 167 | * 168 | *
169 | 	 * {@link org.cloudfoundry.identity.uaa.api.user.UaaUserOperations UaaUserOperations} operations = connection.userOperations();
170 | 	 * 
171 | 	 * {@link FilterRequestBuilder} builder = new FilterRequestBuilder();
172 | 	 * builder.equals("username", userName).attributes("id");
173 | 	 * 
174 | 	 * SearchResults<ScimUser> users = operations.getUsers(builder.build());
175 | 	 * 
176 | 	 * return users.getResources().iterator().next().getId();
177 | 	 * 
178 | * 179 | * @param userName the userName 180 | * @return the user ID 181 | */ 182 | public String getUserIdByName(String userName) { 183 | FilterRequestBuilder builder = new FilterRequestBuilder(); 184 | builder.equals("username", userName).attributes("id"); 185 | 186 | FilterRequest request = builder.build(); 187 | 188 | String uri = buildScimFilterUrl("/Users", request); 189 | 190 | try { 191 | SearchResults retval = exchange(HttpMethod.GET, null, uri, 192 | new ParameterizedTypeReference>() { 193 | }); 194 | 195 | Collection resources = retval.getResources(); 196 | 197 | if (CollectionUtils.isEmpty(resources)) { 198 | return null; 199 | } 200 | 201 | ScimUser user = resources.iterator().next(); 202 | return user.getId(); 203 | } 204 | catch (Throwable t) { 205 | t.printStackTrace(); 206 | return null; 207 | } 208 | } 209 | 210 | /** 211 | * Make a REST call with default headers 212 | * 213 | * @param method the Http Method (GET, POST, etc) 214 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 215 | * @param body the request body 216 | * @param responseType the object type to be returned 217 | * @param uriVariables any uri variables 218 | * @return the response body 219 | * @see #exchange(HttpMethod, HttpHeaders, Object, String, ParameterizedTypeReference, Object...) 220 | */ 221 | private ResponseType exchange(HttpMethod method, RequestType body, String uri, 222 | ParameterizedTypeReference responseType, Object... uriVariables) { 223 | return exchange(method, new HttpHeaders(), body, uri, responseType, uriVariables); 224 | } 225 | 226 | /** 227 | * Make a REST call with custom headers 228 | * 229 | * @param method the Http Method (GET, POST, etc) 230 | * @param uri the URI of the endpoint (relative to the base URL set in the constructor) 231 | * @param body the request body 232 | * @param responseType the object type to be returned 233 | * @param uriVariables any uri variables 234 | * @return the response body 235 | * @see org.springframework.web.client.RestTemplate#exchange(String, HttpMethod, HttpEntity, ParameterizedTypeReference, Object...) 236 | */ 237 | private ResponseType exchange(HttpMethod method, HttpHeaders headers, RequestType body, 238 | String uri, ParameterizedTypeReference responseType, Object... uriVariables) { 239 | getHeaders(headers); 240 | 241 | RestTemplate template = new RestTemplate(); 242 | template.setInterceptors(LoggerInterceptor.INTERCEPTOR); 243 | 244 | HttpEntity requestEntity = null; 245 | if (body == null) { 246 | requestEntity = new HttpEntity(headers); 247 | } 248 | else { 249 | requestEntity = new HttpEntity(body, headers); 250 | } 251 | 252 | // combine url into the varargs 253 | List varList = new ArrayList(); 254 | varList.add(url); 255 | if (uriVariables != null && uriVariables.length > 0) { 256 | varList.addAll(Arrays.asList(uriVariables)); 257 | } 258 | 259 | ResponseEntity responseEntity = template.exchange("{base}" + uri, method, requestEntity, 260 | responseType, varList.toArray()); 261 | 262 | if (HttpStatus.Series.SUCCESSFUL.equals(responseEntity.getStatusCode().series())) { 263 | return responseEntity.getBody(); 264 | } 265 | else { 266 | return null; 267 | } 268 | } 269 | 270 | /** 271 | * Because variable substitution used by {@link org.springframework.web.client.RestTemplate} escapes things in a way 272 | * that makes SCIM filtering difficult, manually include the parameters in the uri 273 | * 274 | * @param baseUrl the url relative to the base URL (i.e. /Users, /oauth/clients, etc) 275 | * @param request the Filter Request to populate the URL 276 | * @return the URL 277 | */ 278 | public String buildScimFilterUrl(String baseUrl, FilterRequest request) { 279 | StringBuilder uriBuilder = new StringBuilder(baseUrl); 280 | 281 | boolean hasParams = false; 282 | 283 | if (request.getAttributes() != null && !request.getAttributes().isEmpty()) { 284 | uriBuilder.append("?attributes=").append( 285 | StringUtils.collectionToCommaDelimitedString(request.getAttributes())); 286 | 287 | hasParams = true; 288 | } 289 | 290 | if (StringUtils.hasText(request.getFilter())) { 291 | if (hasParams) { 292 | uriBuilder.append("&"); 293 | } 294 | else { 295 | uriBuilder.append("?"); 296 | } 297 | 298 | uriBuilder.append("filter=").append(request.getFilter()); 299 | hasParams = true; 300 | } 301 | 302 | if (request.getStart() > 0) { 303 | if (hasParams) { 304 | uriBuilder.append("&"); 305 | } 306 | else { 307 | uriBuilder.append("?"); 308 | } 309 | 310 | uriBuilder.append("startIndex=").append(request.getStart()); 311 | hasParams = true; 312 | } 313 | 314 | if (request.getCount() > 0) { 315 | if (hasParams) { 316 | uriBuilder.append("&"); 317 | } 318 | else { 319 | uriBuilder.append("?"); 320 | } 321 | 322 | uriBuilder.append("count=").append(request.getCount()); 323 | hasParams = true; 324 | } 325 | 326 | return uriBuilder.toString(); 327 | } 328 | 329 | /** 330 | * Add the Authorization, Content-Type, and Accept headers to the request 331 | * 332 | * @param headers 333 | */ 334 | private void getHeaders(HttpHeaders headers) { 335 | OAuth2AccessToken token = getAccessToken(); 336 | headers.add("Authorization", token.getTokenType() + " " + token.getValue()); 337 | 338 | if (headers.getContentType() == null) { 339 | headers.setContentType(MediaType.APPLICATION_JSON); 340 | } 341 | 342 | if (headers.getAccept() == null || headers.getAccept().size() == 0) { 343 | headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); 344 | } 345 | } 346 | 347 | /** 348 | * Get the OAuth access token (and refresh it if necessary) 349 | * 350 | * @return 351 | */ 352 | private OAuth2AccessToken getAccessToken() { 353 | if (token == null) { 354 | token = CHAIN.obtainAccessToken(creds, new DefaultAccessTokenRequest()); 355 | } 356 | else if (token.isExpired()) { 357 | refreshAccessToken(); 358 | } 359 | 360 | return token; 361 | } 362 | 363 | /** 364 | * refresh the access token 365 | */ 366 | private void refreshAccessToken() { 367 | Assert.notNull(token); 368 | 369 | token = CHAIN.refreshAccessToken(creds, token.getRefreshToken(), new DefaultAccessTokenRequest()); 370 | } 371 | 372 | /** 373 | * An interceptor used to log information about HTTP calls 374 | * 375 | * @author Josh Ghiloni 376 | * 377 | */ 378 | private static class LoggerInterceptor implements ClientHttpRequestInterceptor { 379 | public static final List INTERCEPTOR = Arrays 380 | . asList(new LoggerInterceptor()); 381 | 382 | public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) 383 | throws IOException { 384 | if (log.isDebugEnabled()) { 385 | log.debug(new String(body, "UTF-8")); 386 | } 387 | 388 | return execution.execute(request, body); 389 | } 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/impl/UaaConnectionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.impl; 15 | 16 | import org.cloudfoundry.identity.uaa.api.client.UaaClientOperations; 17 | import org.cloudfoundry.identity.uaa.api.client.impl.UaaClientOperationsImpl; 18 | import org.cloudfoundry.identity.uaa.api.common.UaaConnection; 19 | import org.cloudfoundry.identity.uaa.api.group.UaaGroupOperations; 20 | import org.cloudfoundry.identity.uaa.api.group.impl.UaaGroupOperationsImpl; 21 | import org.cloudfoundry.identity.uaa.api.user.UaaUserOperations; 22 | import org.cloudfoundry.identity.uaa.api.user.impl.UaaUserOperationsImpl; 23 | 24 | /** 25 | * @see UaaConnection 26 | * @author Josh Ghiloni 27 | */ 28 | public class UaaConnectionImpl implements UaaConnection { 29 | 30 | private UaaConnectionHelper helper; 31 | 32 | public UaaConnectionImpl(UaaConnectionHelper helper) { 33 | this.helper = helper; 34 | } 35 | 36 | public UaaClientOperations clientOperations() { 37 | return new UaaClientOperationsImpl(helper); 38 | } 39 | 40 | public UaaGroupOperations groupOperations() { 41 | return new UaaGroupOperationsImpl(helper); 42 | } 43 | 44 | public UaaUserOperations userOperations() { 45 | return new UaaUserOperationsImpl(helper); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/UaaTokenGrantType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model; 15 | 16 | /** 17 | * Represent the OAuth token type 18 | * 19 | * @author Josh Ghiloni 20 | */ 21 | public enum UaaTokenGrantType { 22 | client_credentials, implicit, authorization_code, password, refresh_token 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/WrappedSearchResults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.cloudfoundry.identity.uaa.api.common.model; 16 | 17 | import java.util.Collections; 18 | 19 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 20 | 21 | /** 22 | * Used until SearchResults has a default constructor availabe. 23 | * 24 | * @author Josh Ghiloni 25 | * 26 | */ 27 | public class WrappedSearchResults extends SearchResults { 28 | 29 | public WrappedSearchResults() { 30 | super(Collections.emptyList(), Collections.emptyList(), 0, 0, 0); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/AndOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class AndOperator extends BinaryOperation { 21 | AndOperator(Operation left, Operation right) { 22 | super(left, right); 23 | this.operator = "and"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/BinaryOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | import java.text.SimpleDateFormat; 17 | import java.util.Calendar; 18 | import java.util.Date; 19 | 20 | /** 21 | * @author Josh Ghiloni 22 | * 23 | */ 24 | abstract class BinaryOperation extends UnaryOperation { 25 | protected R right; 26 | 27 | protected String operator; 28 | 29 | private static final String ISO_STRING = "yyyy-MM-dd'T'HH:mm:ssXX"; 30 | private static final SimpleDateFormat ISO = new SimpleDateFormat(ISO_STRING); 31 | 32 | /** 33 | * @param left 34 | * @param right 35 | */ 36 | BinaryOperation(L left, R right) { 37 | super(left); 38 | this.right = right; 39 | } 40 | 41 | R getRight() { 42 | return right; 43 | } 44 | 45 | 46 | @Override 47 | public String toString() { 48 | StringBuilder expr = new StringBuilder(); 49 | expr.append(left).append(' ').append(operator).append(' '); 50 | 51 | if (right == null) { 52 | expr.append("null"); 53 | } 54 | else if (right instanceof String) { 55 | expr.append('"').append(right).append('"'); 56 | } 57 | else if (right instanceof Date) { 58 | expr.append('"').append(ISO.format((Date) right)).append('"'); 59 | } 60 | else if (right instanceof Calendar) { 61 | // preserve timezone 62 | SimpleDateFormat sdf = new SimpleDateFormat(ISO_STRING); 63 | sdf.setTimeZone(((Calendar)right).getTimeZone()); 64 | 65 | expr.append('"').append(sdf.format(((Calendar) right).getTime())).append('"'); 66 | } 67 | else if ((right instanceof Number) || (right instanceof Boolean) || (right instanceof Operation)) { 68 | expr.append(right); 69 | } 70 | else { 71 | throw new IllegalArgumentException(String.format("Invalid type %s for RHS", right.getClass().getName())); 72 | } 73 | 74 | return expr.toString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/ContainsOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class ContainsOperator extends BinaryOperation { 21 | ContainsOperator(String left, String right) { 22 | super(left, right); 23 | this.operator = "co"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/EqualsOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class EqualsOperation extends BinaryOperation { 21 | EqualsOperation(String left, Object right) { 22 | super(left, right); 23 | this.operator = "eq"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/FilterRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * A class used to filter results from SCIM paged APIs. Must be constructed using {@link FilterRequestBuilder} 20 | * 21 | * @author Josh Ghiloni 22 | * 23 | */ 24 | public class FilterRequest { 25 | private String filter; 26 | 27 | private List attributes; 28 | 29 | private int start; 30 | 31 | private int count; 32 | 33 | public FilterRequest() { 34 | 35 | } 36 | 37 | FilterRequest(String filter, List attributes, int start, int count) { 38 | this.filter = filter; 39 | this.attributes = attributes; 40 | this.start = start; 41 | this.count = count; 42 | } 43 | 44 | /** 45 | * The SCIM filter string 46 | * @return 47 | */ 48 | public String getFilter() { 49 | return filter; 50 | } 51 | 52 | /** 53 | * @return The 1-based starting index for the query. If < 0, indicates the start of the list 54 | */ 55 | public int getStart() { 56 | return start; 57 | } 58 | 59 | /** 60 | * @return The number of items to be returned by the query. If < 1, no count will be passed to the API, and the 61 | * default UAA behavior will take place. 62 | */ 63 | public int getCount() { 64 | return count; 65 | } 66 | 67 | /** 68 | * @return The attributes to be returned by the query. If the list is empty, all attributes are returned. 69 | */ 70 | public List getAttributes() { 71 | return attributes; 72 | } 73 | 74 | static final FilterRequest SHOW_ALL = new FilterRequest(null, null, 0, 0); 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/FilterRequestBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Stack; 19 | 20 | /** 21 | * A class used to create SCIM filter strings. All methods (except 22 | * {@link #build()} and {@link #showAll()}) return the object, so chaining can be employed. By default, conditions are 23 | * joined with a logical and, but can be joined with or by passing false to the 24 | * constructor. Operations can be joined in prefix manner; that is, 25 | * 26 | *
 27 |  * builder.present("mykey").lessThan("mykey", 10).greaterThanOrEquals("mykey", 50).or().and().build()
 28 |  * 
29 | * 30 | * Will generate a filter string "mykey pr and mykey lt 10 or mykey ge 50". To group using parentheses, use 31 | * the {@link #precedence()} operator. For example, the previous filter could look like this: 32 | * 33 | *
 34 |  * builder.present("mykey").lessThan("mykey", 10).greaterThanOrEquals("mykey", 50).or().precedence().and().build()
 35 |  * 
36 | * 37 | * and would generate a filter string "mykey pr and (mykey lt 10 or mykey ge 50)". 38 | * 39 | * Note that all values are quoted according to SCIM standards (that is, Strings are wrapped in double quotes and Dates 40 | * are double quoted and rendered in ISO 8601 format) 41 | * 42 | * @author Josh Ghiloni 43 | * 44 | */ 45 | public class FilterRequestBuilder { 46 | 47 | private boolean defaultAnd = true; 48 | 49 | private Stack opStack = new Stack(); 50 | 51 | private List attributes = null; 52 | 53 | private int start = 0; 54 | 55 | private int count = 0; 56 | 57 | private boolean built = false; 58 | 59 | /** 60 | * Create a new builder, joining operations with "and" 61 | */ 62 | public FilterRequestBuilder() { 63 | this(true); 64 | } 65 | 66 | /** 67 | * Create a new builder 68 | * @param defaultAnd if true, join operations with "and", otherwise "or" 69 | */ 70 | public FilterRequestBuilder(boolean defaultAnd) { 71 | this.defaultAnd = defaultAnd; 72 | } 73 | 74 | /** 75 | * Creates an operation that renders as key eq val 76 | * @param key the key to check for comparison 77 | * @param val the value to check for comparison 78 | * @return the builder object (for chaining) 79 | * @throws IllegalStateException if {@link #build()} has already been called 80 | */ 81 | public FilterRequestBuilder equals(String key, Object val) { 82 | verifyActive(); 83 | opStack.push(new EqualsOperation(key, val)); 84 | return this; 85 | } 86 | 87 | /** 88 | * Creates an operation that renders as key lt val 89 | * @param key the key to check for comparison 90 | * @param val the value to check for comparison 91 | * @return the builder object (for chaining) 92 | * @throws IllegalStateException if {@link #build()} has already been called 93 | */ 94 | public FilterRequestBuilder lessThan(String key, Object val) { 95 | verifyActive(); 96 | opStack.push(new LessThanOperation(key, val)); 97 | return this; 98 | } 99 | 100 | /** 101 | * Creates an operation that renders as key gt val 102 | * @param key the key to check for comparison 103 | * @param val the value to check for comparison 104 | * @return the builder object (for chaining) 105 | * @throws IllegalStateException if {@link #build()} has already been called 106 | */ 107 | public FilterRequestBuilder greaterThan(String key, Object val) { 108 | verifyActive(); 109 | opStack.push(new GreaterThanOperation(key, val)); 110 | return this; 111 | } 112 | 113 | /** 114 | * Creates an operation that renders as key le val 115 | * @param key the key to check for comparison 116 | * @param val the value to check for comparison 117 | * @return the builder object (for chaining) 118 | * @throws IllegalStateException if {@link #build()} has already been called 119 | */ 120 | public FilterRequestBuilder lessThanOrEquals(String key, Object val) { 121 | verifyActive(); 122 | opStack.push(new LessEqualOperation(key, val)); 123 | return this; 124 | } 125 | 126 | /** 127 | * Creates an operation that renders as key ge val 128 | * @param key the key to check for comparison 129 | * @param val the value to check for comparison 130 | * @return the builder object (for chaining) 131 | * @throws IllegalStateException if {@link #build()} has already been called 132 | */ 133 | public FilterRequestBuilder greaterThanOrEquals(String key, Object val) { 134 | verifyActive(); 135 | opStack.push(new GreaterEqualOperation(key, val)); 136 | return this; 137 | } 138 | 139 | /** 140 | * Creates an operation that renders as key sw "val" 141 | * @param key the key to check for comparison 142 | * @param val the value to check for comparison (must be String) 143 | * @return the builder object (for chaining) 144 | * @throws IllegalStateException if {@link #build()} has already been called 145 | */ 146 | public FilterRequestBuilder startsWith(String key, String val) { 147 | verifyActive(); 148 | opStack.push(new StartsWithOperator(key, val)); 149 | return this; 150 | } 151 | 152 | /** 153 | * Creates an operation that renders as key co "val" 154 | * @param key the key to check for comparison 155 | * @param val the value to check for comparison (must be String) 156 | * @return the builder object (for chaining) 157 | * @throws IllegalStateException if {@link #build()} has already been called 158 | */ 159 | public FilterRequestBuilder contains(String key, String val) { 160 | verifyActive(); 161 | opStack.push(new ContainsOperator(key, val)); 162 | return this; 163 | } 164 | 165 | /** 166 | * Creates an operation that renders as key pr 167 | * @param key the key to check for comparison 168 | * @return the builder object (for chaining) 169 | * @throws IllegalStateException if {@link #build()} has already been called 170 | */ 171 | public FilterRequestBuilder present(String key) { 172 | verifyActive(); 173 | opStack.push(new PresentOperator(key)); 174 | return this; 175 | } 176 | 177 | /** 178 | * Pops the most recent two expressions off the expression stack and joins them as expr1 and expr2 179 | * 180 | * @return the builder object (for chaining) 181 | * @throws IllegalStateException if there are fewer than two expressions defined or if {@link #build()} has already 182 | * been called 183 | */ 184 | public FilterRequestBuilder and() { 185 | verifyActive(); 186 | 187 | if (opStack.size() < 2) { 188 | throw new IllegalStateException("need at least two operations to join"); 189 | } 190 | 191 | Operation second = opStack.pop(); 192 | Operation first = opStack.pop(); 193 | 194 | opStack.push(new AndOperator(first, second)); 195 | return this; 196 | } 197 | 198 | /** 199 | * Pops the most recent two expressions off the expression stack and joins them as expr1 or expr2 200 | * 201 | * @return the builder object (for chaining) 202 | * @throws IllegalStateException if there are fewer than two expressions defined or if {@link #build()} has already 203 | * been called 204 | */ 205 | public FilterRequestBuilder or() { 206 | verifyActive(); 207 | 208 | if (opStack.size() < 2) { 209 | throw new IllegalStateException("need at least two operations to join"); 210 | } 211 | 212 | Operation second = opStack.pop(); 213 | Operation first = opStack.pop(); 214 | 215 | opStack.push(new OrOperator(first, second)); 216 | return this; 217 | } 218 | 219 | /** 220 | * Pops the most current expression off the stack and wraps it in parentheses. Note that if it already is wrapped in 221 | * parentheses, nothing will change. 222 | * 223 | * @return the builder object (for chaining) 224 | * @throws IllegalStateException if there are no expressions defined or if {@link #build()} has already been called 225 | */ 226 | public FilterRequestBuilder precedence() { 227 | verifyActive(); 228 | if (opStack.isEmpty()) { 229 | throw new IllegalStateException("need an operation to set precedence"); 230 | } 231 | 232 | Operation op = opStack.peek(); 233 | 234 | if (!(op instanceof PrecedenceOperator)) { 235 | opStack.pop(); 236 | opStack.push(new PrecedenceOperator(op)); 237 | } 238 | 239 | return this; 240 | } 241 | 242 | /** 243 | * Sets the attributes to be returned by this request 244 | * 245 | * @param attributes the attributes to be returned. If null or empty, this method is a no-op 246 | * @return The builder object (for chaining) 247 | * @throws IllegalStateException if {@link #build()} has already been called 248 | */ 249 | public FilterRequestBuilder attributes(String... attributes) { 250 | verifyActive(); 251 | 252 | if (attributes != null && attributes.length > 0) { 253 | this.attributes = Arrays.asList(attributes); 254 | } 255 | 256 | return this; 257 | } 258 | 259 | /** 260 | * Set the start index. Note that if this value is <= 1, this is a no-op 261 | * @param start The index 262 | * @return The builder object (for chaining) 263 | * @throws IllegalStateException if {@link #build()} has already been called 264 | */ 265 | public FilterRequestBuilder start(int start) { 266 | verifyActive(); 267 | this.start = start; 268 | return this; 269 | } 270 | 271 | /** 272 | * Set the page size. Note that if this value is <= 1, this is a no-op 273 | * @param count The page size 274 | * @return The builder object (for chaining) 275 | * @throws IllegalStateException if {@link #build()} has already been called 276 | */ 277 | public FilterRequestBuilder count(int count) { 278 | verifyActive(); 279 | this.count = count; 280 | return this; 281 | } 282 | 283 | /** 284 | * A convenience method to return a {@link FilterRequest} that allows all items to be returned 285 | * @return a {@link FilterRequest} 286 | */ 287 | public static FilterRequest showAll() { 288 | return FilterRequest.SHOW_ALL; 289 | } 290 | 291 | /** 292 | * Build the {@link FilterRequest} to be used by a SCIM API call. Once this method is called, subsequent method 293 | * calls will result in an {@link IllegalStateException} 294 | * 295 | * @return the {@link FilterRequest} built by this builder 296 | * @throws IllegalStateException if {@link #build()} has already been called 297 | */ 298 | public FilterRequest build() { 299 | verifyActive(); 300 | 301 | Operation filter = joinAll(); 302 | built = true; 303 | 304 | return new FilterRequest(filter.toString(), attributes, start, count); 305 | } 306 | 307 | private Operation joinAll() { 308 | if (opStack.isEmpty()) { 309 | return NullOperation.INSTANCE; 310 | } 311 | 312 | if (opStack.size() == 1) { 313 | return opStack.pop(); 314 | } 315 | 316 | while (opStack.size() > 1) { 317 | Operation second = opStack.pop(); 318 | Operation first = opStack.pop(); 319 | 320 | Operation joined = defaultAnd ? new AndOperator(first, second) : new OrOperator(first, second); 321 | opStack.push(joined); 322 | } 323 | 324 | return opStack.pop(); 325 | } 326 | 327 | private void verifyActive() { 328 | if (built) { 329 | throw new IllegalStateException("Builder already built"); 330 | } 331 | } 332 | } -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/GreaterEqualOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class GreaterEqualOperation extends BinaryOperation { 21 | GreaterEqualOperation(String left, Object right) { 22 | super(left, right); 23 | this.operator = "ge"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/GreaterThanOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class GreaterThanOperation extends BinaryOperation { 21 | GreaterThanOperation(String left, Object right) { 22 | super(left, right); 23 | this.operator = "gt"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/LessEqualOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class LessEqualOperation extends BinaryOperation { 21 | LessEqualOperation(String left, Object right) { 22 | super(left, right); 23 | this.operator = "le"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/LessThanOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class LessThanOperation extends BinaryOperation { 21 | LessThanOperation(String left, Object right) { 22 | super(left, right); 23 | this.operator = "lt"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/NullOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class NullOperation implements Operation { 21 | 22 | private NullOperation() { 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return null; 28 | } 29 | 30 | static final NullOperation INSTANCE = new NullOperation(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/Operation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | interface Operation { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/OrOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class OrOperator extends BinaryOperation { 21 | OrOperator(Operation left, Operation right) { 22 | super(left, right); 23 | this.operator = "or"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/PrecedenceOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class PrecedenceOperator extends UnaryOperation { 21 | PrecedenceOperator(Operation left) { 22 | super(left); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder expr = new StringBuilder(); 28 | expr.append("(").append(left).append(")"); 29 | 30 | return expr.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/PresentOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class PresentOperator extends UnaryOperation { 21 | PresentOperator(String left) { 22 | super(left); 23 | this.operator = "pr"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/StartsWithOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | /** 17 | * @author Josh Ghiloni 18 | * 19 | */ 20 | class StartsWithOperator extends BinaryOperation { 21 | StartsWithOperator(String left, String right) { 22 | super(left, right); 23 | this.operator = "sw"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/common/model/expr/UnaryOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.common.model.expr; 15 | 16 | 17 | /** 18 | * @author Josh Ghiloni 19 | * 20 | */ 21 | abstract class UnaryOperation implements Operation { 22 | protected L left; 23 | 24 | protected String operator; 25 | 26 | /** 27 | * @param left 28 | */ 29 | UnaryOperation(L left) { 30 | this.left = left; 31 | } 32 | 33 | L getLeft() { 34 | return left; 35 | } 36 | 37 | String getOperator() { 38 | return operator; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | StringBuilder expr = new StringBuilder(); 44 | expr.append(left).append(' ').append(operator); 45 | 46 | return expr.toString(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/group/UaaGroupOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.group; 15 | 16 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 17 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 18 | import org.cloudfoundry.identity.uaa.scim.ScimGroup; 19 | import org.cloudfoundry.identity.uaa.scim.ScimGroupExternalMember; 20 | 21 | /** 22 | * Provides endpoints to the UAA group APIs specified here 24 | * 25 | * @author Josh Ghiloni 26 | * 27 | */ 28 | public interface UaaGroupOperations { 29 | public enum ScimGroupExternalMemberType { 30 | groupId, displayName 31 | } 32 | 33 | /** 34 | * Create a group in the UAA database 35 | * 36 | * @param group The partial group to be created. Members will not be created in this call 37 | * @return The newly created group, with id and meta information 38 | * @see #addMember(String, String) 39 | */ 40 | public ScimGroup createGroup(ScimGroup group); 41 | 42 | /** 43 | * Update the display name in the UAA database 44 | * 45 | * @param groupId the ID of the group 46 | * @param newName the new display name 47 | * @return the group with the specified group ID and the new Name 48 | */ 49 | public ScimGroup updateGroupName(String groupId, String newName); 50 | 51 | /** 52 | * Add a member to the group 53 | * 54 | * @param groupId the group id 55 | * @param memberName the member's username (will be converted to ID) 56 | * @return the group with the member in it 57 | */ 58 | public ScimGroup addMember(String groupId, String memberName); 59 | 60 | /** 61 | * Remove a member from the group 62 | * 63 | * @param groupId the group id 64 | * @param memberName the member's username (will be converted to ID) 65 | * @return the group without the member in it 66 | */ 67 | public ScimGroup deleteMember(String groupId, String memberName); 68 | 69 | /** 70 | * Delete the group from the database. An exception will be thrown if the operation fails 71 | * 72 | * @param groupId the group ID 73 | */ 74 | public void deleteGroup(String groupId); 75 | 76 | /** 77 | * Get a page of groups based on the given {@link FilterRequest} 78 | * 79 | * @param request the {@link FilterRequest} 80 | * @return The page of groups. 81 | * @see org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder 82 | */ 83 | public SearchResults getGroups(FilterRequest request); 84 | 85 | /** 86 | * Create a mapping from an external LDAP group to an internal UAA group. Only effective when UAA is configured with 87 | * ldap/ldap-groups-map-to-scopes.xml (see here) 90 | * 91 | * @param type mapping the local group by displayName or id 92 | * @param identifier the identifier specified by type 93 | * @param externalGroupDn the DN of the LDAP group 94 | * @return the new mapping 95 | */ 96 | public ScimGroupExternalMember createGroupMapping(ScimGroupExternalMemberType type, String identifier, 97 | String externalGroupDn); 98 | 99 | /** 100 | * Delete the group mapping. Only effective when UAA is configured with ldap/ldap-groups-map-to-scopes.xml (see here). An exception is thrown if the operation fails. 103 | * 104 | * @param mapping the mapping to delete 105 | */ 106 | public void deleteGroupMapping(ScimGroupExternalMember mapping); 107 | 108 | /** 109 | * List the group mappings with an optional filter. Only effective when UAA is configured with 110 | * ldap/ldap-groups-map-to-scopes.xml (see here) 113 | * 114 | * @param request the filter 115 | * @return the list of group mappings 116 | * @see org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder 117 | */ 118 | public SearchResults getGroupMappings(FilterRequest request); 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/group/impl/UaaGroupOperationsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.group.impl; 15 | 16 | import static org.cloudfoundry.identity.uaa.scim.ScimCore.SCHEMAS; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | import java.util.LinkedHashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import org.cloudfoundry.identity.uaa.api.common.impl.UaaConnectionHelper; 25 | import org.cloudfoundry.identity.uaa.api.common.model.WrappedSearchResults; 26 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 27 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 28 | import org.cloudfoundry.identity.uaa.api.group.UaaGroupOperations; 29 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 30 | import org.cloudfoundry.identity.uaa.scim.ScimGroup; 31 | import org.cloudfoundry.identity.uaa.scim.ScimGroupExternalMember; 32 | import org.cloudfoundry.identity.uaa.scim.ScimGroupMember; 33 | import org.springframework.core.ParameterizedTypeReference; 34 | import org.springframework.http.HttpHeaders; 35 | import org.springframework.util.Assert; 36 | import org.springframework.util.StringUtils; 37 | 38 | /** 39 | * @see UaaGroupOperations 40 | * 41 | * @author Josh Ghiloni 42 | */ 43 | public class UaaGroupOperationsImpl implements UaaGroupOperations { 44 | 45 | private static final ParameterizedTypeReference OBJ_REF = new ParameterizedTypeReference() { 46 | }; 47 | 48 | private static final ParameterizedTypeReference GROUP_REF = new ParameterizedTypeReference() { 49 | }; 50 | 51 | private static final ParameterizedTypeReference EXT_GROUP_REF = new ParameterizedTypeReference() { 52 | }; 53 | 54 | private static final ParameterizedTypeReference> GROUPS_REF = new ParameterizedTypeReference>() { 55 | }; 56 | 57 | private static final ParameterizedTypeReference> EXT_GROUPS_REF = new ParameterizedTypeReference>() { 58 | }; 59 | 60 | private UaaConnectionHelper helper; 61 | 62 | public UaaGroupOperationsImpl(UaaConnectionHelper helper) { 63 | this.helper = helper; 64 | } 65 | 66 | public ScimGroup createGroup(ScimGroup group) { 67 | Assert.notNull(group); 68 | Assert.hasText(group.getDisplayName()); 69 | 70 | group.setSchemas(SCHEMAS); 71 | 72 | return helper.post("/Groups", group, GROUP_REF); 73 | } 74 | 75 | public void deleteGroup(String groupId) { 76 | Assert.hasText(groupId); 77 | 78 | helper.delete("/Groups/{id}", OBJ_REF, groupId); 79 | } 80 | 81 | public SearchResults getGroups(FilterRequest request) { 82 | Assert.notNull(request); 83 | 84 | return helper.get(helper.buildScimFilterUrl("/Groups", request), GROUPS_REF); 85 | } 86 | 87 | public ScimGroupExternalMember createGroupMapping(ScimGroupExternalMemberType type, String identifier, 88 | String externalGroupDn) { 89 | Assert.notNull(type); 90 | Assert.hasText(identifier); 91 | Assert.hasText(externalGroupDn); 92 | 93 | Map request = new LinkedHashMap(3); 94 | 95 | request.put("schemas", SCHEMAS); 96 | request.put(type.toString(), identifier); 97 | request.put("externalGroup", externalGroupDn); 98 | 99 | return helper.post("/Groups/External", request, EXT_GROUP_REF); 100 | } 101 | 102 | public void deleteGroupMapping(ScimGroupExternalMember mapping) { 103 | Assert.notNull(mapping); 104 | 105 | String id = null; 106 | String type = null; 107 | String external = mapping.getExternalGroup(); 108 | 109 | if (StringUtils.hasText(mapping.getGroupId())) { 110 | id = mapping.getGroupId(); 111 | type = "groupId"; 112 | } 113 | else { 114 | id = mapping.getDisplayName(); 115 | type = "displayName"; 116 | } 117 | 118 | helper.delete("/Groups/External/{type}/{id}/externalGroup/{externalGroup}", OBJ_REF, type, id, external); 119 | } 120 | 121 | public SearchResults getGroupMappings(FilterRequest request) { 122 | Assert.notNull(request); 123 | return helper.get(helper.buildScimFilterUrl("/Groups/External", request), EXT_GROUPS_REF); 124 | } 125 | 126 | public ScimGroup updateGroupName(String groupId, String newName) { 127 | ScimGroup group = getGroupById(groupId); 128 | group.setDisplayName(newName); 129 | 130 | return updateGroup(group); 131 | } 132 | 133 | public ScimGroup addMember(String groupId, String memberUserName) { 134 | Assert.hasText(memberUserName); 135 | 136 | ScimGroup group = getGroupById(groupId); 137 | 138 | String memberId = helper.getUserIdByName(memberUserName); 139 | 140 | List members = group.getMembers(); 141 | if (members == null) { 142 | members = new ArrayList(1); 143 | } 144 | 145 | ScimGroupMember member = new ScimGroupMember(memberId); 146 | members.add(member); 147 | group.setMembers(members); 148 | 149 | return updateGroup(group); 150 | } 151 | 152 | public ScimGroup deleteMember(String groupId, String memberUserName) { 153 | Assert.hasText(memberUserName); 154 | 155 | ScimGroup group = getGroupById(groupId); 156 | 157 | String memberId = helper.getUserIdByName(memberUserName); 158 | 159 | List members = group.getMembers(); 160 | if (members != null && !members.isEmpty()) { 161 | for (Iterator iter = members.iterator(); iter.hasNext();) { 162 | ScimGroupMember member = iter.next(); 163 | 164 | if (memberId.equals(member.getMemberId())) { 165 | iter.remove(); 166 | break; 167 | } 168 | } 169 | } 170 | 171 | return updateGroup(group); 172 | } 173 | 174 | private ScimGroup getGroupById(String groupId) { 175 | FilterRequest request = new FilterRequestBuilder().equals("id", groupId).build(); 176 | SearchResults results = getGroups(request); 177 | 178 | if (results.getTotalResults() > 0) { 179 | return results.getResources().iterator().next(); 180 | } 181 | 182 | return null; 183 | } 184 | 185 | private ScimGroup updateGroup(ScimGroup group) { 186 | Assert.notNull(group); 187 | 188 | HttpHeaders headers = new HttpHeaders(); 189 | headers.set("if-match", String.valueOf(group.getMeta().getVersion())); 190 | 191 | return helper.putScimObject("/Groups/{id}", group, GROUP_REF, group.getId()); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/user/UaaUserOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.user; 15 | 16 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 17 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 18 | import org.cloudfoundry.identity.uaa.scim.ScimUser; 19 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; 20 | 21 | /** 22 | * Provides endpoints to the UAA user APIs specified here 24 | * 25 | * @author Josh Ghiloni 26 | * 27 | */ 28 | public interface UaaUserOperations { 29 | /** 30 | * Create the user in the UAA database with the given parameters. 31 | * 32 | * @param user The (partial) user information to be created 33 | * @return The newly created user. Will have id and meta information set 34 | */ 35 | public ScimUser createUser(ScimUser user); 36 | 37 | /** 38 | * Update the user in the UAA database. Cannot use this method to change the user's password. 39 | * 40 | * @param user the updated user 41 | * @return the user as returned from the UAA api 42 | * @see #changeUserPassword(String, String, String) 43 | */ 44 | public ScimUser updateUser(ScimUser user); 45 | 46 | /** 47 | * Delete the user from UAA. Will throw an Exception if the operation fails 48 | * 49 | * @param userId The id of the user 50 | */ 51 | public void deleteUser(String userId); 52 | 53 | /** 54 | * Change the given user's password. You must have uaa.admin and password.write scopes in 55 | * your {@link OAuth2ProtectedResourceDetails} object. An exception will be thrown if the operation fails. 56 | * 57 | * TODO: Add a method to change the current user's password, if not in a client-credential-only scope 58 | * 59 | * @param userId the user's id (not their username) 60 | * @param oldPassword the old password 61 | * @param newPassword the new password 62 | */ 63 | public void changeUserPassword(String userId, String oldPassword, String newPassword); 64 | 65 | /** 66 | * Looks up a user in the database by their name. 67 | * 68 | * @param userName the user's username 69 | * @return the user object for this user, or null if the user does not exist or the operation fails 70 | * @see #getUsers(FilterRequest) 71 | */ 72 | public ScimUser getUserByName(String userName); 73 | 74 | /** 75 | * Get a page of users based on the given {@link FilterRequest} 76 | * 77 | * @param request the {@link FilterRequest} 78 | * @return The page of users. 79 | * @see org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder 80 | */ 81 | public SearchResults getUsers(FilterRequest request); 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/cloudfoundry/identity/uaa/api/user/impl/UaaUserOperationsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.user.impl; 15 | 16 | import static org.cloudfoundry.identity.uaa.scim.ScimCore.*; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import org.cloudfoundry.identity.uaa.api.common.impl.UaaConnectionHelper; 22 | import org.cloudfoundry.identity.uaa.api.common.model.WrappedSearchResults; 23 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 24 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 25 | import org.cloudfoundry.identity.uaa.api.user.UaaUserOperations; 26 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 27 | import org.cloudfoundry.identity.uaa.scim.ScimUser; 28 | import org.springframework.core.ParameterizedTypeReference; 29 | import org.springframework.util.Assert; 30 | import org.springframework.util.CollectionUtils; 31 | 32 | /** 33 | * @see UaaUserOperations 34 | * 35 | * @author Josh Ghiloni 36 | * @author Thomas Darimont 37 | * 38 | */ 39 | public class UaaUserOperationsImpl implements UaaUserOperations { 40 | private UaaConnectionHelper helper; 41 | 42 | private static final ParameterizedTypeReference USER_REF = new ParameterizedTypeReference() { 43 | }; 44 | 45 | private static final ParameterizedTypeReference STRING_REF = new ParameterizedTypeReference() { 46 | }; 47 | 48 | private static final ParameterizedTypeReference> USERS_REF = new ParameterizedTypeReference>() { 49 | }; 50 | 51 | public UaaUserOperationsImpl(UaaConnectionHelper helper) { 52 | this.helper = helper; 53 | } 54 | 55 | public ScimUser createUser(ScimUser user) { 56 | Assert.notNull(user); 57 | Assert.hasText(user.getUserName()); 58 | 59 | user.setSchemas(SCHEMAS); 60 | 61 | return helper.post("/Users", user, USER_REF); 62 | } 63 | 64 | public ScimUser updateUser(ScimUser user) { 65 | Assert.notNull(user); 66 | Assert.hasText(user.getId()); 67 | 68 | // don't try to update the stuff we can't update here 69 | if (!CollectionUtils.isEmpty(user.getGroups())) { 70 | user.getGroups().clear(); 71 | } 72 | 73 | user.setPassword(null); 74 | 75 | return helper.putScimObject("/Users/{id}", user, USER_REF, user.getId()); 76 | } 77 | 78 | public void deleteUser(String userId) { 79 | Assert.hasText(userId); 80 | helper.delete("/Users/{id}", STRING_REF, userId); 81 | } 82 | 83 | public void changeUserPassword(String userId, String oldPassword, String newPassword) { 84 | Assert.hasText(userId); 85 | Assert.hasText(oldPassword); 86 | Assert.hasText(newPassword); 87 | 88 | Map passwordChange = new HashMap(); 89 | passwordChange.put("password", newPassword); 90 | passwordChange.put("oldPassword", oldPassword); 91 | 92 | helper.put("/Users/{id}/password", passwordChange, STRING_REF, userId); 93 | } 94 | 95 | public SearchResults getUsers(FilterRequest request) { 96 | Assert.notNull(request); 97 | 98 | return helper.get(helper.buildScimFilterUrl("/Users", request), USERS_REF); 99 | } 100 | 101 | public ScimUser getUserByName(String userName) { 102 | FilterRequest request = new FilterRequestBuilder().equals("username", userName).build(); 103 | SearchResults result = getUsers(request); 104 | 105 | if (result != null && result.getResources() != null && result.getResources().size() == 1) { 106 | return result.getResources().iterator().next(); 107 | } 108 | 109 | return null; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/AbstractOperationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import java.net.URL; 17 | 18 | import org.cloudfoundry.identity.uaa.api.UaaConnectionFactory; 19 | import org.cloudfoundry.identity.uaa.api.common.UaaConnection; 20 | import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; 21 | import org.springframework.security.oauth2.common.AuthenticationScheme; 22 | 23 | /** 24 | * @author Josh Ghiloni 25 | */ 26 | public abstract class AbstractOperationTest { 27 | 28 | private static final String UAA_BASE_URL = "http://localhost:8080/uaa"; 29 | 30 | protected ClientCredentialsResourceDetails getDefaultClientCredentials() { 31 | 32 | ClientCredentialsResourceDetails credentials = new ClientCredentialsResourceDetails(); 33 | credentials.setAccessTokenUri(UAA_BASE_URL + "/oauth/token"); 34 | credentials.setClientAuthenticationScheme(AuthenticationScheme.header); 35 | credentials.setClientId("admin"); 36 | credentials.setClientSecret("adminsecret"); 37 | 38 | return credentials; 39 | } 40 | 41 | protected UaaConnection getConnection() throws Exception { 42 | return getConnection(getDefaultClientCredentials()); 43 | } 44 | 45 | protected UaaConnection getConnection(ClientCredentialsResourceDetails clientCredentials) throws Exception { 46 | return UaaConnectionFactory.getConnection(new URL(UAA_BASE_URL), clientCredentials); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/FilterRequestBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.fail; 18 | 19 | import java.util.Arrays; 20 | import java.util.Calendar; 21 | import java.util.List; 22 | import java.util.TimeZone; 23 | 24 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequest; 25 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 26 | import org.junit.Test; 27 | 28 | /** 29 | * @author Josh Ghiloni 30 | * 31 | */ 32 | public class FilterRequestBuilderTest { 33 | 34 | @Test 35 | public void testComparisonOperators() throws Exception { 36 | FilterRequestBuilder builder = new FilterRequestBuilder(); 37 | builder.equals("test", true); 38 | 39 | FilterRequest request = builder.build(); 40 | assertEquals("test eq true", request.getFilter()); 41 | 42 | builder = new FilterRequestBuilder(); 43 | builder.greaterThan("test", 4.2); 44 | 45 | request = builder.build(); 46 | assertEquals("test gt 4.2", request.getFilter()); 47 | 48 | builder = new FilterRequestBuilder(); 49 | builder.greaterThanOrEquals("test", -10); 50 | 51 | request = builder.build(); 52 | assertEquals("test ge -10", request.getFilter()); 53 | 54 | builder = new FilterRequestBuilder(); 55 | builder.lessThan("test", 0); 56 | 57 | request = builder.build(); 58 | assertEquals("test lt 0", request.getFilter()); 59 | 60 | Calendar testDate = Calendar.getInstance(); 61 | testDate.setTimeInMillis(0); 62 | testDate.setTimeZone(TimeZone.getTimeZone("UTC")); 63 | 64 | builder = new FilterRequestBuilder(); 65 | builder.lessThanOrEquals("test", testDate); 66 | 67 | request = builder.build(); 68 | assertEquals("test le \"1970-01-01T00:00:00Z\"", request.getFilter()); 69 | 70 | builder = new FilterRequestBuilder(); 71 | builder.startsWith("test", "test"); 72 | 73 | request = builder.build(); 74 | assertEquals("test sw \"test\"", request.getFilter()); 75 | 76 | builder = new FilterRequestBuilder(); 77 | builder.contains("test", "val"); 78 | 79 | request = builder.build(); 80 | assertEquals("test co \"val\"", request.getFilter()); 81 | } 82 | 83 | @Test 84 | public void testUnaryOperators() throws Exception { 85 | FilterRequestBuilder builder = new FilterRequestBuilder(); 86 | builder.present("test"); 87 | 88 | FilterRequest request = builder.build(); 89 | assertEquals("test pr", request.getFilter()); 90 | 91 | builder = new FilterRequestBuilder(); 92 | builder.equals("test", false).precedence(); 93 | 94 | request = builder.build(); 95 | assertEquals("(test eq false)", request.getFilter()); 96 | } 97 | 98 | @Test 99 | public void testJoinOperators() throws Exception { 100 | FilterRequestBuilder builder = new FilterRequestBuilder(); 101 | builder.equals("foo", "bar").present("test").and(); 102 | 103 | FilterRequest request = builder.build(); 104 | assertEquals("foo eq \"bar\" and test pr", request.getFilter()); 105 | 106 | builder = new FilterRequestBuilder(); 107 | builder.equals("foo", "bar").present("test").or(); 108 | 109 | request = builder.build(); 110 | assertEquals("foo eq \"bar\" or test pr", request.getFilter()); 111 | 112 | builder = new FilterRequestBuilder(); 113 | builder.equals("foo", "bar").present("test").contains("test", "val").and().or(); 114 | 115 | request = builder.build(); 116 | assertEquals("foo eq \"bar\" or test pr and test co \"val\"", request.getFilter()); 117 | 118 | builder = new FilterRequestBuilder(); 119 | builder.equals("foo", true).present("test").startsWith("test", "val").and().precedence().or(); 120 | 121 | request = builder.build(); 122 | assertEquals("foo eq true or (test pr and test sw \"val\")", request.getFilter()); 123 | } 124 | 125 | @Test 126 | public void testDefaultJoin() throws Exception { 127 | FilterRequestBuilder builder = new FilterRequestBuilder(); 128 | builder.equals("foo", "bar").present("test").greaterThan("test", 4.3); 129 | 130 | FilterRequest request = builder.build(); 131 | assertEquals("foo eq \"bar\" and test pr and test gt 4.3", request.getFilter()); 132 | 133 | builder = new FilterRequestBuilder(false); 134 | builder.equals("foo", "bar").present("test").greaterThan("test", 4.3); 135 | 136 | request = builder.build(); 137 | assertEquals("foo eq \"bar\" or test pr or test gt 4.3", request.getFilter()); 138 | } 139 | 140 | @Test 141 | public void testAttributes() throws Exception { 142 | FilterRequestBuilder builder = new FilterRequestBuilder(); 143 | builder.attributes("id", "name", "address"); 144 | 145 | FilterRequest request = builder.build(); 146 | 147 | List expectedAttributes = Arrays.asList("id", "name", "address"); 148 | 149 | assertEquals(expectedAttributes, request.getAttributes()); 150 | } 151 | 152 | @Test 153 | public void testCount() throws Exception { 154 | FilterRequestBuilder builder = new FilterRequestBuilder(); 155 | FilterRequest request = builder.build(); 156 | 157 | assertEquals(0, request.getCount()); 158 | 159 | builder = new FilterRequestBuilder().count(10); 160 | request = builder.build(); 161 | 162 | assertEquals(10, request.getCount()); 163 | } 164 | 165 | @Test 166 | public void testStart() throws Exception { 167 | FilterRequestBuilder builder = new FilterRequestBuilder(); 168 | FilterRequest request = builder.build(); 169 | 170 | assertEquals(0, request.getStart()); 171 | 172 | builder = new FilterRequestBuilder().start(10); 173 | request = builder.build(); 174 | 175 | assertEquals(10, request.getStart()); 176 | } 177 | 178 | @Test 179 | public void testSubsequentPrecedence() throws Exception { 180 | FilterRequestBuilder builder = new FilterRequestBuilder(); 181 | builder.present("test").precedence(); 182 | 183 | FilterRequest request = builder.build(); 184 | assertEquals("(test pr)", request.getFilter()); 185 | 186 | builder = new FilterRequestBuilder(); 187 | builder.present("test").precedence().precedence().precedence(); 188 | 189 | request = builder.build(); 190 | assertEquals("(test pr)", request.getFilter()); 191 | } 192 | 193 | @Test 194 | public void testBuildException() throws Exception { 195 | FilterRequestBuilder builder = new FilterRequestBuilder(); 196 | builder.present("test").precedence().build(); 197 | 198 | try { 199 | builder.present("should_fail"); 200 | fail("Should have failed because the builder was built"); 201 | } catch (IllegalStateException e) { 202 | System.out.println("Should see this exception"); 203 | e.printStackTrace(System.out); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/UaaClientOperationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | 21 | import org.cloudfoundry.identity.uaa.api.client.UaaClientOperations; 22 | import org.cloudfoundry.identity.uaa.api.common.model.UaaTokenGrantType; 23 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 24 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 25 | import org.junit.Before; 26 | import org.junit.ClassRule; 27 | import org.junit.Test; 28 | import org.springframework.security.core.authority.AuthorityUtils; 29 | import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; 30 | import org.springframework.security.oauth2.provider.client.BaseClientDetails; 31 | 32 | /** 33 | * @author Josh Ghiloni 34 | */ 35 | public class UaaClientOperationTest extends AbstractOperationTest { 36 | 37 | @ClassRule public static UaaServerAvailable uaaServerAvailable = new UaaServerAvailable(); 38 | 39 | private UaaClientOperations operations; 40 | private BaseClientDetails testClient; 41 | private BaseClientDetails testClientDetails; 42 | 43 | @Before 44 | public void setup() throws Exception { 45 | 46 | operations = getConnection().clientOperations(); 47 | 48 | try { 49 | operations.delete("test"); 50 | } catch (Exception ignore) {} 51 | 52 | testClientDetails = new BaseClientDetails(); 53 | testClientDetails.setClientId("test"); 54 | testClientDetails.setClientSecret("testsecret"); 55 | testClientDetails.setAccessTokenValiditySeconds(3600); 56 | testClientDetails.setAuthorizedGrantTypes(Arrays.asList(UaaTokenGrantType.authorization_code.toString(), 57 | UaaTokenGrantType.client_credentials.toString())); 58 | testClientDetails.setRefreshTokenValiditySeconds(86400); 59 | testClientDetails.setAuthorities(AuthorityUtils.createAuthorityList("uaa.resource", "clients.secret")); 60 | 61 | testClient = operations.create(testClientDetails); 62 | } 63 | 64 | @Test 65 | public void testGetClients() throws Exception { 66 | 67 | SearchResults clients = operations.getClients(FilterRequestBuilder.showAll()); 68 | 69 | assertEquals("Total Results wrong", 12, clients.getTotalResults()); // default 11 + test client 1 = 12 clients 70 | assertEquals("Items Per Page wrong", 12, clients.getItemsPerPage()); 71 | assertEquals("Actual result count wrong", 12, clients.getResources().size()); 72 | } 73 | 74 | @Test 75 | public void testGetClient() throws Exception { 76 | 77 | BaseClientDetails client = operations.findById("app"); 78 | 79 | assertEquals("ID wrong", "app", client.getClientId()); 80 | assertNull("Secret should not be returned", client.getClientSecret()); 81 | } 82 | 83 | @Test 84 | public void testCreateDelete() throws Exception { 85 | 86 | BaseClientDetails checkClient = operations.findById(testClient.getClientId()); 87 | assertEquals(testClient.getClientId(), checkClient.getClientId()); 88 | 89 | operations.delete(checkClient.getClientId()); 90 | } 91 | 92 | @Test 93 | public void testUpdate() throws Exception { 94 | 95 | BaseClientDetails client = operations.findById(testClient.getClientId()); 96 | 97 | client.setScope(Arrays.asList("foo")); 98 | BaseClientDetails updated = operations.update(client); 99 | 100 | assertNotEquals(testClient.getScope(), updated.getScope()); 101 | assertEquals(client.getScope().iterator().next(), updated.getScope().iterator().next()); 102 | } 103 | 104 | @Test 105 | public void testChangeClientSecretForTestUser() throws Exception { 106 | 107 | ClientCredentialsResourceDetails testUserCredentials = getDefaultClientCredentials(); 108 | testUserCredentials.setClientId(testClientDetails.getClientId()); 109 | testUserCredentials.setClientSecret(testClientDetails.getClientSecret()); 110 | testUserCredentials.setScope(new ArrayList(testClientDetails.getScope())); 111 | 112 | UaaClientOperations clientOperations = getConnection(testUserCredentials).clientOperations(); 113 | 114 | String clientSecret = testClientDetails.getClientSecret(); 115 | 116 | assertTrue("Could not change password", 117 | clientOperations.changeClientSecret(testClientDetails.getClientId(), clientSecret, "newSecret")); 118 | 119 | try { 120 | clientOperations.changeClientSecret(testClientDetails.getClientId(), clientSecret, "shouldfail"); 121 | fail("First password change failed"); 122 | } catch (Exception expected) { 123 | expected.toString(); // to avoid empty catch-block. 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/UaaGroupOperationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | import java.util.Collection; 19 | 20 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 21 | import org.cloudfoundry.identity.uaa.api.group.UaaGroupOperations; 22 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 23 | import org.cloudfoundry.identity.uaa.scim.ScimGroup; 24 | import org.cloudfoundry.identity.uaa.scim.ScimGroupMember; 25 | import org.junit.Before; 26 | import org.junit.ClassRule; 27 | import org.junit.Test; 28 | 29 | /** 30 | * @author Josh Ghiloni 31 | * 32 | */ 33 | public class UaaGroupOperationTest extends AbstractOperationTest { 34 | 35 | @ClassRule public static UaaServerAvailable uaaServerAvailable = new UaaServerAvailable(); 36 | 37 | private UaaGroupOperations operations; 38 | 39 | @Before 40 | public void setUp() throws Exception { 41 | operations = getConnection().groupOperations(); 42 | } 43 | 44 | @Test 45 | public void testGroupRetrieval() { 46 | 47 | SearchResults groups = operations.getGroups(FilterRequestBuilder.showAll()); 48 | 49 | assertNotNull(groups); 50 | 51 | assertEquals(28, groups.getTotalResults()); 52 | assertEquals(28, groups.getResources().size()); 53 | assertEquals(1, groups.getStartIndex()); 54 | assertEquals(100, groups.getItemsPerPage()); 55 | } 56 | 57 | @Test 58 | public void testGroupCreateUpdateDelete() { 59 | 60 | String id = "marissa"; 61 | 62 | ScimGroup newGroup = new ScimGroup(); 63 | newGroup.setDisplayName("test.group"); 64 | 65 | ScimGroup createdGroup = operations.createGroup(newGroup); 66 | 67 | assertNotNull(createdGroup.getId()); 68 | 69 | ScimGroup newNameGroup = operations.updateGroupName(createdGroup.getId(), "test.group.renamed"); 70 | 71 | assertEquals(createdGroup.getId(), newNameGroup.getId()); 72 | 73 | ScimGroup updatedGroup = operations.addMember(newNameGroup.getId(), id); 74 | 75 | Collection oldMembers = newNameGroup.getMembers(); 76 | Collection newMembers = updatedGroup.getMembers(); 77 | 78 | assertTrue((oldMembers == null && newMembers.size() == 1) || (oldMembers.size() == newMembers.size() - 1)); 79 | assertEquals(newNameGroup.getId(), updatedGroup.getId()); 80 | 81 | ScimGroup shrunkGroup = operations.deleteMember(updatedGroup.getId(), id); 82 | 83 | oldMembers = newNameGroup.getMembers(); 84 | newMembers = shrunkGroup.getMembers(); 85 | 86 | assertTrue((oldMembers == null && newMembers == null) || (oldMembers.size() == newMembers.size())); 87 | assertEquals(updatedGroup.getId(), shrunkGroup.getId()); 88 | 89 | operations.deleteGroup(createdGroup.getId()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/UaaServerAvailable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import java.io.IOException; 17 | import java.net.Socket; 18 | 19 | import org.junit.Assume; 20 | import org.junit.rules.TestRule; 21 | import org.junit.runner.Description; 22 | import org.junit.runners.model.Statement; 23 | 24 | /** 25 | * @author Thomas Darimont 26 | */ 27 | public class UaaServerAvailable implements TestRule { 28 | 29 | private static final boolean UAA_RUNNING; 30 | 31 | private static final String UAA_TEST_HOST = System.getProperty("test.uaa.server.hostname", "localhost"); 32 | private static final int UAA_TEST_PORT = Integer.getInteger("test.uaa.server.port", 8080); 33 | 34 | static { 35 | 36 | boolean serverAvailable = false; 37 | try { 38 | Socket test = new Socket(UAA_TEST_HOST, UAA_TEST_PORT); 39 | serverAvailable = true; 40 | test.close(); 41 | } catch (IOException e) { 42 | System.err.println("UAA is not running, skip these tests"); 43 | } 44 | 45 | UAA_RUNNING = serverAvailable; 46 | } 47 | 48 | private static final Statement NOT_AVAILABLE_STATEMEMT = new Statement() { 49 | @Override 50 | public void evaluate() throws Throwable { 51 | Assume.assumeTrue( 52 | String.format("Skipping test due to UAA not being available @: %s:%s", UAA_TEST_HOST, UAA_TEST_PORT), false); 53 | } 54 | }; 55 | 56 | @Override 57 | public Statement apply(final Statement base, Description description) { 58 | return UAA_RUNNING ? new Statement() { 59 | 60 | @Override 61 | public void evaluate() throws Throwable { 62 | base.evaluate(); 63 | } 64 | } : NOT_AVAILABLE_STATEMEMT; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/org/cloudfoundry/identity/uaa/api/client/test/UaaUserOperationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package org.cloudfoundry.identity.uaa.api.client.test; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | import java.util.Collections; 19 | 20 | import org.cloudfoundry.identity.uaa.api.common.model.expr.FilterRequestBuilder; 21 | import org.cloudfoundry.identity.uaa.api.user.UaaUserOperations; 22 | import org.cloudfoundry.identity.uaa.rest.SearchResults; 23 | import org.cloudfoundry.identity.uaa.scim.ScimUser; 24 | import org.cloudfoundry.identity.uaa.scim.ScimUser.Email; 25 | import org.cloudfoundry.identity.uaa.scim.ScimUser.Name; 26 | import org.cloudfoundry.identity.uaa.scim.ScimUser.PhoneNumber; 27 | import org.junit.Before; 28 | import org.junit.ClassRule; 29 | import org.junit.Rule; 30 | import org.junit.Test; 31 | import org.junit.rules.ExpectedException; 32 | 33 | /** 34 | * @author Josh Ghiloni 35 | */ 36 | public class UaaUserOperationTest extends AbstractOperationTest { 37 | 38 | @ClassRule public static UaaServerAvailable uaaServerAvailable = new UaaServerAvailable(); 39 | @Rule public ExpectedException expectedException = ExpectedException.none(); 40 | 41 | private UaaUserOperations operations; 42 | 43 | @Before 44 | public void setUp() throws Exception { 45 | 46 | operations = getConnection().userOperations(); 47 | 48 | ScimUser testUser = operations.getUserByName("testuser"); 49 | if (testUser != null) { 50 | operations.deleteUser(testUser.getId()); 51 | } 52 | } 53 | 54 | @Test 55 | public void testUserRetrieval() { 56 | 57 | SearchResults users = operations.getUsers(FilterRequestBuilder.showAll()); 58 | 59 | assertNotNull(users); 60 | 61 | assertEquals(1, users.getTotalResults()); 62 | assertEquals(1, users.getResources().size()); 63 | assertEquals(1, users.getStartIndex()); 64 | assertEquals(100, users.getItemsPerPage()); 65 | } 66 | 67 | @Test 68 | public void testUserCreateUpdateDelete() { 69 | 70 | ScimUser newUser = createNewTestUser(); 71 | 72 | ScimUser createdUser = operations.createUser(newUser); 73 | assertNotNull(createdUser.getId()); 74 | 75 | PhoneNumber updatedPhone = new PhoneNumber(); 76 | updatedPhone.setValue("212-867-5309"); 77 | createdUser.setPhoneNumbers(Collections.singletonList(updatedPhone)); 78 | ScimUser updatedUser = operations.updateUser(createdUser); 79 | 80 | assertEquals(createdUser.getId(), updatedUser.getId()); 81 | } 82 | 83 | @Test 84 | public void testUserPasswordChange() { 85 | 86 | ScimUser newUser = createNewTestUser(); 87 | ScimUser createdUser = operations.createUser(newUser); 88 | 89 | operations.changeUserPassword(createdUser.getId(), newUser.getPassword(), "newk0ala"); 90 | } 91 | 92 | private ScimUser createNewTestUser() { 93 | 94 | ScimUser newUser = new ScimUser(); 95 | newUser.setUserName("testuser"); 96 | newUser.setPassword("p4ssw0rd"); 97 | 98 | newUser.setName(new Name("Test", "User")); 99 | 100 | Email email = new Email(); 101 | email.setValue("testuser@test.com"); 102 | 103 | newUser.setEmails(Collections.singletonList(email)); 104 | 105 | PhoneNumber phone = new PhoneNumber(); 106 | phone.setValue("303-555-1212"); 107 | newUser.setPhoneNumbers(Collections.singletonList(phone)); 108 | 109 | return newUser; 110 | } 111 | 112 | } 113 | --------------------------------------------------------------------------------