Added authentication for Swagger ui, added extended documentation on description queries endpoint

This commit is contained in:
Thomas Georgios Giannos 2024-05-31 16:05:27 +03:00
parent f05962a3b2
commit 37243dba2a
3 changed files with 182 additions and 5 deletions

View File

@ -0,0 +1,49 @@
package org.opencdmp.configurations;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenAPISecurityConfig {
@Value("${keycloak-client.serverUrl}")
String authServerUrl;
@Value("${keycloak-client.realm}")
String realm;
private static final String OAUTH_SCHEME_NAME = "oAuth_security_schema";
@Bean
public OpenAPI openAPI() {
Components oauthComponent = new Components();
oauthComponent.addSecuritySchemes(OAUTH_SCHEME_NAME, createOAuthScheme());
OpenAPI openAPI = new OpenAPI();
openAPI.components(oauthComponent);
openAPI.addSecurityItem(new SecurityRequirement().addList(OAUTH_SCHEME_NAME));
return openAPI;
}
private SecurityScheme createOAuthScheme() {
OAuthFlows flows = createOAuthFlows();
return new SecurityScheme().type(SecurityScheme.Type.OAUTH2)
.flows(flows);
}
private OAuthFlows createOAuthFlows() {
OAuthFlow flow = createAuthorizationCodeFlow();
return new OAuthFlows().implicit(flow);
}
private OAuthFlow createAuthorizationCodeFlow() {
return new OAuthFlow()
.authorizationUrl(authServerUrl + "/realms/" + realm + "/protocol/openid-connect/auth")
.scopes(new Scopes().addString("read_access", "read data")
.addString("write_access", "modify data"));
}
}

View File

@ -11,6 +11,7 @@ import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidationFilterAnnotation;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.xml.bind.JAXBException;
@ -108,6 +109,7 @@ public class DescriptionController {
@PostMapping("public/query")
@Operation(summary = "Query public descriptions")
@Hidden
public QueryResult<PublicDescription> publicQuery(@RequestBody DescriptionLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", PublicDescription.class.getSimpleName());
@ -123,6 +125,7 @@ public class DescriptionController {
@GetMapping("public/{id}")
@Operation(summary = "Fetch a specific public description by id")
@Hidden
public PublicDescription publicGet(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("retrieving" + PublicDescription.class.getSimpleName()).And("id", id).And("fields", fieldSet));
fieldSet = this.fieldSetExpanderService.expand(fieldSet);
@ -143,7 +146,7 @@ public class DescriptionController {
}
@PostMapping("query")
@Operation(summary = "Query all descriptions")
@Operation(summary = "Query all descriptions", description = SwaggerHelpers.endpoint_query)
public QueryResult<Description> query(@RequestBody DescriptionLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", Description.class.getSimpleName());
@ -216,6 +219,7 @@ public class DescriptionController {
@PostMapping("get-description-section-permissions")
@Operation(summary = "Fetch the section specific user permissions")
@Hidden
@ValidationFilterAnnotation(validator = DescriptionSectionPermissionResolver.DescriptionSectionPermissionResolverPersistValidator.ValidatorName, argumentName = "model")
public Map<UUID, List<String>> getDescriptionSectionPermissions(@RequestBody DescriptionSectionPermissionResolver model) {
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model));
@ -230,6 +234,7 @@ public class DescriptionController {
@GetMapping("validate")
@Operation(summary = "Validate if a description is ready for finalization by id")
@Hidden
public List<DescriptionValidationResult> validate(@RequestParam("descriptionIds") List<UUID> descriptionIds) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("validating" + Description.class.getSimpleName()).And("descriptionIds", descriptionIds));
@ -331,4 +336,125 @@ public class DescriptionController {
));
return response;
}
protected static class SwaggerHelpers {
static final String endpoint_query =
"""
This endpoint is used to fetch all the available descriptions.<br/>
It also allows to restrict the results using a query object passed in the request body.<br/>
Let's explore the options this object gives us.
### <u>General query parameters:</u>
<ul>
<li><b>page:</b>
This is an object controlling the pagination of the results. It contains two properties.
</li>
<ul>
<li><b>offset:</b>
How many records to omit.
</li>
<li><b>size:</b>
How many records to include in each page.
</li>
</ul>
</ul>
For example, if we want the third page, and our pages to contain 15 elements, we would pass the following object:
```JSON
{
"offset": 30,
"size": 15
}
```
<ul>
<li><b>order:</b>
This is an object controlling the ordering of the results.
It contains a list of strings called <i>items</i> with the names of the properties to use.
<br/>If the name of the property is prefixed with a <b>'-'</b>, the ordering direction is <b>DESC</b>. Otherwise, it is <b>ASC</b>.
</li>
</ul>
For example, if we wanted to order based on the field 'createdAt' in descending order, we would pass the following object:
```JSON
{
"items": [
"-createdAt"
],
}
```
<ul>
<li><b>metadata:</b>
This is an object containing metadata for the request. There is only one available option.
<ul>
<li><b>countAll:</b>
If this is set to true, the count property included in the response will account for all the records regardless the pagination,
with all the rest of filtering options applied of course.
Otherwise, if it is set to false or not present, only the returned results will be counted.
<br/>The first option is useful for the UI clients to calculate how many result pages are available.
</li>
</ul>
</li>
<li><b>project:</b>
This is an object controlling the data projection of the results.
It contains a list of strings called <i>fields</i> with the names of the properties to project.
<br/>You can also include properties that are deeper in the object tree by prefixing them with dots.
</li>
</ul>
### <u>Description specific query parameters:</u>
<ul>
<li><b>like:</b>
If there is a like parameter present in the query, only the description entities that include the contents of the parameter either in their labels or the descriptions will be in the response.
</li>
<li><b>ids:</b>
This is a list and contains the ids we want to include in the response. <br/>If empty, every record is included.
</li>
<li><b>excludedIds:</b>
This is a list and contains the ids we want to exclude from the response. <br/>If empty, no record gets excluded.
</li>
<li><b>isActive:</b>
This is a list and determines which records we want to include in the response, based on if they are deleted or not.
This filter works like this. If we want to view only the active records we pass [1] and for only the deleted records we pass [0].
<br/>If not present or if we pass [0,1], every record is included.
</li>
<li><b>statuses:</b>
This is a list and determines which records we want to include in the response, based on their status.
The status can be <i>Draft</i>, <i>Finalized</i> or <i>Canceled</i>. We add 0, 1 or 2 to the list respectively.
<br/>If not present, every record is included.
</li>
<li><b>createdAfter:</b>
This is a date and determines which records we want to include in the response, based on their creation date.
Specifically, only the records created after the given date are included.
<br/>If not present, every record is included.
</li>
<li><b>createdBefore:</b>
This is a date and determines which records we want to include in the response, based on their creation date.
Specifically, only the records created before the given date are included.
<br/>If not present, every record is included.
</li>
<li><b>finalizedAfter:</b>
This is a date and determines which records we want to include in the response, based on their finalization date.
Specifically, only the records finalized after the given date are included.
<br/>If not present, every record is included.
</li>
<li><b>finalizedBefore:</b>
This is a date and determines which records we want to include in the response, based on their finalization date.
Specifically, only the records finalized before the given date are included.
<br/>If not present, every record is included.
</li>
</ul>
""";
static final String endpoint_ =
"""
""";
}
}

View File

@ -4,15 +4,17 @@ springdoc:
enabled: true
groupConfigs:
- group: public-api
displayName: Public API
displayName: Legacy
packagesToScan: org.opencdmp.controllers.publicapi
pathsToMatch: "/api/public/dmps/**, /api/public/datasets/**"
- group: internal-api
displayName: Internal API
displayName: Current
packagesToScan: org.opencdmp.controllers
packagesToExclude: org.opencdmp.controllers.publicapi
pathsToMatch: "/api/dmp/**, /api/description/**"
swagger-ui:
swaggerUi:
enabled: true
useRootPath: true
docExpansion: none
docExpansion: none
oauth:
clientId: dmp_swagger