Compare commits
10 Commits
main
...
itest-supp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ded978132d | ||
|
|
8a66816d42 | ||
|
|
dd4bdc4b1b | ||
|
|
948a5e1e18 | ||
|
|
75520d778b | ||
|
|
01c3ebdecb | ||
|
|
8fedbb0d97 | ||
|
|
5566fd332c | ||
|
|
d527cd25b1 | ||
|
|
2445eeeea8 |
104
Jenkinsfile
vendored
104
Jenkinsfile
vendored
@@ -1,104 +0,0 @@
|
||||
def projectProperties = [
|
||||
[$class: 'BuildDiscarderProperty',
|
||||
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
|
||||
pipelineTriggers([cron('@daily')])
|
||||
]
|
||||
properties(projectProperties)
|
||||
|
||||
def SUCCESS = hudson.model.Result.SUCCESS.toString()
|
||||
currentBuild.result = SUCCESS
|
||||
|
||||
def GRADLE_ENTERPRISE_CACHE_USER = usernamePassword(credentialsId: 'gradle_enterprise_cache_user',
|
||||
passwordVariable: 'GRADLE_ENTERPRISE_CACHE_PASSWORD',
|
||||
usernameVariable: 'GRADLE_ENTERPRISE_CACHE_USERNAME')
|
||||
def GRADLE_ENTERPRISE_SECRET_ACCESS_KEY = string(credentialsId: 'gradle_enterprise_secret_access_key',
|
||||
variable: 'GRADLE_ENTERPRISE_ACCESS_KEY')
|
||||
def SPRING_SIGNING_SECRING = file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')
|
||||
def SPRING_GPG_PASSPHRASE = string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')
|
||||
def OSSRH_CREDENTIALS = usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')
|
||||
def ARTIFACTORY_CREDENTIALS = usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')
|
||||
def JENKINS_PRIVATE_SSH_KEY = file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')
|
||||
def SONAR_LOGIN_CREDENTIALS = string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')
|
||||
|
||||
def jdkEnv(String jdk = 'jdk8') {
|
||||
def jdkTool = tool(jdk)
|
||||
return "JAVA_HOME=${ jdkTool }"
|
||||
}
|
||||
|
||||
try {
|
||||
parallel check: {
|
||||
stage('Check') {
|
||||
node {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withCredentials([ARTIFACTORY_CREDENTIALS,
|
||||
GRADLE_ENTERPRISE_CACHE_USER,
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY]) {
|
||||
withEnv([jdkEnv(),
|
||||
"GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USERNAME}",
|
||||
"GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PASSWORD}",
|
||||
"GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY}"]) {
|
||||
sh "./gradlew check -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --stacktrace"
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: check'
|
||||
throw e
|
||||
} finally {
|
||||
junit '**/build/test-results/*/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentBuild.result == 'SUCCESS') {
|
||||
parallel artifacts: {
|
||||
stage('Deploy Artifacts') {
|
||||
node {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
withCredentials([SPRING_SIGNING_SECRING,
|
||||
SPRING_GPG_PASSPHRASE,
|
||||
OSSRH_CREDENTIALS,
|
||||
ARTIFACTORY_CREDENTIALS,
|
||||
GRADLE_ENTERPRISE_CACHE_USER,
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY]) {
|
||||
withEnv([jdkEnv(),
|
||||
"GRADLE_ENTERPRISE_CACHE_USERNAME=${GRADLE_ENTERPRISE_CACHE_USERNAME}",
|
||||
"GRADLE_ENTERPRISE_CACHE_PASSWORD=${GRADLE_ENTERPRISE_CACHE_PASSWORD}",
|
||||
"GRADLE_ENTERPRISE_ACCESS_KEY=${GRADLE_ENTERPRISE_ACCESS_KEY}"]) {
|
||||
sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --stacktrace"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: deploys'
|
||||
throw e
|
||||
} finally {
|
||||
def buildStatus = currentBuild.result
|
||||
def buildNotSuccess = !SUCCESS.equals(buildStatus)
|
||||
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
|
||||
|
||||
if(buildNotSuccess || lastBuildNotSuccess) {
|
||||
|
||||
stage('Notifiy') {
|
||||
node {
|
||||
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
|
||||
|
||||
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
|
||||
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
|
||||
|
||||
emailext (
|
||||
subject: subject,
|
||||
body: details,
|
||||
recipientProviders: RECIPIENTS,
|
||||
to: "$SPRING_SECURITY_TEAM_EMAILS"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample;
|
||||
package sample.authorizationserver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
package sample.authorizationserver.config;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
package sample.authorizationserver.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@@ -11,4 +11,7 @@ dependencies {
|
||||
compile 'org.webjars:webjars-locator-core'
|
||||
compile 'org.webjars:bootstrap:3.4.1'
|
||||
compile 'org.webjars:jquery:3.4.1'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation project(':spring-security-samples-boot-oauth2-integrated-authorizationserver')
|
||||
testImplementation project(':spring-security-samples-boot-oauth2-integrated-resourceserver')
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample;
|
||||
package sample.client;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
package sample.client.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
package sample.client.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.web;
|
||||
package sample.client.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.web;
|
||||
package sample.client.web;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -41,7 +41,8 @@ spring:
|
||||
client-name: messaging-client-client-credentials
|
||||
provider:
|
||||
spring:
|
||||
issuer-uri: http://auth-server:9000
|
||||
authorization-uri: http://auth-server:9000/oauth2/authorize
|
||||
token-uri: http://auth-server:9000/oauth2/token
|
||||
|
||||
messages:
|
||||
base-uri: http://localhost:8090/messages
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package sample.client;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import sample.authorizationserver.OAuth2AuthorizationServerApplication;
|
||||
import sample.resourceserver.OAuth2ResourceServerApplication;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
||||
// Notes:
|
||||
//
|
||||
// Use-case:
|
||||
// - A user that develops a bunch of services
|
||||
// - One of the apps is a “frontend” app that acts as the OAuth 2 client
|
||||
// - The other apps are OAuth 2 Resource Servers providing (I'm just doing one service for the sake of the example)
|
||||
// - They rely on a single OAuth 2 provider, a OAuth 2.0-compliant authz server (or at least Spring-supported)
|
||||
// - They want to make an integration/e2e test of the components they develop (client + resource servers)
|
||||
// - Probably using SpringBootTest
|
||||
//
|
||||
// Some early conclusions:
|
||||
// - Users would need a _nice and easy_ way of starting an authz server, preferably on a dynamic port
|
||||
// - At the very least on a configurable port
|
||||
// - Thinking maybe an annotation, e.g. @EnableTestSpringAuthorizationServer
|
||||
// - They'd need some sort of autoconfiguration of their client application so that it points at the correct (test) auth provider
|
||||
// - <explore> is there a way that @SpringBootTest, coupled with @EnableTestSpringAuthorizationServer, auto-configures the client app ?
|
||||
// - <parking lot> what do we do when users have multiple auth providers ?
|
||||
// - I assume they'd be starting their authorization server through SpringApplicationBuilder but that's very rough
|
||||
// - They'd also need to point their resource servers at the correct validation mechanism for JWT (jwks uri, OIDC provider configuration uri, OAuth 2.0 server metadata)
|
||||
// - For now we inject properties through SpringApplicationBuilder
|
||||
// - They could turn off jwt validation in their resource servers, but I don't think it makes sense for integration testing
|
||||
// - <explore> Maybe you could autowire some sort of factory for configuring you resource server, linked to the TestSpringAuthorizationServer ?
|
||||
// - <parking lot> think about public-key jwt validation
|
||||
//
|
||||
// Misc notes on the changes on this branch:
|
||||
// - This was very much quick-and-dirty, just to make it work
|
||||
// - e.g., I used 3 spring boot applications, but the authz-server could probably be trimmed down to two config files
|
||||
// - there's no specific isolation of any kind between the apps; they run in the same JVM, in different threads, but with the same classloader
|
||||
// - I moved packages around to avoid @SpringBootApplication package scanning conflicts
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = OAuth2ClientApplication.class)
|
||||
@ContextConfiguration(initializers= OAuth2ClientApplicationTest.OAuth2ServersProperties.class)
|
||||
@AutoConfigureMockMvc
|
||||
public class OAuth2ClientApplicationTest {
|
||||
private static ConfigurableApplicationContext authorizationServer;
|
||||
private static ConfigurableApplicationContext resourceServer;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
// embedded-server-test: RUN
|
||||
// Run on dynamic port and get that port for the user to use
|
||||
authorizationServer = new SpringApplicationBuilder(OAuth2AuthorizationServerApplication.class)
|
||||
.properties("spring.config.name:embeddedauthorizationserver") // Required to avoid clashes in MBean registration
|
||||
.properties("server.port:0")
|
||||
.application()
|
||||
.run();
|
||||
String authzServerPort = authorizationServer.getEnvironment().getProperty("local.server.port");
|
||||
// END embedded-server-test: RUN
|
||||
|
||||
// Here the user would configure _their_ embedded resource server themselves
|
||||
// PROVIDED THAT they use the JWK Set URI to validate jwts
|
||||
// TODO: think about the public key case
|
||||
// TODO: could we easily provider a TestJwtDecoder that skips validation? SHOULD we ?
|
||||
resourceServer = new SpringApplicationBuilder(OAuth2ResourceServerApplication.class)
|
||||
.properties("spring.config.name:embeddedresourceserver")
|
||||
.properties("spring.security.oauth2.resourceserver.jwt.jwk-set-uri:http://localhost:" + authzServerPort + "/oauth2/jwks")
|
||||
.properties("server.port:0")
|
||||
.application()
|
||||
.run();
|
||||
String resourceServerPort = resourceServer.getEnvironment().getProperty("local.server.port");
|
||||
|
||||
// embedded-server-test: CONFIGURE CLIENT
|
||||
// Nasty: override some property configurations for the client through static variables
|
||||
// TODO: find a better way to interact with OAuth2ClientProperties ; maybe auto-configure it ?
|
||||
OAuth2ServersProperties.AUTH_SERVER_URL = "http://localhost:" + authzServerPort;
|
||||
// END embedded-server-test: CONFIGURE CLIENT
|
||||
|
||||
// Here would be the user configuring their app to point to _their_ resource server
|
||||
// That's not great, but this allows for dynamic port on the resource server without touching at the client app code
|
||||
OAuth2ServersProperties.RESOURCE_SERVER_URL = "http://localhost:" + resourceServerPort;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
authorizationServer.stop();
|
||||
resourceServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void index() throws Exception {
|
||||
mockMvc
|
||||
.perform(get("/index"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("Client Credentials")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authorize() throws Exception {
|
||||
mockMvc
|
||||
.perform(get("/authorize?grant_type=client_credentials"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("Message 1")));
|
||||
}
|
||||
|
||||
static class OAuth2ServersProperties implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
// Helper class to point the clients at the right authz/resource servers
|
||||
static String AUTH_SERVER_URL;
|
||||
static String RESOURCE_SERVER_URL;
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext,
|
||||
"spring.security.oauth2.client.provider.spring.authorization-uri=" + AUTH_SERVER_URL + "/oauth2/authorize",
|
||||
"spring.security.oauth2.client.provider.spring.token-uri=" + AUTH_SERVER_URL + "/oauth2/token",
|
||||
"messages.base-uri=" + RESOURCE_SERVER_URL + "/messages"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample;
|
||||
package sample.resourceserver;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.config;
|
||||
package sample.resourceserver.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.web;
|
||||
package sample.resourceserver.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
Reference in New Issue
Block a user