attributes = new ArrayList<>();
62 | attributes.add(ATTRIBUTE_GROUP);
63 | attributes.add(ATTRIBUTE_BULLETINBOARD);
64 | attributes.add(ATTRIBUTE_LOCATION);
65 | return attributes;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/spring-security-acl/src/main/java/com/sap/cp/appsec/dto/AdvertisementDto.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.dto;
2 |
3 | import com.sap.cp.appsec.domain.Advertisement;
4 |
5 | import javax.validation.constraints.NotBlank;
6 | import javax.validation.constraints.NotNull;
7 | import java.sql.Timestamp;
8 | import java.time.ZoneId;
9 | import java.time.ZonedDateTime;
10 | import java.time.format.DateTimeFormatter;
11 |
12 |
13 | /**
14 | * A Data Transfer Object (DTO) is a data structure without logic.
15 | *
16 | * Note: This class implements also the mapping between DTO and Entity and vice versa
17 | */
18 | //@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
19 | public class AdvertisementDto {
20 | /**
21 | * id is null in case of a new advertisement
22 | **/
23 | private Long id;
24 |
25 | @NotBlank
26 | @NotNull
27 | public String title;
28 |
29 | @NotBlank
30 | @NotNull
31 | public String contact;
32 |
33 | public boolean isPublished;
34 |
35 | public MetaData metadata = new MetaData();
36 |
37 | /**
38 | * Default constructor required by Jackson JSON Converter
39 | */
40 | public AdvertisementDto() {
41 | }
42 |
43 | /**
44 | * Transforms Advertisement entity to DTO
45 | */
46 | public AdvertisementDto(Advertisement ad) {
47 | this.id = ad.getId();
48 | this.title = ad.getTitle();
49 | this.contact = ad.getContact();
50 |
51 | this.metadata.createdAt = convertToDateTime(ad.getCreatedAt());
52 | this.metadata.modifiedAt = convertToDateTime(ad.getModifiedAt());
53 | this.metadata.createdBy = "" + ad.getCreatedBy();
54 | this.metadata.modifiedBy = "" + ad.getModifiedBy();
55 | this.metadata.version = ad.getVersion();
56 | this.isPublished = ad.isPublished();
57 | }
58 |
59 | public void setId(long id) {
60 | this.id = id;
61 | }
62 |
63 | public Long getId() {
64 | return id;
65 | }
66 |
67 | public Advertisement toEntity() {
68 | // does not map "read-only" attributes
69 | Advertisement ad = new Advertisement(title, contact);
70 | ad.setId(id);
71 | ad.setVersion(metadata.version);
72 | return ad;
73 | }
74 |
75 | private String convertToDateTime(Timestamp timestamp) {
76 | if (timestamp == null) {
77 | return null;
78 | }
79 | ZonedDateTime dateTime = ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault());
80 | return dateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // ISO 8601
81 | }
82 |
83 | public static class MetaData {
84 | public String createdAt;
85 | public String modifiedAt;
86 | public String createdBy;
87 | public String modifiedBy;
88 |
89 | public long version = 0L;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/spring-security-basis/src/main/java/com/sap/cp/appsec/dto/AdvertisementDto.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.dto;
2 |
3 | import com.sap.cp.appsec.domain.Advertisement;
4 | import com.sap.cp.appsec.domain.ConfidentialityLevel;
5 |
6 | import javax.validation.constraints.NotBlank;
7 | import javax.validation.constraints.NotNull;
8 | import java.math.BigDecimal;
9 | import java.sql.Timestamp;
10 | import java.time.ZoneId;
11 | import java.time.ZonedDateTime;
12 | import java.time.format.DateTimeFormatter;
13 |
14 |
15 | /**
16 | * A Data Transfer Object (DTO) is a data structure without logic.
17 | *
18 | * Note: This class implements also the mapping between DTO and Entity and vice versa
19 | */
20 | public class AdvertisementDto {
21 | /**
22 | * id is null in case of a new advertisement
23 | **/
24 | private Long id;
25 |
26 | @NotBlank
27 | public String title;
28 |
29 | @NotNull
30 | public BigDecimal price;
31 |
32 | @NotBlank
33 | public String contact;
34 |
35 | @NotBlank
36 | public String currency;
37 |
38 | public final MetaData metadata = new MetaData();
39 |
40 | public ConfidentialityLevel confidentialityLevel;
41 |
42 | /**
43 | * Default constructor required by Jackson JSON Converter
44 | */
45 | public AdvertisementDto() {
46 | }
47 |
48 | /**
49 | * Transforms Advertisement entity to DTO
50 | */
51 | public AdvertisementDto(Advertisement ad) {
52 | this.id = ad.getId();
53 | this.title = ad.getTitle();
54 | this.contact = ad.getContact();
55 | if (ad.getConfidentialityLevel() != null) {
56 | this.confidentialityLevel = ad.getConfidentialityLevel();
57 | }
58 | this.metadata.createdAt = convertToDateTime(ad.getCreatedAt());
59 | this.metadata.modifiedAt = convertToDateTime(ad.getModifiedAt());
60 | this.metadata.createdBy = "" + ad.getCreatedBy();
61 | this.metadata.modifiedBy = "" + ad.getModifiedBy();
62 | this.metadata.version = ad.getVersion();
63 | }
64 |
65 | // use only in tests
66 | public void setId(long id) {
67 | this.id = id;
68 | }
69 |
70 | public Long getId() {
71 | return id;
72 | }
73 |
74 | public Advertisement toEntity() {
75 | // does not map "read-only" attributes
76 | Advertisement ad = new Advertisement(title, contact, confidentialityLevel);
77 | ad.setId(id);
78 | ad.setVersion(metadata.version);
79 | return ad;
80 | }
81 |
82 | private String convertToDateTime(Timestamp timestamp) {
83 | if (timestamp == null) {
84 | return null;
85 | }
86 | ZonedDateTime dateTime = ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault());
87 | return dateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); // ISO 8601
88 | }
89 |
90 | public static class MetaData {
91 | public String createdAt;
92 | public String modifiedAt;
93 | public String createdBy;
94 | public String modifiedBy;
95 |
96 | public long version = 0L;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/spring-security-basis/src/main/java/com/sap/cp/appsec/config/WebSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.config;
2 |
3 | import static org.springframework.http.HttpMethod.GET;
4 | import static org.springframework.http.HttpMethod.POST;
5 | import static org.springframework.http.HttpMethod.PUT;
6 |
7 | import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration;
8 | import com.sap.cloud.security.xsuaa.token.TokenAuthenticationConverter;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.core.convert.converter.Converter;
12 | import org.springframework.security.authentication.AbstractAuthenticationToken;
13 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
15 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
16 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
17 | import org.springframework.security.config.http.SessionCreationPolicy;
18 | import org.springframework.security.oauth2.jwt.Jwt;
19 |
20 | @Configuration
21 | @EnableWebSecurity
22 | @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
23 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
24 |
25 | @Autowired
26 | XsuaaServiceConfiguration xsuaaServiceConfiguration;
27 |
28 | // configure Spring Security, demand authentication and specific scopes
29 | @Override
30 | public void configure(HttpSecurity http) throws Exception {
31 |
32 | http
33 | .sessionManagement()
34 | // session is created by approuter
35 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
36 | .and()
37 | // demand specific scopes depending on intended request
38 | .authorizeRequests()
39 | // enable OAuth2 checks
40 | .antMatchers("/**/*.js", "/**/*.json", "/**/*.xml", "/**/*.html").permitAll()
41 | .antMatchers(GET, "/api/v1/ads/**").hasAuthority("Display")
42 | .antMatchers(POST, "/api/v1/ads/**").hasAuthority("Update")
43 | .antMatchers(PUT, "/api/v1/ads/**").hasAuthority("Update")
44 | .antMatchers(GET, "/api/v1/attribute/**").permitAll()
45 | .antMatchers("/api/v1/**").authenticated()
46 | .antMatchers("/").authenticated()
47 | .antMatchers("/actuator/**").permitAll()
48 | .antMatchers("/hystrix.stream").permitAll()
49 | .anyRequest().denyAll() // deny anything not configured above
50 | .and()
51 | .oauth2ResourceServer().jwt()
52 | .jwtAuthenticationConverter(getJwtAuthoritiesConverter());
53 | }
54 |
55 | /**
56 | * Customizes how GrantedAuthority are derived from a Jwt
57 | *
58 | * @returns jwt converter
59 | */
60 | Converter getJwtAuthoritiesConverter() {
61 | TokenAuthenticationConverter converter = new TokenAuthenticationConverter(xsuaaServiceConfiguration);
62 | converter.setLocalScopeAsAuthorities(true);
63 | return converter;
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/spring-security-basis/documentation/testing/spring-security-local.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "3b5fabae-c6ce-929a-2a19-15340b38bc66",
3 | "name": "spring-sec-basis-local",
4 | "values": [
5 | {
6 | "enabled": true,
7 | "key": "AUTH_advertiser",
8 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInVzZXJfbmFtZSI6Im93bmVyIiwib3JpZ2luIjoidXNlcklkcCIsInNjb3BlIjpbImJ1bGxldGluYm9hcmQhdDQwMC5EaXNwbGF5IiwiYnVsbGV0aW5ib2FyZCF0NDAwLlVwZGF0ZSJdLCJleHAiOjY5NzQwMzE2MDAsImlhdCI6MTU2MjMzNjUwOSwiZW1haWwiOiJvd25lckB0ZXN0Lm9yZyIsImNpZCI6InNiLXhzYXBwbGljYXRpb24hdDg5NSJ9.MF92W9CdPmQYL8Yz2Za4mJWbSDoFR0NdgZUwo5BLXMPmqyJkG8efcXiAcbR69_UDWMaljPQYwadQsgAV0U1jFG04BQwkctZAlYNCmb8-JmgpRNiXWrHs44CSLhpEIwmSvAWnDlLIO-Ricc50zIIr80ae2-5lKoGqaMYVoozLpzfE0_czmcHv6WLX6vIHInGxN7m8GFMB2RmvUJZiJEW5nlspbWahyWNHK-I9dlIePQE2GoDwMedRgwZ1Gub7zbX_4WX93_kg1BKDvBPnEcVCCbVweTMcBqN5YgrC-y9IRI2rxucYqBcCHwdM8kTTrGa9NZeNIa81SQnLwcQ0mSm0lQ",
9 | "type": "text"
10 | },
11 | {
12 | "enabled": true,
13 | "key": "AUTH_viewer_public",
14 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJjb25maWRlbnRpYWxpdHlfbGV2ZWwiOlsiUFVCTElDIl19LCJ1c2VyX25hbWUiOiJ2aWV3ZXIiLCJvcmlnaW4iOiJ1c2VySWRwIiwic2NvcGUiOlsiYnVsbGV0aW5ib2FyZCF0NDAwLkRpc3BsYXkiXSwiZXhwIjo2OTc0MDMxNjAwLCJpYXQiOjE1NjIzMzY1NTcsImVtYWlsIjoidmlld2VyQHRlc3Qub3JnIiwiY2lkIjoic2IteHNhcHBsaWNhdGlvbiF0ODk1In0.xWCwlAdOLFH76gZiLoKsnFH48pof5A7Em2k9oZ9CHzbMQZK26D66BD76bavwqnAhIn1toig3e6E01mtnuBwCymbMU1lUNBq5dfkxSBDWFsbQ-btjzb9ktiax5_joGY1xH5R87CFIs87grntZdP5Cw3XfvfOaTX_-WHobRVR59K6EkRP9K1QzqVOjeSm2j_iAqwwAR8QhoUtgDcJTtTEbXfZj1Ri2MSOJyrPpeRXd_ZT7ku7phFII4M7Szz8RFSQFWBaOiC_uyxWjYx7zr1mLw1buSNZUg-TW8bHdkZ_cWZDYSQBjulU5gadLKyzXkZaZHkAEn2KNKsc-azF58mrbuw",
15 | "type": "text"
16 | },
17 | {
18 | "enabled": true,
19 | "key": "AUTH_viewer_confidential",
20 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJjb25maWRlbnRpYWxpdHlfbGV2ZWwiOlsiQ09ORklERU5USUFMIl19LCJ1c2VyX25hbWUiOiJ2aWV3ZXIiLCJvcmlnaW4iOiJ1c2VySWRwIiwic2NvcGUiOlsiYnVsbGV0aW5ib2FyZCF0NDAwLkRpc3BsYXkiXSwiZXhwIjo2OTc0MDMxNjAwLCJpYXQiOjE1NjIzMzY1ODgsImVtYWlsIjoidmlld2VyQHRlc3Qub3JnIiwiY2lkIjoic2IteHNhcHBsaWNhdGlvbiF0ODk1In0.y7XG-r-SQ5M_zrvwYZhy9ms2sWFOEMnMpO29TzjXzIwqlojTcqGswoYGDOyl_enFItQ67WVjR31Rmefq84pZsFaKxGOh3KOBu_vC3uM8_EV3MpgioOnZ2DDHkylABlTBOVffFI2omBpqk_eJYePcuW84WSdI3H-pveF8KgqBPZ4FhWtH617s44BxD0Rar9Bc37Hp8nFTfK4HOrVQQr8CkUHaYBovVo9YsS7UA8iHussQFGxCh5skvY3q9qaTJ9q-Z7UWuVoCe6_MH1h0EZbyfSAApEjsghGY49xysGCUOsde5E0_rsPOeXj0BGRud1sf-nqLdGnEKHgjjMVyT3dZPA",
21 | "type": "text"
22 | }
23 | ],
24 | "timestamp": 1562336636231,
25 | "_postman_variable_scope": "environment",
26 | "_postman_exported_at": "2019-07-05T14:26:27.084Z",
27 | "_postman_exported_using": "Postman/5.5.4"
28 | }
--------------------------------------------------------------------------------
/spring-security-basis/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.sap.cloud.security.xsuaa
5 | demo-application-security-basis
6 | 2.1.0
7 | jar
8 |
9 | demo-application-security-basis
10 | Demo project for Spring Boot using Spring Security. It integrates to SAP BTP XSUAA service
11 | using the Java Client Security Library offered by SAP.
12 |
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.1.9.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 | 5.2.0.RELEASE
26 | 2.1.0
27 |
28 |
29 |
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-actuator
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-cloud-connectors
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-data-jpa
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-web
46 |
47 |
48 |
49 |
50 | com.h2database
51 | h2
52 | runtime
53 |
54 |
55 |
56 |
57 | com.sap.cloud.security.xsuaa
58 | xsuaa-spring-boot-starter
59 | ${sap.cloud.security.version}
60 |
61 |
62 | com.sap.cloud.security.xsuaa
63 | spring-xsuaa-mock
64 | ${sap.cloud.security.version}
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-test
70 | test
71 |
72 |
73 | com.sap.cloud.security.xsuaa
74 | spring-xsuaa-test
75 | ${sap.cloud.security.version}
76 | test
77 |
78 |
79 |
80 |
81 | ${project.artifactId}
82 |
83 |
84 | org.springframework.boot
85 | spring-boot-maven-plugin
86 |
87 |
88 | org.apache.maven.plugins
89 | maven-surefire-plugin
90 | 2.18.1
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/CREDITS:
--------------------------------------------------------------------------------
1 | This program references the following third party open source or other free download components.
2 | The third party licensors of these components may provide additional license rights,
3 | terms and conditions and/or require certain notices as described below.
4 |
5 | Spring Boot Framework (https://github.com/spring-projects/spring-boot)
6 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
7 |
8 | Spring Security (https://github.com/spring-projects/spring-security)
9 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | SAP Java Security Library (https://github.com/SAP/cloud-security-xsuaa-integration)
12 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Hibernate Core ORM (JPA) (https://github.com/hibernate/hibernate-orm/tree/master/hibernate-core)
15 | Licensed under GNU Lesser General Public License v2.1 - https://github.com/hibernate/hibernate-orm/blob/master/lgpl.txt
16 |
17 | PostgreSQL JDBC Driver (https://jdbc.postgresql.org/)
18 | Licensed under BSD 2-Clause "Simplified" License - https://github.com/pgjdbc/pgjdbc/blob/master/LICENSE
19 |
20 | HikariCP JDBC Connection Pool (https://github.com/brettwooldridge/HikariCP)
21 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
22 |
23 | Spring Data JPA (https://github.com/spring-projects/spring-data-jpa)
24 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
25 |
26 | Liquibase (http://github.com/liquibase/liquibase/)
27 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
28 |
29 | EHCache (http://ehcache.org/)
30 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
31 |
32 | Spring Cloud Connector (https://github.com/spring-cloud/spring-cloud-connectors)
33 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
34 |
35 | SAP Java Logging Support for Cloud Foundry (https://github.com/SAP/cf-java-logging-support)
36 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
37 |
38 | Jackson Project (https://github.com/FasterXML/jackson)
39 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
40 |
41 | Hibernate Validator (http://hibernate.org/validator/)
42 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
43 |
44 |
45 | JUnit (https://junit.org/junit4/)
46 | Licensed under Eclipse Public License 1.0 - http://www.eclipse.org/legal/epl-v10.html
47 |
48 | Java Hamcrest (http://hamcrest.org/JavaHamcrest/)
49 | Licensed under BSD-3-Clause License - https://opensource.org/licenses/BSD-3-Clause
50 |
51 | Mockito (http://site.mockito.org/)
52 | Licensed under MIT License - https://github.com/mockito/mockito/blob/master/LICENSE
53 |
54 | Jayway JsonPath (https://github.com/json-path/JsonPath/)
55 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
56 |
57 | H2 Database (http://www.h2database.com/html/main.html)
58 | Licensed under Mozilla Public License Version 2.0 - https://www.mozilla.org/en-US/MPL/2.0/
59 |
60 |
61 | Tomcat Maven Plugin (https://tomcat.apache.org/maven-plugin-trunk/tomcat7-maven-plugin/)
62 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
63 |
64 | JaCoCo Java Code Coverage Library (http://www.eclemma.org/jacoco/)
65 | Licensed under Eclipse Public License v1.0 - http://www.eclipse.org/legal/epl-v10.html
66 |
67 | FindBugs Maven Plugin (https://gleclaire.github.io/findbugs-maven-plugin/)
68 | Licensed under Apache License, Version 2.0 - https://gleclaire.github.io/findbugs-maven-plugin/license.html
69 |
70 | Apache Maven PMD Plugin (https://maven.apache.org/plugins/maven-pmd-plugin/)
71 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
72 |
73 | Maven Surefire Plugin (http://maven.apache.org/components/surefire/maven-surefire-plugin/)
74 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
75 |
76 | Maven Failsafe Plugin (http://maven.apache.org/components/surefire/maven-failsafe-plugin/)
77 | Licensed under Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
78 |
79 |
--------------------------------------------------------------------------------
/spring-security-acl/documentation/testing/spring-acl-local.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "9ec1dfbb-daee-713e-37be-0e46052581d0",
3 | "name": "spring-acl-local",
4 | "values": [
5 | {
6 | "enabled": true,
7 | "key": "AUTH_myfriend",
8 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJidWxsZXRpbmJvYXJkIjpbIkRFX1dERjAzX0JvYXJkIl19LCJ1c2VyX25hbWUiOiJteWZyaWVuZCIsIm9yaWdpbiI6InVzZXJJZHAiLCJleHAiOjY5NzQwMzE2MDAsImlhdCI6MTU2MjMzODEzNCwiZW1haWwiOiJteWZyaWVuZEB0ZXN0Lm9yZyIsImNpZCI6InNiLWJ1bGxldGluYm9hcmQhdDQwMCJ9.b50Qa0-aXk9Uxj7J2GfVKOpu6kqwfxf-yc6DDgYVFXSvpASDQ74iPNEhLR4njxdBngLtirJZ0v5TaO9pYycK25ZARGK4sihmbzvMXm65BpBUBi7YTnRi293hsd-n72PWwo1Z7lji45WArvZ_5UF4BublRwAWpvQ9_mTr8CVlPw41d96ubuknxvjGhtNntJBv2KN4VV4PZV2K20WDw1ZchvtHyxDblFFS3k6xqxsYOWCbwv2mTfrOka_PADB4jyCcqzxEfx3Gp57SXdQ1HSD4Z5iEIdqRmx6pGOWRNMMbLNOJbCw_Fsi-EyKSaW2IxOTApkwc-UcQPebwdc4dNWEuCA",
9 | "type": "text"
10 | },
11 | {
12 | "enabled": true,
13 | "key": "AUTH_advertiser",
14 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJsb2NhdGlvbiI6WyJERSJdLCJidWxsZXRpbmJvYXJkIjpbIklMX1JBQTAzX0JvYXJkIl19LCJ1c2VyX25hbWUiOiJhZE93bmVyIiwib3JpZ2luIjoidXNlcklkcCIsImV4cCI6Njk3NDAzMTYwMCwiaWF0IjoxNTYyMzM4MDM5LCJlbWFpbCI6ImFkT3duZXJAdGVzdC5vcmciLCJjaWQiOiJzYi1idWxsZXRpbmJvYXJkIXQ0MDAifQ.AhLzE1GXeIIZVC0XIg7dGQWItYXIIqJThGAUJDp24xsIyfMk8uOUOCGvYRQgdcY9gAEIUgMQjLTVBTvfU0lQWt78_D0cR3dhvUD9vT-MRkSjA08kjl5NTuaXTCInYmeCpbQ1fuIpZV5rhAvNyhaJalwLSnq90ANJV8_dnLjx950dhOQ5dvqJJ8oKu-U-pB7qmutFz8QiDVuiYKLblaDOlvfrKJxYmKPUozR2_nuGIHVHSs6vjEiVk_mJLcPH5UajTg5CDFmiq_2DcZUSMWoUzBUvRRdal7guCJYmAv-fR40Nya9Gh2HQ1E9vccMVYZTccLXaHf6EBOhlsafD1rvPLQ",
15 | "type": "text"
16 | },
17 | {
18 | "enabled": true,
19 | "key": "AUTH_myTeamMember",
20 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJncm91cCI6WyJVR19NWV9URUFNIl19LCJ1c2VyX25hbWUiOiJncm91cE1lbWJlciIsIm9yaWdpbiI6InVzZXJJZHAiLCJleHAiOjY5NzQwMzE2MDAsImlhdCI6MTU2MjMzODA3NywiZW1haWwiOiJncm91cE1lbWJlckB0ZXN0Lm9yZyIsImNpZCI6InNiLWJ1bGxldGluYm9hcmQhdDQwMCJ9.tXQku3SFF9_S4_FgFy_D-a8eLrkM2mWfPpvZ2IHO5eKs4goO23p6ppsnz5Uz0uCMfK9wGZyUcyc00RsdF27hGLWPCaYmR-K9DOzd1Qj-bZWO4kUlTFNUYvxNP1NSwK7sI5XiDsQ4bmVoQWFaeY-V1xjy-aOGaxVZBQv8GVvMIfLhejaP0RpiQfgpALT3ZFiQiX_zGuDytS3h4ePSdPiD_Z9oA9jSWBN7Ea9MW6UIozKDF-Z32fVTfq8fDrK_OxxNDr8r1AHJmtUaRymydty9_v_WLoPCuqSbhPSCH6qGFb_cLqL4-4eeuRcmm8fH_7ej5bEb-Tp6S3ACAnoT_2Tzxw",
21 | "type": "text"
22 | },
23 | {
24 | "enabled": true,
25 | "key": "AUTH_viewer_DE_WDF03",
26 | "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzMxOTUvdG9rZW5fa2V5cyIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkifQ.eyJleHRfYXR0ciI6eyJ6ZG4iOiIifSwiemlkIjoidWFhIiwiemRuIjoiIiwiZ3JhbnRfdHlwZSI6InVybjppZXRmOnBhcmFtczpvYXV0aDpncmFudC10eXBlOnNhbWwyLWJlYXJlciIsInhzLnVzZXIuYXR0cmlidXRlcyI6eyJidWxsZXRpbmJvYXJkIjpbIkRFX1dERjAzX0JvYXJkIl19LCJ1c2VyX25hbWUiOiJib2FyZFZpZXdlciIsIm9yaWdpbiI6InVzZXJJZHAiLCJleHAiOjY5NzQwMzE2MDAsImlhdCI6MTU2MjMzODE1MSwiZW1haWwiOiJib2FyZFZpZXdlckB0ZXN0Lm9yZyIsImNpZCI6InNiLWJ1bGxldGluYm9hcmQhdDQwMCJ9.JOQH7eLvOyzHPxa_bRMw3ibiPAI7P5TdLDcd6RqgHtkCTnu_NCMYVZBBIYI_IUwpykKZd78fc085Lt2sADKH1J7GZo3DGwrxYd0TgJ34vjJcHd9JFeCRvWMwQyUO_uZWKQJJgPTwy4FK6vC1GPktIsDjRbHBVg-9xbhA_z8jpOkL00K4SWv5n8angCBqurWsLVz-E104Vni4WgUGSRLCuUHEkF00vzp0JwuRoCoTbK7EA-Fbv6ZaQJ9n-zkCO4R-xAVhZNJlglQwYeuvC4LEjZ3qKLE8G1d7Uj4o069nAdY53vfF4fBM91YTqRacUsVxzDrTlrh8csyzC4JNb_QD9g",
27 | "type": "text"
28 | }
29 | ],
30 | "timestamp": 1562338254879,
31 | "_postman_variable_scope": "environment",
32 | "_postman_exported_at": "2019-07-05T15:00:18.874Z",
33 | "_postman_exported_using": "Postman/5.5.4"
34 | }
--------------------------------------------------------------------------------
/spring-security-acl/src/main/java/com/sap/cp/appsec/domain/AdvertisementAclRepository.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.domain;
2 |
3 | import org.springframework.data.domain.Page;
4 | import org.springframework.data.domain.Pageable;
5 | import org.springframework.data.jpa.repository.Query;
6 | import org.springframework.data.repository.PagingAndSortingRepository;
7 | import org.springframework.data.repository.query.Param;
8 |
9 | import java.util.List;
10 |
11 | public interface AdvertisementAclRepository extends PagingAndSortingRepository {
12 |
13 | String FIND_ADS_FOR_SID_SUBQUERY =
14 | "FROM ACL_OBJECT_IDENTITY obj " +
15 | "INNER JOIN ACL_ENTRY entry ON entry.acl_object_identity = obj.id " +
16 | "INNER JOIN ACL_SID sid ON entry.sid = sid.id " +
17 | "INNER JOIN ADVERTISEMENT ads ON CAST(obj.object_id_identity as bigint) = ads.id " +
18 | "WHERE sid.sid IN :sid " +
19 | "AND (entry.mask = :mask) " +
20 | "AND entry.granting = true " +
21 | "AND obj.object_id_class = (SELECT id FROM ACL_CLASS WHERE acl_class.class = 'com.sap.cp.appsec.domain.Advertisement')";
22 |
23 | String GET_ALL_ACCESSIBLE_OBJECTS_RECURSIVE_CTE =
24 | "WITH RECURSIVE accessibleObjects (id, object_id_class, object_id_identity, parent_object) AS " +
25 | "( SELECT id, " +
26 | "object_id_class, " +
27 | "object_id_identity, " +
28 | "parent_object " +
29 | " FROM ACL_OBJECT_IDENTITY " +
30 | " WHERE id IN (SELECT DISTINCT acl_object_identity " +
31 | "FROM ACL_ENTRY entry " +
32 | "INNER JOIN ACL_SID sid ON entry.sid = sid.id " +
33 | "INNER JOIN ACL_OBJECT_IDENTITY obj ON entry.acl_object_identity = obj.id " +
34 | "WHERE " +
35 | "entry.granting = true " + // AND ENTRIES_INHERITING = true to exclude leafs
36 | "AND entry.mask = :mask " +
37 | "AND sid.sid IN :sid " +
38 | ") " +
39 | " UNION ALL " +
40 | "SELECT parent.id, " +
41 | "parent.object_id_class, " +
42 | "parent.object_id_identity, " +
43 | "parent.parent_object " +
44 | "FROM ACL_OBJECT_IDENTITY parent, accessibleObjects " +
45 | "WHERE parent.parent_object = accessibleObjects.id " +
46 | ") ";
47 |
48 | String FIND_ADS_FOR_SID_IN_HIERARCHY_SUBQUERY =
49 | "FROM accessibleObjects " +
50 | "INNER JOIN ADVERTISEMENT ads ON CAST(object_id_identity AS bigint) = ads.id " +
51 | "WHERE ads.is_published = TRUE " +
52 | "AND object_id_class = ( " +
53 | "SELECT id FROM ACL_CLASS WHERE acl_class.class = 'com.sap.cp.appsec.domain.Advertisement' ) " +
54 | "ORDER BY ads.id DESC ";
55 |
56 |
57 | String SELECT_ADS_FOR_SID_QUERY = "SELECT DISTINCT ads.*"
58 | + FIND_ADS_FOR_SID_SUBQUERY;
59 |
60 | String COUNT_ADS_FOR_SID_QUERY = "SELECT COUNT( DISTINCT ads.id) " + FIND_ADS_FOR_SID_SUBQUERY;
61 |
62 | String SELECT_ADS_FOR_SID_IN_HIERARCHY_QUERY = GET_ALL_ACCESSIBLE_OBJECTS_RECURSIVE_CTE +
63 | "SELECT DISTINCT ads.* " + FIND_ADS_FOR_SID_IN_HIERARCHY_SUBQUERY;
64 |
65 | String COUNT_ADS_FOR_SID_IN_HIERARCHY_QUERY = GET_ALL_ACCESSIBLE_OBJECTS_RECURSIVE_CTE +
66 | "SELECT COUNT(DISTINCT ads.id) " + FIND_ADS_FOR_SID_IN_HIERARCHY_SUBQUERY;
67 |
68 |
69 | @Query(value = SELECT_ADS_FOR_SID_QUERY, countQuery = COUNT_ADS_FOR_SID_QUERY, nativeQuery = true)
70 | Page findAllByPermission(@Param("mask") int permissionCode, @Param("sid") String[] sid, Pageable pageable);
71 |
72 | @Query(value = SELECT_ADS_FOR_SID_IN_HIERARCHY_QUERY, countQuery = COUNT_ADS_FOR_SID_IN_HIERARCHY_QUERY, nativeQuery = true)
73 | Page findAllPublishedByHierarchicalPermission(
74 | @Param("mask") int permissionCode, @Param("sid") String[] sid, Pageable pageable);
75 |
76 | List findByTitle(String title);
77 | }
78 |
--------------------------------------------------------------------------------
/spring-security-acl/documentation/testing/spring-acl-cloudfoundry.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "variables": [],
3 | "info": {
4 | "name": "CPApplicationSecurity-CF",
5 | "_postman_id": "8e7e3ccb-8a80-3f64-035f-1adfafa489a7",
6 | "description": "",
7 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
8 | },
9 | "item": [
10 | {
11 | "name": "/ads/actuator/health",
12 | "request": {
13 | "url": "{{approuterUri}}/ads/actuator/health",
14 | "method": "GET",
15 | "header": [],
16 | "body": {
17 | "mode": "raw",
18 | "raw": "{\n\t\"title\":\"test\",\n\t\"price\": \"40\",\n\t\"contact\": \"myemail\"\n}"
19 | },
20 | "description": ""
21 | },
22 | "response": []
23 | },
24 | {
25 | "name": "/ads/api/v1/ads/acl/my",
26 | "request": {
27 | "url": "{{approuterUri}}/ads/api/v1/ads/acl/my",
28 | "method": "GET",
29 | "header": [],
30 | "body": {},
31 | "description": ""
32 | },
33 | "response": []
34 | },
35 | {
36 | "name": "Fetch X-Csrf-Token for POST / PUT",
37 | "request": {
38 | "url": "{{approuterUri}}",
39 | "method": "GET",
40 | "header": [
41 | {
42 | "key": "x-csrf-token",
43 | "value": "fetch",
44 | "description": ""
45 | }
46 | ],
47 | "body": {},
48 | "description": "Prerequisite: Activate Postman Interceptor\n\nEnter the received JSESSIONID in the \"Cookie\" header"
49 | },
50 | "response": []
51 | },
52 | {
53 | "name": "/ads/api/v1/ads/acl",
54 | "request": {
55 | "url": "{{approuterUri}}/ads/api/v1/ads/acl",
56 | "method": "POST",
57 | "header": [
58 | {
59 | "key": "Content-Type",
60 | "value": "application/json",
61 | "description": ""
62 | },
63 | {
64 | "key": "x-csrf-token",
65 | "value": "",
66 | "description": "",
67 | "disabled": true
68 | }
69 | ],
70 | "body": {
71 | "mode": "raw",
72 | "raw": "{\n\t\"title\":\"test\",\n\t\"price\": \"40\",\n\t\"contact\": \"myemail\"\n}"
73 | },
74 | "description": ""
75 | },
76 | "response": []
77 | },
78 | {
79 | "name": "/.../acl/grantPermissionsToUserGroup/{id}",
80 | "request": {
81 | "url": "{{approuterUri}}/ads/api/v1/ads/acl/grantPermissionsToUserGroup/{id}",
82 | "method": "PUT",
83 | "header": [
84 | {
85 | "key": "Content-Type",
86 | "value": "application/json",
87 | "description": ""
88 | },
89 | {
90 | "key": "x-csrf-token",
91 | "value": "csrfProtection",
92 | "description": "",
93 | "disabled": true
94 | }
95 | ],
96 | "body": {
97 | "mode": "raw",
98 | "raw": "{\n\t\"name\":\"UG_MY_TEAM\",\n\t\"permissionCodes\":[\"R\"]\n}"
99 | },
100 | "description": ""
101 | },
102 | "response": []
103 | },
104 | {
105 | "name": "/.../acl/grantPermissionsToUser/{id}",
106 | "request": {
107 | "url": "{{approuterUri}}/ads/api/v1/ads/acl/grantPermissionsToUser/{id}",
108 | "method": "PUT",
109 | "header": [
110 | {
111 | "key": "Content-Type",
112 | "value": "application/json",
113 | "description": ""
114 | },
115 | {
116 | "key": "x-csrf-token",
117 | "value": "csrfProtection",
118 | "description": "",
119 | "disabled": true
120 | }
121 | ],
122 | "body": {
123 | "mode": "raw",
124 | "raw": "{\n\t\"name\":\"myfriend\",\n\t\"permissionCodes\":[\"R\", \"A\"]\n}"
125 | },
126 | "description": "Publish to Group, specify permission(s)"
127 | },
128 | "response": []
129 | },
130 | {
131 | "name": "/.../acl/removePermissionsFromUser/{id}",
132 | "request": {
133 | "url": "{{approuterUri}}/ads/acl/removePermissionsFromUser/{id}",
134 | "method": "PUT",
135 | "header": [
136 | {
137 | "key": "Content-Type",
138 | "value": "application/json",
139 | "description": ""
140 | },
141 | {
142 | "key": "x-csrf-token",
143 | "value": "",
144 | "description": "",
145 | "disabled": true
146 | }
147 | ],
148 | "body": {
149 | "mode": "raw",
150 | "raw": "{\n\t\"name\":\"myfriend\",\n\t\"permissionCodes\":[\"R\"]\n}"
151 | },
152 | "description": "Publish to Group, specify permission(s)"
153 | },
154 | "response": []
155 | }
156 | ]
157 | }
--------------------------------------------------------------------------------
/spring-security-acl/src/main/java/com/sap/cp/appsec/config/AclConfig.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.config;
2 |
3 | import com.zaxxer.hikari.HikariDataSource;
4 | import org.springframework.cache.ehcache.EhCacheFactoryBean;
5 | import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.security.access.PermissionEvaluator;
9 | import org.springframework.security.acls.AclPermissionEvaluator;
10 | import org.springframework.security.acls.domain.*;
11 | import org.springframework.security.acls.jdbc.BasicLookupStrategy;
12 | import org.springframework.security.acls.jdbc.JdbcMutableAclService;
13 | import org.springframework.security.acls.jdbc.LookupStrategy;
14 | import org.springframework.security.acls.model.AclCache;
15 | import org.springframework.security.acls.model.MutableAclService;
16 | import org.springframework.security.acls.model.PermissionGrantingStrategy;
17 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
18 |
19 | import javax.sql.DataSource;
20 | import java.util.List;
21 |
22 | @Configuration
23 | public class AclConfig {
24 |
25 | @Bean
26 | public MutableAclService aclService(HikariDataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
27 | JdbcMutableAclService jdbcAclService = new PostgresJdbcMutableAclService(
28 | dataSource, lookupStrategy, aclCache);
29 |
30 | jdbcAclService.setAclClassIdSupported(true);
31 |
32 | if (dataSource.getDriverClassName().equals("org.postgresql.Driver")) {
33 | // because of PostgreSQL as documented here:
34 | // https://docs.spring.io/spring-security/site/docs/current/reference/html5/#postgresql
35 | jdbcAclService.setClassIdentityQuery("select currval(pg_get_serial_sequence('acl_class', 'id'))");
36 | jdbcAclService.setSidIdentityQuery("select currval(pg_get_serial_sequence('acl_sid', 'id'))");
37 | }
38 | return jdbcAclService;
39 | }
40 |
41 | @Bean //implements hasPermission annotations
42 | public PermissionEvaluator permissionEvaluator(MutableAclService aclService) {
43 | return new AclPermissionEvaluator(aclService);
44 | }
45 |
46 | @Bean
47 | public LookupStrategy lookupStrategy(DataSource dataSource) {
48 | BasicLookupStrategy strategy = new BasicLookupStrategy(
49 | dataSource,
50 | aclCache(),
51 | aclAuthorizationStrategy(),
52 | new AclAuditLogger()
53 | );
54 |
55 | strategy.setAclClassIdSupported(true);
56 | strategy.setPermissionFactory(new DefaultPermissionFactory(BasePermission.class));
57 | return strategy;
58 | }
59 |
60 | @Bean
61 | public AclAuthorizationStrategy aclAuthorizationStrategy() {
62 | return new AclAuthorizationStrategyImpl(
63 | new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
64 | }
65 |
66 | @Bean
67 | public PermissionGrantingStrategy permissionGrantingStrategy() {
68 | return new DefaultPermissionGrantingStrategy(
69 | new AclAuditLogger());
70 | }
71 |
72 | // Cache Setup
73 |
74 | @Bean
75 | public AclCache aclCache() {
76 | return new EhCacheBasedAclCache(
77 | aclEhCacheFactoryBean().getObject(),
78 | permissionGrantingStrategy(),
79 | aclAuthorizationStrategy()
80 | );
81 | }
82 |
83 | @Bean
84 | public EhCacheFactoryBean aclEhCacheFactoryBean() {
85 | EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
86 | ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
87 | ehCacheFactoryBean.setCacheName("acl_cache");
88 | return ehCacheFactoryBean;
89 | }
90 |
91 | @Bean
92 | public EhCacheManagerFactoryBean aclCacheManager() {
93 | EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
94 | cacheManagerFactoryBean.setShared(true);
95 | return cacheManagerFactoryBean;
96 | }
97 |
98 |
99 | public static class PostgresJdbcMutableAclService extends JdbcMutableAclService {
100 | private String selectSidsWithPrefix = "select acl_sid.sid from acl_sid "
101 | + "where acl_sid.sid like ? and "
102 | + " acl_sid.principal = false";
103 |
104 | public PostgresJdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
105 | super(dataSource, lookupStrategy, aclCache);
106 | }
107 |
108 | public List getAllSidsWithPrefix(String prefix) {
109 | return jdbcOperations.queryForList(selectSidsWithPrefix, String.class, prefix + "_%");
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/spring-security-basis/documentation/testing/spring-security-local.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "variables": [],
3 | "info": {
4 | "name": "Basis-Local-CPApplicationSecurity",
5 | "_postman_id": "121b1c05-0cd4-9f83-2f60-3d4ca2202e3b",
6 | "description": "",
7 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
8 | },
9 | "item": [
10 | {
11 | "name": "F4 /api/v1/attribute/confidentiality_level",
12 | "request": {
13 | "url": "http://localhost:8080/api/v1/attribute/confidentiality_level",
14 | "method": "GET",
15 | "header": [],
16 | "body": {
17 | "mode": "raw",
18 | "raw": ""
19 | },
20 | "description": ""
21 | },
22 | "response": []
23 | },
24 | {
25 | "name": "/api/v1/ads/",
26 | "request": {
27 | "url": "http://localhost:8080/api/v1/ads/",
28 | "method": "POST",
29 | "header": [
30 | {
31 | "key": "Content-Type",
32 | "value": "application/json"
33 | },
34 | {
35 | "key": "Authorization",
36 | "value": "{{AUTH_advertiser}}",
37 | "description": "Advertiser role with confidentiality_level = PUBLIC"
38 | }
39 | ],
40 | "body": {
41 | "mode": "raw",
42 | "raw": "{\n\t\"title\":\"hi\",\n\t\"price\": \"50\",\n\t\"contact\": \"myemail\",\n\t\"currency\" : \"EUR\",\n\t\"confidentialityLevel\": \"CONFIDENTIAL\"\n}"
43 | },
44 | "description": ""
45 | },
46 | "response": []
47 | },
48 | {
49 | "name": "/api/v1/ads/",
50 | "request": {
51 | "url": "http://localhost:8080/api/v1/ads/{id}",
52 | "method": "GET",
53 | "header": [
54 | {
55 | "key": "Content-Type",
56 | "value": "application/json",
57 | "description": ""
58 | },
59 | {
60 | "key": "Authorization",
61 | "value": "{{AUTH_advertiser}}",
62 | "description": "Advertiser role with confidentiality_level = PUBLIC"
63 | }
64 | ],
65 | "body": {
66 | "mode": "raw",
67 | "raw": "{\n\t\"title\":\"hi\",\n\t\"price\": \"50\",\n\t\"contact\": \"myemail\",\n\t\"currency\" : \"EUR\",\n\t\"confidentialityLevel\": \"CONFIDENTIAL\"\n}"
68 | },
69 | "description": ""
70 | },
71 | "response": []
72 | },
73 | {
74 | "name": "/api/v1/ads/confidentiality/{confidentiality_level}",
75 | "request": {
76 | "url": "http://localhost:8080/api/v1/ads/confidentiality/{confidentiality_level}",
77 | "method": "GET",
78 | "header": [
79 | {
80 | "key": "Content-Type",
81 | "value": "application/json"
82 | },
83 | {
84 | "key": "Authorization",
85 | "value": "{{AUTH_viewer_public}}",
86 | "description": "Viewer role with confidentiality_level = PUBLIC"
87 | },
88 | {
89 | "key": "Authorization",
90 | "value": "{{AUTH_viewer_confidential}}",
91 | "description": "Viewer role with confidentiality_level = STRICTLY_CONFIDENTIAL",
92 | "disabled": true
93 | }
94 | ],
95 | "body": {
96 | "mode": "raw",
97 | "raw": ""
98 | },
99 | "description": ""
100 | },
101 | "response": []
102 | },
103 | {
104 | "name": "/api/v1/ads/{id}",
105 | "request": {
106 | "url": "http://localhost:8080/api/v1/ads/{id}",
107 | "method": "PUT",
108 | "header": [
109 | {
110 | "key": "Content-Type",
111 | "value": "application/json"
112 | },
113 | {
114 | "key": "Authorization",
115 | "value": "{{AUTH_advertiser}}",
116 | "description": "Advertiser role with confidentiality_level = PUBLIC"
117 | }
118 | ],
119 | "body": {
120 | "mode": "raw",
121 | "raw": "{\n \"id\": {id},\n \"title\": \"some update\",\n \"price\": 50,\n \"contact\": \"myemail\",\n \"currency\": \"EUR\",\n \"category\": null,\n \"purchasedOn\": null,\n \"metadata\": {\n \"version\": 0\n }\n}\n"
122 | },
123 | "description": ""
124 | },
125 | "response": []
126 | },
127 | {
128 | "name": "/api/v1/ads/{id}",
129 | "request": {
130 | "url": "http://localhost:8080/api/v1/ads/{id}",
131 | "method": "DELETE",
132 | "header": [
133 | {
134 | "key": "Authorization",
135 | "value": "{{AUTH_advertiser}}",
136 | "description": "Advertiser role with confidentiality_level = PUBLIC"
137 | }
138 | ],
139 | "body": {
140 | "mode": "raw",
141 | "raw": ""
142 | },
143 | "description": ""
144 | },
145 | "response": []
146 | }
147 | ]
148 | }
--------------------------------------------------------------------------------
/spring-security-acl/src/test/java/com/sap/cp/appsec/domain/AdvertisementAclRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.domain;
2 |
3 | import org.junit.After;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.boot.test.mock.mockito.MockBean;
10 | import org.springframework.data.domain.AuditorAware;
11 | import org.springframework.orm.ObjectOptimisticLockingFailureException;
12 | import org.springframework.test.context.junit4.SpringRunner;
13 |
14 | import java.sql.Timestamp;
15 | import java.util.Optional;
16 |
17 | import static org.hamcrest.MatcherAssert.assertThat;
18 | import static org.hamcrest.Matchers.is;
19 | import static org.hamcrest.Matchers.not;
20 | import static org.hamcrest.core.IsNull.notNullValue;
21 | import static org.mockito.Mockito.when;
22 |
23 |
24 | @RunWith(SpringRunner.class)
25 | @SpringBootTest
26 | public class AdvertisementAclRepositoryTest {
27 | @Autowired
28 | private AdvertisementAclRepository repo;
29 | private Advertisement entity;
30 |
31 | @MockBean
32 | private AuditorAware auditorAware;
33 |
34 | @Before
35 | public void setUp() {
36 | when(auditorAware.getCurrentAuditor())
37 | .thenReturn(Optional.of("Auditor_1"))
38 | .thenReturn(Optional.of("Auditor_2"))
39 | .thenReturn(Optional.of("Auditor_3"));
40 | entity = new Advertisement("SOME title", "contact@email.de");
41 | }
42 |
43 | @After
44 | public void tearDown() {
45 | repo.deleteAll();
46 | assertThat(repo.count(), is(0L));
47 | }
48 |
49 | @Test
50 | public void shouldSetIdOnFirstSave() {
51 | entity = repo.save(entity);
52 | assertThat(entity.getId(), is(notNullValue()));
53 | }
54 |
55 | @Test
56 | public void shouldSetCreatedTimestampOnFirstSaveOnly() throws InterruptedException {
57 | entity = repo.save(entity);
58 | Timestamp timestampAfterCreation = entity.getCreatedAt();
59 | assertThat(timestampAfterCreation, is(notNullValue()));
60 |
61 | entity.setTitle("Updated Title");
62 | Thread.sleep(5); // Better: mock time!
63 |
64 | entity = repo.save(entity);
65 | Timestamp timestampAfterUpdate = entity.getCreatedAt();
66 | assertThat(timestampAfterUpdate, is(timestampAfterCreation));
67 | }
68 |
69 | @Test
70 | public void shouldSetCreatedByOnFirstSaveOnly() {
71 | entity = repo.save(entity);
72 | String userAfterCreation = entity.getCreatedBy();
73 | assertThat(userAfterCreation, is("Auditor_1"));
74 |
75 | entity.setTitle("Updated Title");
76 |
77 | entity = repo.save(entity);
78 | String userAfterUpdate = entity.getCreatedBy();
79 | assertThat(userAfterUpdate, is(userAfterCreation));
80 | }
81 |
82 | @Test
83 | public void shouldSetModifiedTimestampOnEveryUpdate() throws InterruptedException {
84 | entity = repo.save(entity);
85 |
86 | entity.setTitle("Updated Title");
87 | entity = repo.save(entity);
88 |
89 | Timestamp timestampAfterFirstUpdate = entity.getModifiedAt();
90 | assertThat(timestampAfterFirstUpdate, is(notNullValue()));
91 |
92 | Thread.sleep(5); // Better: mock time!
93 |
94 | entity.setTitle("Updated Title 2");
95 | entity = repo.save(entity);
96 | Timestamp timestampAfterSecondUpdate = entity.getModifiedAt();
97 | assertThat(timestampAfterSecondUpdate, is(not(timestampAfterFirstUpdate)));
98 | }
99 |
100 | @Test
101 | public void shouldSetModifiedByOnEveryUpdate() throws InterruptedException {
102 | entity = repo.save(entity);
103 |
104 | entity.setTitle("Updated Title");
105 | entity = repo.save(entity);
106 |
107 | String userAfterFirstUpdate = entity.getModifiedBy();
108 | assertThat(userAfterFirstUpdate, is("Auditor_2"));
109 |
110 | Thread.sleep(5); // Better: mock time!
111 |
112 | entity.setTitle("Updated Title 2");
113 | entity = repo.save(entity);
114 | String userAfterSecondUpdate = entity.getModifiedBy();
115 | assertThat(userAfterSecondUpdate, is(not(userAfterFirstUpdate)));
116 | }
117 |
118 |
119 | @Test(expected = ObjectOptimisticLockingFailureException.class)
120 | public void shouldUseVersionForConflicts() {
121 | // persists entity and sets initial version
122 | entity = repo.save(entity);
123 |
124 | entity.setTitle("entity instance 1");
125 | repo.save(entity); // returns instance with updated version
126 |
127 | repo.save(entity); // tries to persist entity with outdated version
128 | }
129 |
130 | @Test
131 | public void shouldFindByTitle() {
132 | String title = "Find me";
133 |
134 | entity.setTitle(title);
135 | repo.save(entity);
136 |
137 | Advertisement foundEntity = repo.findByTitle(title).get(0);
138 | assertThat(foundEntity.getTitle(), is(title));
139 | }
140 | }
--------------------------------------------------------------------------------
/spring-security-basis/src/main/java/com/sap/cp/appsec/controllers/CustomExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.sap.cp.appsec.controllers;
2 |
3 | import com.sap.cp.appsec.dto.ErrorDto;
4 | import com.sap.cp.appsec.exceptions.BadRequestException;
5 | import com.sap.cp.appsec.exceptions.NotAuthorizedException;
6 | import com.sap.cp.appsec.exceptions.NotFoundException;
7 | import org.springframework.http.HttpHeaders;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.security.access.AccessDeniedException;
11 | import org.springframework.transaction.TransactionSystemException;
12 | import org.springframework.web.bind.MethodArgumentNotValidException;
13 | import org.springframework.web.bind.annotation.ExceptionHandler;
14 | import org.springframework.web.bind.annotation.RestControllerAdvice;
15 | import org.springframework.web.context.request.WebRequest;
16 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
17 |
18 | import javax.validation.ConstraintViolation;
19 | import javax.validation.ConstraintViolationException;
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | import static com.sap.cp.appsec.dto.ErrorDto.DetailError;
24 |
25 |
26 | /**
27 | * A simple exception mapper for exceptions that also provides the error messages as part of the response. Gathers
28 | * all @ExceptionHandler methods in a single class so that exceptions from all controllers are handled consistently in
29 | * one place.
30 | */
31 | @RestControllerAdvice
32 | public class CustomExceptionMapper extends ResponseEntityExceptionHandler {
33 |
34 | @ExceptionHandler
35 | public ResponseEntity