Skip to content

Commit

Permalink
getAuthorities should only return app scopes SAP#34
Browse files Browse the repository at this point in the history
  • Loading branch information
nenaraab committed Jan 15, 2019
1 parent 79ff15a commit 76319cf
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 130 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package testservice.api.v1;

import static org.hamcrest.CoreMatchers.*;
Expand Down Expand Up @@ -72,22 +57,17 @@ public String message(@AuthenticationPrincipal Token token) {

// service instance id
Assert.assertEquals("abcd1234", token.getCloneServiceInstanceId());
// // groups
// Assert.assertEquals(1, token.getSystemAttribute("xs.saml.groups").length);
// Assert.assertEquals("g1", token.getSystemAttribute("xs.saml.groups")[0]);
// role collections
// Assert.assertEquals(1, token.getSystemAttribute("xs.rolecollections").length);
// Assert.assertEquals("rc1", token.getSystemAttribute("xs.rolecollections")[0]);

return "user:" + token.getLogonName();
}

@GetMapping("/scope")
public void checkScope(@AuthenticationPrincipal Token token) {
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) token.getAuthorities();
assertThat(authorities.size(), is(4));
assertThat(authorities, hasItem(new SimpleGrantedAuthority("openid")));
assertThat(authorities, hasItem(new SimpleGrantedAuthority("java-hello-world.Display")));
assertThat(authorities, not(hasItem(new SimpleGrantedAuthority("java-hello-world.Other"))));
assertThat(authorities.size(), is(3));
assertThat(authorities, not(hasItem(new SimpleGrantedAuthority("openid"))));
assertThat(authorities, hasItem(new SimpleGrantedAuthority("Display")));
assertThat(authorities, not(hasItem(new SimpleGrantedAuthority("Other"))));
}

@GetMapping("/requesttoken")
Expand Down
124 changes: 70 additions & 54 deletions spring-xsuaa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,97 @@

## Integrate in a OAuth resource server

This library enhances the spring-security project. As of version 5 of spring-security, this includes the OAuth resource-server functionality. A Spring boot application needs a security configuration class that enables the resource server and configures authentication using JWT tokens.
This library enhances the [spring-security](https://github.com/spring-projects/spring-security/) project. As of version 5 of spring-security, this includes the OAuth resource-server functionality. A Spring boot application needs a security configuration class that enables the resource server and configures authentication using JWT tokens.

## Usage
## Setup
Set the property source for xsuaa service binding on the application:

```
```java
@SpringBootApplication
@ComponentScan(basePackageClasses=XsuaaServiceConfigurationDefault.class)
@PropertySource(factory = XsuaaServicePropertySourceFactory.class, value = { "" })
public class Application
@ComponentScan
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Bean
XsuaaServiceConfigurationDefault xsuaaDefaultConfig() {
return new XsuaaServiceConfigurationDefault();
}
}
```

Configure the OAuth resource server

```
```java
@Configuration
@EnableWebSecurity
@PropertySource(factory = XsuaaServicePropertySourceFactory.class, value = {""})
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
XsuaaServiceConfigurationDefault xsuaaServiceConfiguration;

@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.antMatchers("/hello-token/**").hasAuthority("Read") // checks whether it has scope "<xsappId>.Read"
.antMatchers("/actuator/**").authenticated()
.anyRequest().denyAll()
.and()
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder())
.jwtAuthenticationConverter(jwtAuthenticationConverter());
// @formatter:on
}

@Bean
JwtDecoder jwtDecoder() {
return new XsuaaJwtDecoderBuilder(xsuaaServiceConfiguration).build();
}

@Bean
Converter<Jwt, AbstractAuthenticationToken> jwtAuthenticationConverter() {
return new TokenAuthenticationConverter(xsuaaServiceConfiguration);
}
}
```

@Autowired
XsuaaServiceConfigurationDefault xsuaaServiceConfiguration;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests().
antMatchers("/hello-token/**").hasAuthority("openid")
.anyRequest().authenticated().
and()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(new TokenAuthenticationConverter(xsuaaServiceConfiguration));
// @formatter:on
}
> Note: with `XsuaaServicePropertySourceFactory` the VCAP_SERVICES properties are read from the system environment variable and mapped to properties such as `xsuaa.xsappname`.
> You can access them via Spring `@Value` annotation e.g. `@Value("${xsuaa.xsappname:}") String appId`.
> For testing purposes you can overwrite them, for example, as part of a *.properties file.
## Usage

@Bean
JwtDecoder jwtDecoder() {
return new XsuaaJwtDecoderBuilder(xsuaaServiceConfiguration).build();
}
### Check authorization on method level

```java
@GetMapping("/hello-token")
@PreAuthorize("hasAuthority('Display')
public Map<String, String> message() {
...
}
```
### Access user/token information
In the Java coding, use the `Token` to extract user information:
```java
@GetMapping("/hello-token")
public Map<String, String> message(@AuthenticationPrincipal Token token) {
token.getGivenName();
}
```
@GetMapping("/hello-token")
public Map<String, String> message(@AuthenticationPrincipal Token token) {
```

## Inject VCAP-Service Properties

Reading xsuaa variables from VCAP SERVICES and injecting into fields.

### Usage

Or alternatively:
```java
@Configuration
@PropertySource(factory = XsuaaServicePropertySourceFactory.class, value = { "" })
public class XsuaaConfiguration {

public XsuaaConfiguration() {
}

@Value("${xsuaa.clientid:}")
private String clientId;

@Value("${xsuaa.clientsecret:}")
private String clientSecret;

@Value("${xsuaa.url:}")
private String uaaUrl;

@Value("${xsuaa.uaadomain:}")
private String uaadomain;

public Map<String, String> message() {
Token token = SecurityContext.getToken();
token.getGivenName();
}
```
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
/**
* Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
* This file is licensed under the Apache Software License,
* v. 2 except as noted otherwise in the LICENSE file
* https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE
*/
package com.sap.cloud.security.xsuaa.token;

import com.sap.xsa.security.container.XSTokenRequest;
import java.net.URISyntaxException;
import java.util.Collection;

import org.springframework.lang.Nullable;
import org.springframework.security.core.userdetails.UserDetails;

import java.net.URISyntaxException;
import com.sap.xsa.security.container.XSTokenRequest;

public interface Token extends UserDetails {
String CLAIM_XS_USER_ATTRIBUTES = "xs.user.attributes";
Expand Down Expand Up @@ -119,8 +115,8 @@ public interface Token extends UserDetails {

/**
* Get the encoded authentication token, e.g. for token forwarding to another app.
*
* Never expose this token via log or via HTTP.
*
* Never expose this token via log or via HTTP.
*
* @return token
*/
Expand All @@ -132,8 +128,15 @@ public interface Token extends UserDetails {
* @param tokenRequest
* request data
* @throws URISyntaxException
* in case of wron URLs
* in case of wron URLs
* @return requested token
*/
String requestToken(XSTokenRequest tokenRequest) throws URISyntaxException;

/**
* Returns list of scopes with appId prefix, e.g. "<my-xsapp!123>.Display"
*
* @return all scopes
*/
Collection<String> getScopes();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/**
* Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
* This file is licensed under the Apache Software License,
* v. 2 except as noted otherwise in the LICENSE file
* https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE
*/
package com.sap.cloud.security.xsuaa.token;

import java.util.Collection;
Expand Down Expand Up @@ -44,7 +38,7 @@ protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
Collection<String> customAuthorities = getCustomAuthorities(new TokenImpl(jwt, appId));

Stream<String> authorities = Stream.of(scopeAuthorities, customAuthorities).flatMap(Collection::stream);
return authorities.map(authority -> authority).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
return authorities.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}

protected Collection<String> getCustomAuthorities(Token token) {
Expand All @@ -53,6 +47,9 @@ protected Collection<String> getCustomAuthorities(Token token) {

protected Collection<String> getScopes(Jwt jwt) {
List<String> scopesList = jwt.getClaimAsStringList(Token.CLAIM_SCOPES);
return scopesList != null ? scopesList : Collections.emptyList();
if (scopesList != null) {
return scopesList.stream().filter(scope -> scope.startsWith(appId + ".")).map(scope -> scope.replace(appId + ".", "")).collect(Collectors.toList());
}
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
/**
* Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
* This file is licensed under the Apache Software License,
* v. 2 except as noted otherwise in the LICENSE file
* https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE
*/
package com.sap.cloud.security.xsuaa.token;

import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.sap.xs2.security.container.XSTokenRequestImpl;
Expand Down Expand Up @@ -50,23 +46,23 @@ public class TokenImpl implements Token {
static final String CLAIM_EXTERNAL_CONTEXT = "ext_ctx";

private final Log logger = LogFactory.getLog(getClass());
private String xsappname = null;
private String appId = null;
private Jwt jwt;

/**
* @param jwt
* token
* @param xsappname
* @param appId
* app name
*/
protected TokenImpl(Jwt jwt, String xsappname) {
this.xsappname = xsappname;
protected TokenImpl(Jwt jwt, String appId) {
this.appId = appId;
this.jwt = jwt;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
TokenAuthenticationConverter converter = new TokenAuthenticationConverter(xsappname);
TokenAuthenticationConverter converter = new TokenAuthenticationConverter(appId);
return converter.extractAuthorities(jwt);
}

Expand Down Expand Up @@ -257,6 +253,12 @@ public String requestToken(XSTokenRequest tokenRequest) throws URISyntaxExceptio
}
}

@Override
public Collection<String> getScopes() {
List<String> scopesList = jwt.getClaimAsStringList(Token.CLAIM_SCOPES);
return scopesList != null ? scopesList : Collections.emptyList();
}

/**
* Check if the authentication token contains a claim, e.g. "email".
* @param claim
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
/**
* Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
* This file is licensed under the Apache Software License,
* v. 2 except as noted otherwise in the LICENSE file
* https://github.com/SAP/cloud-security-xsuaa-integration/blob/master/LICENSE
*/
package com.sap.cloud.security.xsuaa.token;

import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;

import java.util.Collection;
Expand All @@ -27,6 +20,7 @@ public class TokenAuthenticationConverterTest {
private TokenAuthenticationConverter tokenConverter;
String scopeAdmin = xsAppName + "." + "Admin";
String scopeRead = xsAppName + "." + "Read";
String scopeOther = "other-app!234" + "." + "Other";

@Before
public void setup() throws Exception {
Expand All @@ -41,14 +35,23 @@ public void extractAuthoritiesWithoutScopes() throws Exception {
assertThat(authenticationToken.getAuthorities().size(), is(0));
}

@Test
public void extractAuthoritiesIgnoresForeignScopes() throws Exception {
Jwt jwt = new JwtGenerator().addScopes(scopeAdmin, scopeOther, scopeRead).getToken();

AbstractAuthenticationToken authenticationToken = tokenConverter.convert(jwt);
assertThat(authenticationToken.getAuthorities().size(), is(2));
assertThat(authenticationToken.getAuthorities(), not(hasItem(new SimpleGrantedAuthority("Other"))));
}

@Test
public void extractAuthoritiesWithScopes() throws Exception {
Jwt jwt = new JwtGenerator().addScopes(scopeAdmin, scopeRead).getToken();

AbstractAuthenticationToken authenticationToken = tokenConverter.convert(jwt);
assertThat(authenticationToken.getAuthorities().size(), is(2));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeRead)));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeAdmin)));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("Read")));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("Admin")));
}

@Test
Expand All @@ -62,7 +65,7 @@ public void extractCustomAuthoritiesWithScopes() throws Exception {
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("ATTR:COST-CENTER=0815")));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("ATTR:COUNTRY=DE")));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("ATTR:COUNTRY=IL")));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority(scopeAdmin)));
assertThat(authenticationToken.getAuthorities(), hasItem(new SimpleGrantedAuthority("Admin")));
}

private static class MyTokenAuthenticationConverter extends TokenAuthenticationConverter {
Expand Down
Loading

0 comments on commit 76319cf

Please sign in to comment.