Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit df08b063 authored by Flavio Ferraioli's avatar Flavio Ferraioli
Browse files

Merge branch 'release' into 'main'

release 0.8.0

See merge request !89
parents 18675ae0 3f376656
Branches
Tags v0.8.0
2 merge requests!90Align to main and update version,!89release 0.8.0
Pipeline #240234 passed
Showing
with 1206 additions and 3 deletions
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.0] - 2024-12-02
### Added
- Added seeding component for identity attributes.
- Added documentation for seeding configuration.
- Added `enabled` and `filePath` properties to configure seeding procedure.
- Added column `is_right` to table `identity_attribute`.
### Changed
- Renamed attribute `participantType` to `participantTypes` in `SecurityAttributesMappingDTO`.
- Column `is_right` of `identity_attribute` is not nullable anymore and has "false" as default value.
### Fixed
- Fix unit testing.
## [0.7.0] - 2024-11-11
### Changed
- Adapt logic to use `CredentialId` instead of `ParticipantId`
### Fixed
- Fix `credentialId` instead of `certificateId`
### Removed
- Remove `participantType` enum
......@@ -26,6 +26,13 @@ The environment variables listed below are used to define the connection details
- `MICROSERVICE_ONBOARDING_URL`: The URL for the Onboarding microservice.
- Value is derived from `microservices.onboardingUrl` specified in the Helm values.
### Identity Attribute Initialization Configuration
- `DATABASESEEDING_SECURITYATTRIBUTEPROVIDERMAPPING_ENABLED`: Toggles the initialization of identity attributes.
- Value is derived from `databaseSeeding.securityAttributeProviderMapping.enabled` specified in the Helm values. **Default: true**.
- `DATABASESEEDING_SECURITYATTRIBUTEPROVIDERMAPPING_FILEPATH`: Specifies the file path to the JSON configuration file used for identity attribute initialization.
- Value is derived from `databaseSeeding.securityAttributeProviderMapping.filePath` specified in the Helm values. **Default: classpath:db.seeding/identityAttributes.default.json**.
## Usage
To use these environment variables, define them in your Kubernetes manifests or Helm chart. The values for URLs and passwords will be dynamically generated based on the namespace and other configuration values.
......@@ -51,4 +58,9 @@ microservices:
identityProviderUrl: http://identity-provider.{{ .Release.Namespace }}.svc.cluster.local:8080
onboardingUrl: http://onboarding.{{ .Release.Namespace }}.svc.cluster.local:8080
databaseSeeding:
securityAttributeProviderMapping:
enabled: true
filePath: "classpath:db.seeding/identityAttributes.default.json OR file:///my/custom/path/file.json"
```
......@@ -8,4 +8,9 @@ data:
SPRING_DATASOURCE_PASSWORD: "{{ .Values.db.password }}"
MICROSERVICE_IDENTITY_PROVIDER_URL: "{{ .Values.microservices.identityProviderUrl }}"
MICROSERVICE_ONBOARDING_URL: "{{ .Values.microservices.onboardingUrl }}"
{{- if ne ((.Values.databaseSeeding).securityAttributeProviderMapping).enabled nil }}
DATABASESEEDING_SECURITYATTRIBUTEPROVIDERMAPPING_ENABLED: "{{ ((.Values.databaseSeeding).securityAttributeProviderMapping).enabled }}"
{{- end }}
{{- with ((.Values.databaseSeeding).securityAttributeProviderMapping).filePath }}
DATABASESEEDING_SECURITYATTRIBUTEPROVIDERMAPPING_FILEPATH: "{{ . }}"
{{- end }}
......@@ -118,3 +118,8 @@ db:
microservices:
identityProviderUrl: http://identity-provider.{{ .Release.Namespace }}.svc.cluster.local:8080
onboardingUrl: http://onboarding.{{ .Release.Namespace }}.svc.cluster.local:8080
# databaseSeeding:
# securityAttributeProviderMapping:
# enabled: true
# filePath: "classpath:db.seeding/identityAttributes.default.json"
This diff is collapsed.
PROJECT_VERSION_NUMBER="0.7.0"
PROJECT_VERSION_NUMBER="0.8.0"
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.aruba.simpl</groupId>
<artifactId>simpl-parent</artifactId>
<version>0.7.0</version>
<version>0.8.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
......
package com.aruba.simpl.securityattributesprovider.configurations;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "database-seeding")
public record DBSeedingProperties(SecurityAttributeProviderMapping securityAttributeProviderMapping) {
public record SecurityAttributeProviderMapping(String filePath, boolean enabled) {}
}
package com.aruba.simpl.securityattributesprovider.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class SecurityAttributesMappingDTO {
@NotBlank
private String name;
@NotBlank
private String code;
@NotBlank
private String description;
@NotNull private Boolean assignableToRoles;
@NotNull @JsonProperty("isRight")
@NotNull private Boolean right;
@NotNull private List<String> participantTypes = new ArrayList<>();
}
......@@ -47,6 +47,9 @@ public class IdentityAttribute {
@Column(name = "enabled")
private boolean enabled;
@Column(name = "is_right")
private boolean right;
@CreationTimestamp
@Column(name = "creation_timestamp")
private Instant creationTimestamp;
......
......@@ -2,6 +2,7 @@ package com.aruba.simpl.securityattributesprovider.model.mappers;
import com.aruba.simpl.common.model.dto.IdentityAttributeDTO;
import com.aruba.simpl.common.model.dto.IdentityAttributeWithOwnershipDTO;
import com.aruba.simpl.securityattributesprovider.model.dto.SecurityAttributesMappingDTO;
import com.aruba.simpl.securityattributesprovider.model.entities.IdentityAttribute;
import com.aruba.simpl.securityattributesprovider.model.projection.IdentityAttributeWithOwnership;
import org.mapstruct.*;
......@@ -9,6 +10,13 @@ import org.mapstruct.*;
@Mapper
public interface IdentityAttributeMapper {
@Mapping(target = "enabled", constant = "true")
@Mapping(target = "id", ignore = true)
@Mapping(target = "participants", ignore = true)
@Mapping(target = "creationTimestamp", ignore = true)
@Mapping(target = "updateTimestamp", ignore = true)
IdentityAttribute toEntity(SecurityAttributesMappingDTO attribute);
IdentityAttribute toEntity(IdentityAttributeDTO dto);
IdentityAttributeDTO toDto(IdentityAttribute entity);
......
package com.aruba.simpl.securityattributesprovider.services;
public interface SecurityAttributeProviderInitialization {
void init();
}
package com.aruba.simpl.securityattributesprovider.services.impl;
import com.aruba.simpl.common.exchanges.onboarding.ParticipantTypeExchange;
import com.aruba.simpl.common.model.dto.ParticipantTypeDTO;
import com.aruba.simpl.securityattributesprovider.configurations.DBSeedingProperties;
import com.aruba.simpl.securityattributesprovider.model.dto.SecurityAttributesMappingDTO;
import com.aruba.simpl.securityattributesprovider.model.mappers.*;
import com.aruba.simpl.securityattributesprovider.model.repositories.IdentityAttributeRepository;
import com.aruba.simpl.securityattributesprovider.services.SecurityAttributeProviderInitialization;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
@Log4j2
@Service
public class SecurityAttributeProviderInitializationImpl implements SecurityAttributeProviderInitialization {
private final DBSeedingProperties props;
private final ResourceLoader resourceLoader;
private final Validator validator;
private final IdentityAttributeRepository identityAttributeRepository;
private final IdentityAttributeMapper identityAttributeMapper;
private final ParticipantTypeExchange participantTypeExchange;
public SecurityAttributeProviderInitializationImpl(
DBSeedingProperties props,
ResourceLoader resourceLoader,
Validator validator,
IdentityAttributeRepository identityAttributeRepository,
IdentityAttributeMapper identityAttributeMapper,
ParticipantTypeExchange participantTypeExchange) {
this.props = props;
this.resourceLoader = resourceLoader;
this.validator = validator;
this.identityAttributeRepository = identityAttributeRepository;
this.identityAttributeMapper = identityAttributeMapper;
this.participantTypeExchange = participantTypeExchange;
}
@PostConstruct
public void init() {
if (props.securityAttributeProviderMapping().enabled()) {
if (identityAttributeRepository.findAll().isEmpty()) {
try {
var attributes = getSecurityAttributeProviderInitData();
validateAttributes(attributes);
var validParticipantTypes = getParticipantTypesFromOnboarding();
validateParticipantTypes(attributes, validParticipantTypes);
storeIdentityAttributes(attributes);
} catch (IOException e) {
log.error("Unable to process attributes", e);
}
log.info("End initialization attributes");
} else {
log.info("Identity attribute respository is not empty. Skip initialization process");
}
} else {
log.info("Skip initialization identity attributes");
}
}
private List<String> getParticipantTypesFromOnboarding() {
log.info("Retrieve participantTypes from onboarding microservices");
return participantTypeExchange.getParticipantTypes().stream()
.map(ParticipantTypeDTO::getValue)
.toList();
}
private void validateParticipantTypes(
List<SecurityAttributesMappingDTO> attributes, List<String> validParticipantTypes) {
log.info("Validation participant types");
attributes.forEach(attribute -> attribute.getParticipantTypes().forEach(participantType -> {
if (!validParticipantTypes.contains(participantType)) {
log.error("Invalid participant type {} in attribute {}", participantType, attribute.getName());
throw new RuntimeException("Invalid participant type");
}
}));
}
private List<SecurityAttributesMappingDTO> getSecurityAttributeProviderInitData() throws IOException {
var filePath = this.props.securityAttributeProviderMapping().filePath();
try (InputStream is = resourceLoader.getResource(filePath).getInputStream()) {
var mappings = new ObjectMapper().readValue(is, new TypeReference<List<SecurityAttributesMappingDTO>>() {});
log.info("Reading identity attributes from file system mappings from file system. File: {}", filePath);
return mappings;
} catch (IOException e) {
log.error("Error reading mappings from file: {}", filePath);
throw e;
}
}
private void validateAttributes(List<SecurityAttributesMappingDTO> mappings) {
log.info("Start validation security attributes");
mappings.forEach(mapping -> {
var violations = this.validator.validate(mapping);
if (!violations.isEmpty()) {
violations.forEach(violation ->
log.error("Validation failed for mapping: {} - {}", mapping, violation.getMessage()));
throw new ConstraintViolationException(violations);
}
});
}
private void storeIdentityAttributes(List<SecurityAttributesMappingDTO> attributes) {
log.info("Start store all identity attributes");
attributes.forEach(this::storeIdentityAttribute);
}
private void storeIdentityAttribute(SecurityAttributesMappingDTO attribute) {
log.info("Initialize attribute {}", attribute);
var attributeEntity = identityAttributeMapper.toEntity(attribute);
identityAttributeRepository.save(attributeEntity);
log.info("Read attribute {}", attributeEntity.getId());
}
}
......@@ -21,3 +21,8 @@ microservice:
url: [IDENTITY_PROVIDER_URL]
onboarding:
url: [ONBOARDING_URL]
database-seeding:
securityAttributeProviderMapping:
enabled: true
filePath: "classpath:db.seeding/identityAttributes.default.json"
[
{
"name": "Consumer",
"code": "CONSUMER",
"participantTypes": [
"CONSUMER"
],
"description": "Identity attribute used for tagging an end user able to act as a user of a data consumer participant",
"assignableToRoles": false,
"isRight": false
},
{
"name": "Data Provider",
"code": "DATA_PROVIDER",
"participantTypes": [
"DATA_PROVIDER"
],
"description": "Identity attribute used to tag the data provider",
"assignableToRoles": false,
"isRight": false
},
{
"name": "Application Provider",
"code": "APP_PROVIDER",
"participantTypes": [
"APPLICATION_PROVIDER"
],
"description": "Identity attribute used to tag the application provider",
"assignableToRoles": false,
"isRight": false
},
{
"name": "Infrastructure Provider",
"code": "INFRA_PROVIDER",
"participantTypes": [
"INFRASTRUCTURE_PROVIDER"
],
"description": "Identity attribute used to tag the infrastructure provider",
"assignableToRoles": false,
"isRight": false
},
{
"name": "Data Provider Publisher",
"code": "DATA_PROVIDER_PUBLISHER",
"participantTypes": [
"DATA_PROVIDER"
],
"description": "Identity attribute needed for publishing Data Catalogue",
"assignableToRoles": true,
"isRight": true
},
{
"name": "Application Provider Publisher",
"code": "APP_PROVIDER_PUBLISHER",
"participantTypes": [
"APPLICATION_PROVIDER"
],
"description": "Identity attribute needed for publishing Application Catalogue",
"assignableToRoles": true,
"isRight": true
},
{
"name": "Infrastructure Provider Publisher",
"code": "INFRA_PROVIDER_PUBLISHER",
"participantTypes": [
"INFRASTRUCTURE_PROVIDER"
],
"description": "Identity attribute needed for publishing Infrastructure",
"assignableToRoles": true,
"isRight": true
}
]
databaseChangeLog:
- changeSet:
id: 0
author: Lorenzo Gandino
changes:
- addColumn:
tableName: identity_attribute
columns:
- column:
name: is_right
type: BOOLEAN
defaultValue: "false"
package com.aruba.simpl.securityattributesprovider.services.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
import com.aruba.simpl.common.exchanges.onboarding.ParticipantTypeExchange;
import com.aruba.simpl.common.model.dto.ParticipantTypeDTO;
import com.aruba.simpl.securityattributesprovider.configurations.DBSeedingProperties;
import com.aruba.simpl.securityattributesprovider.model.entities.IdentityAttribute;
import com.aruba.simpl.securityattributesprovider.model.mappers.IdentityAttributeMapperImpl;
import com.aruba.simpl.securityattributesprovider.model.repositories.IdentityAttributeRepository;
import jakarta.validation.Validator;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@MockBean({
Validator.class,
})
@EnableConfigurationProperties(DBSeedingProperties.class)
@TestPropertySource(
properties = {
"database-seeding.securityAttributeProviderMapping.enabled=true",
"database-seeding.securityAttributeProviderMapping.filePath=db.seeding/identityAttributes.default.json",
})
@Import(IdentityAttributeMapperImpl.class)
public class SecurityAttributeProviderInitializationImplTest {
@Autowired
private ApplicationContext applicationContext;
@MockBean
private IdentityAttributeRepository identityAttributeRepository;
@MockBean
private ParticipantTypeExchange participantTypeExchange;
@Test
public void initDatabaseTest_success() {
var arguments = ArgumentCaptor.forClass(IdentityAttribute.class);
var validParticipantTypes = getValidParticipantTypes();
when(participantTypeExchange.getParticipantTypes()).thenReturn(validParticipantTypes);
initializationSecurityAttributes();
Mockito.verify(this.identityAttributeRepository, times(7)).save(arguments.capture());
assertEquals(7, arguments.getAllValues().size());
var ia = arguments.getAllValues().getFirst();
assertEquals("Consumer", ia.getName());
assertEquals("CONSUMER", ia.getCode());
assertEquals(
"Identity attribute used for tagging an end user able to act as a user of a data consumer participant",
ia.getDescription());
assertEquals(false, ia.isAssignableToRoles());
assertEquals(false, ia.isRight());
assertEquals("CONSUMER", ia.getParticipantTypes().iterator().next());
ia = arguments.getAllValues().getLast();
assertEquals("Infrastructure Provider Publisher", ia.getName());
assertEquals("INFRA_PROVIDER_PUBLISHER", ia.getCode());
assertEquals("Identity attribute needed for publishing Infrastructure", ia.getDescription());
assertEquals(true, ia.isAssignableToRoles());
assertEquals(true, ia.isRight());
assertEquals(
"INFRASTRUCTURE_PROVIDER", ia.getParticipantTypes().iterator().next());
}
@Test
public void initDatabaseTest_InvalidParticipantType_ThrowException() {
var validParticipantTypes = Set.of(createParticipantTypeDTO("JUNIT_TEST_VALUE"));
when(participantTypeExchange.getParticipantTypes()).thenReturn(validParticipantTypes);
assertThrows(Exception.class, () -> initializationSecurityAttributes());
}
@Test
public void initDatabaseTest_TableIsNoEmpy_SkipInitialization() {
var mockIdentityAttribute = new IdentityAttribute();
when(identityAttributeRepository.findAll()).thenReturn(List.of(mockIdentityAttribute));
initializationSecurityAttributes();
verify(this.identityAttributeRepository, never()).save(any());
}
private void initializationSecurityAttributes() {
try (var testContext = new AnnotationConfigApplicationContext()) {
testContext.setParent(applicationContext);
testContext.register(SecurityAttributeProviderInitializationImpl.class);
testContext.refresh();
}
}
private Set<ParticipantTypeDTO> getValidParticipantTypes() {
return Set.of(
createParticipantTypeDTO("APPLICATION_PROVIDER"),
createParticipantTypeDTO("CONSUMER"),
createParticipantTypeDTO("DATA_PROVIDER"),
createParticipantTypeDTO("INFRASTRUCTURE_PROVIDER"));
}
private ParticipantTypeDTO createParticipantTypeDTO(String value) {
var pt = new ParticipantTypeDTO();
pt.setValue(value);
return pt;
}
}
database-seeding:
securityAttributeProviderMapping:
enabled: false
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment