Authentication can now alternatively use CAs TGT
This commit is contained in:
parent
cf85966224
commit
eee84cb005
@ -132,9 +132,10 @@ Also try for example 'admin@xxx.example.com' or 'unknown@example.org'.
|
|||||||
|
|
||||||
If you want a formatted JSON output, you can pipe the result to `jq` or similar.
|
If you want a formatted JSON output, you can pipe the result to `jq` or similar.
|
||||||
|
|
||||||
And to see the full, currently implemented, API, open http://localhost:8080/swagger-ui/index.html (on same port as the API to avoid CORS problems).
|
And to see the full, currently implemented, API, open http://localhost:8080/swagger-ui/index.html).
|
||||||
|
For a locally running app without CAS-authentication (export HSADMINNG_CAS_SERVER=''),
|
||||||
If you still need to install some of these tools, find some hints in the next chapters.
|
authorize using the name of the subject (e.g. "superuser-alex@hostsharing.net" in case of test-data).
|
||||||
|
Otherwise, use a valid CAS-ticket.
|
||||||
|
|
||||||
|
|
||||||
### PostgreSQL Server
|
### PostgreSQL Server
|
||||||
|
@ -31,7 +31,7 @@ def search_keywords_in_files(keywords):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Allowed comment symbols
|
# Allowed comment symbols
|
||||||
comment_symbols = {"//", "#", ";"}
|
comment_symbols = {"//", "#", "##", "###", "####", "#####", ";"}
|
||||||
|
|
||||||
for root, dirs, files in os.walk("."):
|
for root, dirs, files in os.walk("."):
|
||||||
# Ausschließen bestimmter Verzeichnisse
|
# Ausschließen bestimmter Verzeichnisse
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.4.1'
|
id 'org.springframework.boot' version '3.4.2'
|
||||||
id 'io.spring.dependency-management' version '1.1.7' // manages implicit dependencies
|
id 'io.spring.dependency-management' version '1.1.7' // manages implicit dependencies
|
||||||
id 'io.openapiprocessor.openapi-processor' version '2023.2' // generates Controller-interface and resources from API-spec
|
id 'io.openapiprocessor.openapi-processor' version '2023.2' // generates Controller-interface and resources from API-spec
|
||||||
id 'com.github.jk1.dependency-license-report' version '2.9' // checks dependency-license compatibility
|
id 'com.github.jk1.dependency-license-report' version '2.9' // checks dependency-license compatibility
|
||||||
@ -67,7 +67,6 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
|
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
|
||||||
implementation 'org.springdoc:springdoc-openapi:2.8.3'
|
|
||||||
implementation 'org.postgresql:postgresql'
|
implementation 'org.postgresql:postgresql'
|
||||||
implementation 'org.liquibase:liquibase-core'
|
implementation 'org.liquibase:liquibase-core'
|
||||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
|
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
|
||||||
@ -77,7 +76,8 @@ dependencies {
|
|||||||
implementation 'net.java.dev.jna:jna:5.16.0'
|
implementation 'net.java.dev.jna:jna:5.16.0'
|
||||||
implementation 'org.modelmapper:modelmapper:3.2.2'
|
implementation 'org.modelmapper:modelmapper:3.2.2'
|
||||||
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
|
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
|
||||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.3'
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5'
|
||||||
|
//implementation 'org.springdoc:springdoc-openapi-security:1.8.0'
|
||||||
implementation 'org.reflections:reflections:0.10.2'
|
implementation 'org.reflections:reflections:0.10.2'
|
||||||
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package net.hostsharing.hsadminng;
|
package net.hostsharing.hsadminng;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@OpenAPIDefinition
|
||||||
public class HsadminNgApplication {
|
public class HsadminNgApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -21,12 +21,16 @@ public class CasAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
protected void doFilterInternal(
|
protected void doFilterInternal(
|
||||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
|
||||||
|
|
||||||
if (request.getHeader("Authorization") != null) {
|
request.getInputStream();
|
||||||
|
|
||||||
|
if (request.getHeader("authorization") != null) {
|
||||||
final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(request);
|
final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(request);
|
||||||
final var currentSubject = authenticator.authenticate(request);
|
final var currentSubject = authenticator.authenticate(request);
|
||||||
authenticatedRequest.addHeader("current-subject", currentSubject);
|
authenticatedRequest.addHeader("current-subject", currentSubject);
|
||||||
|
authenticatedRequest.getInputStream();
|
||||||
filterChain.doFilter(authenticatedRequest, response);
|
filterChain.doFilter(authenticatedRequest, response);
|
||||||
|
} else {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
|
||||||
|
/** Explicitly marks a REST-Controller for not requiring authorization for Swagger UI.
|
||||||
|
*
|
||||||
|
* @see SecurityRequirement
|
||||||
|
*/
|
||||||
|
@Target(TYPE)
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
public @interface NoSecurityRequirement {
|
||||||
|
}
|
@ -7,7 +7,9 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@ -31,41 +33,68 @@ public class RealCasAuthenticator implements CasAuthenticator {
|
|||||||
public String authenticate(final HttpServletRequest httpRequest) {
|
public String authenticate(final HttpServletRequest httpRequest) {
|
||||||
final var userName = StringUtils.isBlank(casServerUrl)
|
final var userName = StringUtils.isBlank(casServerUrl)
|
||||||
? bypassCurrentSubject(httpRequest)
|
? bypassCurrentSubject(httpRequest)
|
||||||
: casValidation(httpRequest);
|
: casAuthentication(httpRequest);
|
||||||
final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null);
|
final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
return authentication.getName();
|
return authentication.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String bypassCurrentSubject(final HttpServletRequest httpRequest) {
|
private static String bypassCurrentSubject(final HttpServletRequest httpRequest) {
|
||||||
final var userName = httpRequest.getHeader("current-subject");
|
final var userName = httpRequest.getHeader("authorization").replaceAll("^Bearer ", "");
|
||||||
System.err.println("CasAuthenticator.bypassCurrentSubject: " + userName);
|
System.err.println("CasAuthenticator.bypassCurrentSubject: " + userName);
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String casValidation(final HttpServletRequest httpRequest)
|
private String casAuthentication(final HttpServletRequest httpRequest)
|
||||||
throws SAXException, IOException, ParserConfigurationException {
|
throws SAXException, IOException, ParserConfigurationException {
|
||||||
|
|
||||||
final var ticket = httpRequest.getHeader("Authorization");
|
final var ticket = httpRequest.getHeader("authorization").replaceAll("^Bearer ", "");
|
||||||
final var url = casServerUrl + "/p3/serviceValidate" +
|
final var serviceTicket = ticket.startsWith("TGT-")
|
||||||
"?service=" + serviceUrl +
|
? fetchServiceTicket(ticket)
|
||||||
"&ticket=" + ticket;
|
: ticket;
|
||||||
|
final var userName = extractUserName(verifyServiceTicket(serviceTicket));
|
||||||
System.err.println("CasAuthenticator.casValidation using URL: " + url);
|
|
||||||
|
|
||||||
final var response = ((Supplier<String>) () -> restTemplate.getForObject(url, String.class)).get();
|
|
||||||
|
|
||||||
final var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
|
||||||
.parse(new java.io.ByteArrayInputStream(response.getBytes()));
|
|
||||||
if (doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
|
||||||
System.err.println("CAS service ticket could not be validated");
|
|
||||||
System.err.println("CAS-validation-URL: " + url);
|
|
||||||
System.err.println(response);
|
|
||||||
throw new BadCredentialsException("CAS service ticket could not be validated");
|
|
||||||
}
|
|
||||||
final var userName = doc.getElementsByTagName("cas:user").item(0).getTextContent();
|
|
||||||
System.err.println("CAS-user: " + userName);
|
System.err.println("CAS-user: " + userName);
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String fetchServiceTicket(final String ticketGrantingTicket) {
|
||||||
|
final var tgtUrl = casServerUrl + "/cas/v1/tickets/" + ticketGrantingTicket;
|
||||||
|
|
||||||
|
final var restTemplate = new RestTemplate();
|
||||||
|
final var formData = new LinkedMultiValueMap<String, String>();
|
||||||
|
formData.add("service", serviceUrl);
|
||||||
|
|
||||||
|
return restTemplate.postForObject(tgtUrl, formData, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Document verifyServiceTicket(final String serviceTicket) throws SAXException, IOException, ParserConfigurationException {
|
||||||
|
if ( !serviceTicket.startsWith("ST-") ) {
|
||||||
|
throwBadCredentialsException("Invalid authorization ticket");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var url = casServerUrl + "/cas/p3/serviceValidate" +
|
||||||
|
"?service=" + serviceUrl +
|
||||||
|
"&ticket=" + serviceTicket;
|
||||||
|
|
||||||
|
final var response = ((Supplier<String>) () -> restTemplate.getForObject(url, String.class)).get();
|
||||||
|
|
||||||
|
return DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
.parse(new java.io.ByteArrayInputStream(response.getBytes()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractUserName(final Document verification) {
|
||||||
|
|
||||||
|
if (verification.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
||||||
|
System.err.println("CAS service ticket could not be validated");
|
||||||
|
System.err.println(verification);
|
||||||
|
throwBadCredentialsException("CAS service ticket could not be validated");
|
||||||
|
}
|
||||||
|
return verification.getElementsByTagName("cas:user").item(0).getTextContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String throwBadCredentialsException(final String message) {
|
||||||
|
throw new BadCredentialsException(message);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package net.hostsharing.hsadminng.config;
|
package net.hostsharing.hsadminng.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -15,6 +18,8 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
|
// TODO.impl: securitySchemes should work in OpenAPI yaml, but the Spring templates seem not to support it
|
||||||
|
@SecurityScheme(type = SecuritySchemeType.HTTP, name = "casTicket", scheme = "bearer", bearerFormat = "CAS ticket", description = "CAS ticket", in = SecuritySchemeIn.HEADER)
|
||||||
public class WebSecurityConfig {
|
public class WebSecurityConfig {
|
||||||
|
|
||||||
private static final String[] PERMITTED_PATHS = new String[] { "/swagger-ui/**", "/v3/api-docs/**", "/actuator/**" };
|
private static final String[] PERMITTED_PATHS = new String[] { "/swagger-ui/**", "/v3/api-docs/**", "/actuator/**" };
|
||||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.booking.item;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsApi;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsApi;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
||||||
@ -32,6 +33,7 @@ import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateR
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Profile("!only-office")
|
@Profile("!only-office")
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsBookingItemController implements HsBookingItemsApi {
|
public class HsBookingItemController implements HsBookingItemsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.booking.project;
|
package net.hostsharing.hsadminng.hs.booking.project;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
|
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
|
||||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjectsApi;
|
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjectsApi;
|
||||||
@ -22,6 +23,7 @@ import java.util.function.BiConsumer;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Profile("!only-office")
|
@Profile("!only-office")
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsBookingProjectController implements HsBookingProjectsApi {
|
public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
|
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
||||||
@ -29,6 +30,7 @@ import java.util.function.BiConsumer;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Profile("!only-office")
|
@Profile("!only-office")
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsHostingAssetController implements HsHostingAssetsApi {
|
public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import net.hostsharing.hsadminng.config.NoSecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
||||||
@ -14,6 +15,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Profile("!only-office")
|
@Profile("!only-office")
|
||||||
|
@NoSecurityRequirement
|
||||||
public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
|
public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.bankaccount;
|
package net.hostsharing.hsadminng.hs.office.bankaccount;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
|
||||||
@ -18,7 +19,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.contact;
|
package net.hostsharing.hsadminng.hs.office.contact;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
|
||||||
@ -20,6 +21,7 @@ import java.util.UUID;
|
|||||||
import static net.hostsharing.hsadminng.errors.Validate.validate;
|
import static net.hostsharing.hsadminng.errors.Validate.validate;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeContactController implements HsOfficeContactsApi {
|
public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopassets;
|
package net.hostsharing.hsadminng.hs.office.coopassets;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
|
||||||
@ -37,6 +38,7 @@ import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOffic
|
|||||||
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.coopshares;
|
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
||||||
@ -27,6 +28,7 @@ import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOffic
|
|||||||
import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
|
import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopSharesApi {
|
public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopSharesApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
|
||||||
@ -32,7 +33,7 @@ import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
|
|||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.membership;
|
package net.hostsharing.hsadminng.hs.office.membership;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembershipsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembershipsApi;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
|
||||||
@ -24,6 +25,7 @@ import static net.hostsharing.hsadminng.errors.Validate.validate;
|
|||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.partner;
|
package net.hostsharing.hsadminng.hs.office.partner;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
|
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactFromResourceConverter;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactFromResourceConverter;
|
||||||
@ -35,7 +36,7 @@ import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.
|
|||||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficePartnerController implements HsOfficePartnersApi {
|
public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.person;
|
package net.hostsharing.hsadminng.hs.office.person;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
|
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
|
||||||
@ -17,7 +18,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficePersonController implements HsOfficePersonsApi {
|
public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.relation;
|
package net.hostsharing.hsadminng.hs.office.relation;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.errors.Validate;
|
import net.hostsharing.hsadminng.errors.Validate;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||||
@ -26,6 +27,7 @@ import java.util.function.BiConsumer;
|
|||||||
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
||||||
@ -26,7 +27,7 @@ import java.util.function.BiConsumer;
|
|||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.grant;
|
package net.hostsharing.hsadminng.rbac.grant;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
|
||||||
@ -17,6 +18,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class RbacGrantController implements RbacGrantsApi {
|
public class RbacGrantController implements RbacGrantsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.role;
|
package net.hostsharing.hsadminng.rbac.role;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
|
||||||
@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class RbacRoleController implements RbacRolesApi {
|
public class RbacRoleController implements RbacRolesApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.subject;
|
package net.hostsharing.hsadminng.rbac.subject;
|
||||||
|
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
|
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
|
||||||
@ -16,6 +17,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class RbacSubjectController implements RbacSubjectsApi {
|
public class RbacSubjectController implements RbacSubjectsApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.test.cust;
|
package net.hostsharing.hsadminng.rbac.test.cust;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
|
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
|
||||||
@ -15,6 +16,7 @@ import jakarta.persistence.PersistenceContext;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class TestCustomerController implements TestCustomersApi {
|
public class TestCustomerController implements TestCustomersApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.test.pac;
|
package net.hostsharing.hsadminng.rbac.test.pac;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
@ -15,6 +16,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@SecurityRequirement(name = "casTicket")
|
||||||
public class TestPackageController implements TestPackagesApi {
|
public class TestPackageController implements TestPackagesApi {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -4,6 +4,7 @@ get:
|
|||||||
tags:
|
tags:
|
||||||
- testCustomers
|
- testCustomers
|
||||||
operationId: listCustomers
|
operationId: listCustomers
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||||
|
@ -57,7 +57,7 @@ spring:
|
|||||||
# keep this in sync with test/.../application.yml
|
# keep this in sync with test/.../application.yml
|
||||||
springdoc:
|
springdoc:
|
||||||
# SwaggerUI must run on the same port as the API itself, otherwise CORS will block accessing the API
|
# SwaggerUI must run on the same port as the API itself, otherwise CORS will block accessing the API
|
||||||
use-management-port: false
|
x-use-management-port: false
|
||||||
|
|
||||||
hsadminng:
|
hsadminng:
|
||||||
postgres:
|
postgres:
|
||||||
@ -72,3 +72,9 @@ metrics:
|
|||||||
http:
|
http:
|
||||||
server:
|
server:
|
||||||
requests: true
|
requests: true
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
springframework:
|
||||||
|
security: TRACE
|
||||||
|
@ -11,7 +11,9 @@ import com.tngtech.archunit.lang.ArchRule;
|
|||||||
import com.tngtech.archunit.lang.ConditionEvents;
|
import com.tngtech.archunit.lang.ConditionEvents;
|
||||||
import com.tngtech.archunit.lang.SimpleConditionEvent;
|
import com.tngtech.archunit.lang.SimpleConditionEvent;
|
||||||
import io.micrometer.core.annotation.Timed;
|
import io.micrometer.core.annotation.Timed;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
|
import net.hostsharing.hsadminng.config.NoSecurityRequirement;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRbacEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRbacEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
import net.hostsharing.hsadminng.rbac.context.ContextBasedTest;
|
||||||
@ -352,6 +354,15 @@ public class ArchitectureTest {
|
|||||||
static final ArchRule restControllerNaming =
|
static final ArchRule restControllerNaming =
|
||||||
classes().that().areAnnotatedWith(RestController.class).should().haveSimpleNameEndingWith("Controller");
|
classes().that().areAnnotatedWith(RestController.class).should().haveSimpleNameEndingWith("Controller");
|
||||||
|
|
||||||
|
@ArchTest
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static final ArchRule restControllerSecurityRequirement =
|
||||||
|
// TODO.impl: seems that the Spring templates for the OpenAPI generator don't support this,
|
||||||
|
// thus we need this annotation to support Swagger UI authorization.
|
||||||
|
classes().that().areAnnotatedWith(RestController.class).should()
|
||||||
|
.beAnnotatedWith(SecurityRequirement.class).orShould()
|
||||||
|
.beAnnotatedWith(NoSecurityRequirement.class);
|
||||||
|
|
||||||
@ArchTest
|
@ArchTest
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
static final ArchRule restControllerMethods = classes()
|
static final ArchRule restControllerMethods = classes()
|
||||||
|
@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server=http://localhost:8088/cas"})
|
@TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server=http://localhost:8088"})
|
||||||
@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile!
|
@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile!
|
||||||
@Tag("generalIntegrationTest")
|
@Tag("generalIntegrationTest")
|
||||||
class CasAuthenticationFilterIntegrationTest {
|
class CasAuthenticationFilterIntegrationTest {
|
||||||
@ -40,7 +40,7 @@ class CasAuthenticationFilterIntegrationTest {
|
|||||||
public void shouldAcceptRequestWithValidCasTicket() {
|
public void shouldAcceptRequestWithValidCasTicket() {
|
||||||
// given
|
// given
|
||||||
final var username = "test-user-" + randomAlphanumeric(4);
|
final var username = "test-user-" + randomAlphanumeric(4);
|
||||||
wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=valid"))
|
wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=ST-valid"))
|
||||||
.willReturn(aResponse()
|
.willReturn(aResponse()
|
||||||
.withStatus(200)
|
.withStatus(200)
|
||||||
.withBody("""
|
.withBody("""
|
||||||
@ -56,7 +56,7 @@ class CasAuthenticationFilterIntegrationTest {
|
|||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/ping",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
new HttpEntity<>(null, headers("Authorization", "valid")),
|
new HttpEntity<>(null, headers("Authorization", "ST-valid")),
|
||||||
String.class
|
String.class
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ class CasAuthenticatorUnitTest {
|
|||||||
|
|
||||||
// given
|
// given
|
||||||
final var request = mock(HttpServletRequest.class);
|
final var request = mock(HttpServletRequest.class);
|
||||||
given(request.getHeader("current-subject")).willReturn("given-user");
|
// bypassing the CAS-server HTTP-request fakes the user from the authorization header's fake CAS-ticket
|
||||||
|
given(request.getHeader("authorization")).willReturn("Bearer given-user");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var userName = casAuthenticator.authenticate(request);
|
final var userName = casAuthenticator.authenticate(request);
|
||||||
|
@ -20,13 +20,15 @@ import org.springframework.test.context.TestPropertySource;
|
|||||||
|
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
|
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.post;
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server=http://localhost:8088/cas"})
|
@TestPropertySource(properties = {"management.port=0", "server.port=0", "hsadminng.cas.server=http://localhost:8088"})
|
||||||
@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile!
|
@ActiveProfiles("wiremock") // IMPORTANT: To test prod config, do not use test profile!
|
||||||
@Tag("generalIntegrationTest")
|
@Tag("generalIntegrationTest")
|
||||||
class WebSecurityConfigIntegrationTest {
|
class WebSecurityConfigIntegrationTest {
|
||||||
@ -59,20 +61,55 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void accessToApiWithValidTokenShouldBePermitted() {
|
void accessToApiWithValidServiceTicketSouldBePermitted() {
|
||||||
// given
|
// given
|
||||||
givenCasTicketValidationResponse("fake-cas-ticket");
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
// http request
|
// http request
|
||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/ping",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
httpHeaders(entry("Authorization", "fake-cas-ticket")),
|
httpHeaders(entry("Authorization", "Bearer ST-fake-cas-ticket")),
|
||||||
String.class
|
String.class
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(result.getBody()).startsWith("pong fake-cas-ticket");
|
assertThat(result.getBody()).startsWith("pong fake-user-name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void accessToApiWithValidTicketGrantingTicketShouldBePermitted() {
|
||||||
|
// given
|
||||||
|
givenCasServiceTicketForTicketGrantingTicket("TGT-fake-cas-ticket", "ST-fake-cas-ticket");
|
||||||
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
|
// http request
|
||||||
|
final var result = restTemplate.exchange(
|
||||||
|
"http://localhost:" + this.serverPort + "/api/ping",
|
||||||
|
HttpMethod.GET,
|
||||||
|
httpHeaders(entry("Authorization", "Bearer TGT-fake-cas-ticket")),
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
|
assertThat(result.getBody()).startsWith("pong fake-user-name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void accessToApiWithInvalidTicketGrantingTicketShouldBePermitted() {
|
||||||
|
// given
|
||||||
|
givenCasServiceTicketForTicketGrantingTicket("TGT-fake-cas-ticket", "ST-fake-cas-ticket");
|
||||||
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
|
// http request
|
||||||
|
final var result = restTemplate.exchange(
|
||||||
|
"http://localhost:" + this.serverPort + "/api/ping",
|
||||||
|
HttpMethod.GET,
|
||||||
|
httpHeaders(entry("Authorization", "Bearer TGT-WRONG-cas-ticket")),
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -85,13 +122,13 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void accessToApiWithInvalidTokenShouldBeDenied() {
|
void accessToApiWithInvalidTokenShouldBeDenied() {
|
||||||
// given
|
// given
|
||||||
givenCasTicketValidationResponse("fake-cas-ticket");
|
givenCasTicketValidationResponse("ST-fake-cas-ticket", "fake-user-name");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = restTemplate.exchange(
|
final var result = restTemplate.exchange(
|
||||||
"http://localhost:" + this.serverPort + "/api/ping",
|
"http://localhost:" + this.serverPort + "/api/ping",
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
httpHeaders(entry("Authorization", "WRONG-cas-ticket")),
|
httpHeaders(entry("Authorization", "Bearer ST-WRONG-cas-ticket")),
|
||||||
String.class
|
String.class
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -129,17 +166,25 @@ class WebSecurityConfigIntegrationTest {
|
|||||||
assertThat(result.getBody().get("status")).isEqualTo("UP");
|
assertThat(result.getBody().get("status")).isEqualTo("UP");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void givenCasTicketValidationResponse(final String casToken) {
|
private void givenCasServiceTicketForTicketGrantingTicket(final String ticketGrantingTicket, final String serviceTicket) {
|
||||||
|
wireMockServer.stubFor(post(urlEqualTo("/cas/v1/tickets/" + ticketGrantingTicket))
|
||||||
|
.withFormParam("service", equalTo(serviceUrl))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withStatus(201)
|
||||||
|
.withBody(serviceTicket)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void givenCasTicketValidationResponse(final String casToken, final String userName) {
|
||||||
wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=" + casToken))
|
wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=" + casToken))
|
||||||
.willReturn(aResponse()
|
.willReturn(aResponse()
|
||||||
.withStatus(200)
|
.withStatus(200)
|
||||||
.withBody("""
|
.withBody("""
|
||||||
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
|
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
|
||||||
<cas:authenticationSuccess>
|
<cas:authenticationSuccess>
|
||||||
<cas:user>${casToken}</cas:user>
|
<cas:user>${userName}</cas:user>
|
||||||
</cas:authenticationSuccess>
|
</cas:authenticationSuccess>
|
||||||
</cas:serviceResponse>
|
</cas:serviceResponse>
|
||||||
""".replace("${casToken}", casToken))));
|
""".replace("${userName}", userName))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
|
@ -42,7 +42,7 @@ spring:
|
|||||||
# keep this in sync with main/.../application.yml
|
# keep this in sync with main/.../application.yml
|
||||||
springdoc:
|
springdoc:
|
||||||
# SwaggerUI must run on the same port as the API itself, otherwise CORS will block accessing the API
|
# SwaggerUI must run on the same port as the API itself, otherwise CORS will block accessing the API
|
||||||
use-management-port: false
|
x-use-management-port: false
|
||||||
|
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user