From d4cdef37950d26e0cbf4c6ec1ca248c1d1c1085e Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 12 Jun 2024 14:47:56 +0200 Subject: [PATCH 01/50] wip commit --- build.gradle | 13 +- src/main/java/no/fintlabs/Application.java | 4 + ...entAuthorizationProducerRecordBuilder.java | 10 +- .../user/UserPermissionRepository.java | 13 -- .../authorization/AuthorizationUtil.java | 2 +- .../authorization/adminuser/AdminUser.java | 2 +- .../adminuser/AdminUserController.java | 22 ++- .../authorization/user/UserPermission.java | 2 +- .../user/UserPermissionController.java | 4 +- .../authorization/user/UserPermissionDto.java | 2 +- .../user/UserPermissionRepository.java | 25 ++++ .../user/UserPermissionService.java | 53 +++++++ .../java/no/fintlabs/flyt/azure/AppRoles.java | 12 ++ .../flyt/azure/AzureADUserSyncService.java | 141 ++++++++++++++++++ .../no/fintlabs/flyt/azure/AzureGroup.java | 22 +++ .../fintlabs/flyt/azure/AzureRoleService.java | 141 ++++++++++++++++++ .../fintlabs/flyt/azure/AzureUserCache.java | 11 ++ .../flyt/azure/AzureUserCacheRepository.java | 27 ++++ .../java/no/fintlabs/flyt/azure/Config.java | 61 ++++++++ .../no/fintlabs/flyt/azure/ConfigUser.java | 24 +++ .../AcosSourceApplication.java | 2 +- .../DigisakSourceApplication.java | 2 +- .../EgrunnervervSourceApplication.java | 2 +- .../VigoSourceApplication.java | 2 +- .../resources/application-flyt-azure.yaml | 33 ++++ .../resources/application-local-staging.yaml | 7 +- src/main/resources/application.yaml | 1 + ...uthorizationProducerRecordBuilderTest.java | 6 +- 28 files changed, 606 insertions(+), 40 deletions(-) delete mode 100644 src/main/java/no/fintlabs/authorization/user/UserPermissionRepository.java rename src/main/java/no/fintlabs/{ => flyt}/authorization/AuthorizationUtil.java (90%) rename src/main/java/no/fintlabs/{ => flyt}/authorization/adminuser/AdminUser.java (70%) rename src/main/java/no/fintlabs/{ => flyt}/authorization/adminuser/AdminUserController.java (85%) rename src/main/java/no/fintlabs/{ => flyt}/authorization/user/UserPermission.java (94%) rename src/main/java/no/fintlabs/{ => flyt}/authorization/user/UserPermissionController.java (95%) rename src/main/java/no/fintlabs/{ => flyt}/authorization/user/UserPermissionDto.java (89%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AppRoles.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureGroup.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/Config.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/ConfigUser.java rename src/main/java/no/fintlabs/{ => flyt}/models/sourceapplication/AcosSourceApplication.java (89%) rename src/main/java/no/fintlabs/{ => flyt}/models/sourceapplication/DigisakSourceApplication.java (89%) rename src/main/java/no/fintlabs/{ => flyt}/models/sourceapplication/EgrunnervervSourceApplication.java (89%) rename src/main/java/no/fintlabs/{ => flyt}/models/sourceapplication/VigoSourceApplication.java (89%) create mode 100644 src/main/resources/application-flyt-azure.yaml diff --git a/build.gradle b/build.gradle index 3df25a3..4e7d841 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-webflux' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.kafka:spring-kafka' implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' @@ -38,11 +39,19 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'org.postgresql:postgresql' - implementation 'org.flywaydb:flyway-core' + implementation 'org.flywaydb:flyway-core' implementation 'no.fintlabs:fint-kafka:4.0.1' + implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0' + +// implementation 'com.microsoft.graph:microsoft-graph:6.11.0' +// implementation 'com.azure:azure-identity:1.12.1' + + implementation 'com.azure:azure-identity:1.10.2' + implementation 'com.microsoft.graph:microsoft-graph:5.80.0' - implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0-rc-3' +// implementation 'com.azure:azure-identity:1.4.6' +// implementation 'com.microsoft.graph:microsoft-graph:5.9.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'io.micrometer:micrometer-registry-prometheus' diff --git a/src/main/java/no/fintlabs/Application.java b/src/main/java/no/fintlabs/Application.java index 27a753b..4887207 100644 --- a/src/main/java/no/fintlabs/Application.java +++ b/src/main/java/no/fintlabs/Application.java @@ -2,7 +2,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling +@ConfigurationPropertiesScan @SpringBootApplication public class Application { diff --git a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java b/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java index 8bdc9fc..57fb3bf 100644 --- a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java +++ b/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java @@ -2,10 +2,10 @@ import lombok.extern.slf4j.Slf4j; import no.fintlabs.kafka.requestreply.ReplyProducerRecord; -import no.fintlabs.models.sourceapplication.AcosSourceApplication; -import no.fintlabs.models.sourceapplication.EgrunnervervSourceApplication; -import no.fintlabs.models.sourceapplication.DigisakSourceApplication; -import no.fintlabs.models.sourceapplication.VigoSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.AcosSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.DigisakSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.VigoSourceApplication; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.stereotype.Component; @@ -22,10 +22,8 @@ public ReplyProducerRecord apply(ConsumerRecord getGroup(UUID principalId) { +// return this.pageThroughGetGroups( +// graphService.groups() +// .buildRequest() +// .select("id,members")) +// //.filter(String.format("startsWith(displayName,'%s')",configGroup.getPrefix())) +// .expand("members($select=id)") +// .get() +// ); +// } + +// private List pageThroughGetGroups(GroupCollectionPage inPage) { +// int groups = 0; +// GroupCollectionPage page = inPage; +// List retGroupList = new ArrayList(); +// do { +// for (Group group : page.getCurrentPage()) { +// +// AzureGroup newGroup; +// try { +// newGroup = new AzureGroup(group); +// } catch (NumberFormatException e) { +// log.warn("Problems converting resourceID to LONG! {}. Skipping creation of group", e); +// continue; +// } +// retGroupList.add(newGroup); +// } +// if (page.getNextPage() == null) { +// break; +// } else { +// log.debug("Processing group page"); +// page = page.getNextPage().buildRequest().get(); +// } +// } while (page != null); +// log.debug("{} Group objects detected in Microsoft Entra", groups); +// return retGroupList; +// } + + @Scheduled( + initialDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.initial-delay-ms}", + fixedDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.fixed-delay-ms}" + ) + private void pullUsers() { + log.info("*** <<< Starting to pull users from Azure AD >>> ***"); + long startTime = System.currentTimeMillis(); + + try { + UserCollectionPage usersPage = graphService.users() + .buildRequest() + .select(String.join(",", configUser.allAttributes())) + .filter("usertype eq 'member'") + .get(); + + processUsers(usersPage); + } catch (Exception e) { + log.error("Error fetching users : {}", e.getMessage(), e); + } + + long endTime = System.currentTimeMillis(); + long elapsedTimeInSeconds = (endTime - startTime) / 1000; + long minutes = elapsedTimeInSeconds / 60; + long seconds = elapsedTimeInSeconds % 60; + + log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); + } + + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java b/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java new file mode 100644 index 0000000..c9bedb9 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java @@ -0,0 +1,22 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.Group; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +public class AzureGroup { + protected String id; + protected List members; + + public AzureGroup(Group group) { + this.id = group.id; + this.members = new ArrayList<>(); + } +} + + diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java b/src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java new file mode 100644 index 0000000..27c2268 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java @@ -0,0 +1,141 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.*; +import com.microsoft.graph.options.QueryOption; +import com.microsoft.graph.requests.GraphServiceClient; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class AzureRoleService { + + protected final GraphServiceClient graphService; + + public AzureRoleService(GraphServiceClient graphService) { + this.graphService = graphService; + } + + public List getAppRoleAssignments(String appId) { + List requestOptions = new ArrayList<>(); + requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); + List servicePrincipals = Objects.requireNonNull(graphService + .servicePrincipals() + .buildRequest(requestOptions) + .get()) + .getCurrentPage(); + if (!servicePrincipals.isEmpty()) { + ServicePrincipal servicePrincipal = servicePrincipals.get(0); + if (servicePrincipal != null && servicePrincipal.id != null) { + return Objects.requireNonNull(graphService + .servicePrincipals(servicePrincipal.id) + .appRoleAssignedTo() + .buildRequest() + .get()) + .getCurrentPage(); + } + } + return new ArrayList<>(); + } + + public List getAppRoles(String appId) { + List requestOptions = new ArrayList<>(); + requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); + List servicePrincipals = Objects.requireNonNull(graphService + .servicePrincipals() + .buildRequest(requestOptions) + .get()) + .getCurrentPage(); + if (!servicePrincipals.isEmpty()) { + ServicePrincipal servicePrincipal = servicePrincipals.get(0); + if (servicePrincipal != null && servicePrincipal.id != null) { + return servicePrincipal.appRoles; + } + } + return new ArrayList<>(); + } + + public String getRoleName(UUID appRoleId, String appId) { + List appRoles = getAppRoles(appId); + if (appRoles == null) { + log.warn("App roles are null for appId: {}", appId); + return null; + } + for (AppRole appRole : appRoles) { + if (appRole.id != null && appRole.id.equals(appRoleId)) { + return appRole.value; + } + } + log.warn("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); + return null; + } + + public List getGroupMembers(String groupId) { + List members = Objects.requireNonNull(graphService + .groups(groupId) + .members() + .buildRequest() + .get()) + .getCurrentPage(); + + return members.stream() + .filter(directoryObject -> directoryObject instanceof User) + .map(directoryObject -> (User) directoryObject) + .collect(Collectors.toList()); + } + + public List getUserRoles(String userId, String email, String appId) { + List appRoleAssignments = getAppRoleAssignments(appId); + List roles = new ArrayList<>(); + + // Check direct user role assignments + for (AppRoleAssignment assignment : appRoleAssignments) { + + assert assignment.principalType != null; + assert assignment.principalId != null; + String principalType = assignment.principalType; + String principalId = assignment.principalId.toString(); + + if (principalType.equals("User") && principalId.equals(userId)) { + String roleName = getRoleName(assignment.appRoleId, appId); + if (roleName != null) { + roles.add(roleName); + } else { + log.warn("Role name for appRoleId {} not found", assignment.appRoleId); + } + } + } + + // Check if user is in any group that has specific roles + List groupMembers; + for (AppRoleAssignment assignment : appRoleAssignments) { + if (assignment.principalType != null && assignment.principalType.equals("Group")) { + groupMembers = getGroupMembers(String.valueOf(assignment.principalId)); + for (User user : groupMembers) { + if (user.id != null && user.id.equals(userId)) { + String roleName = getRoleName(assignment.appRoleId, appId); + if (roleName != null) { + roles.add(roleName); + } + } + } + } + } + + // Log the roles + if (roles.isEmpty()) { + log.info("User {} has no roles assigned in app {}", email, appId); + return List.of(); + } else { + log.info("User {} has the following roles in app {}: {}", email, appId, roles); + return roles; + } + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java b/src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java new file mode 100644 index 0000000..8c2f7df --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java @@ -0,0 +1,11 @@ +package no.fintlabs.flyt.azure; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class AzureUserCache { + private String objectIdentifier; + private String email; +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java new file mode 100644 index 0000000..e1160c9 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java @@ -0,0 +1,27 @@ +package no.fintlabs.flyt.azure; + +import org.springframework.stereotype.Repository; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Repository +public class AzureUserCacheRepository { + private final Map userCache = new ConcurrentHashMap<>(); + + public void save(AzureUserCache user) { + userCache.put(user.getObjectIdentifier(), user); + } + + public AzureUserCache findByObjectIdentifier(String objectIdentifier) { + return userCache.get(objectIdentifier); + } + + public void deleteByObjectIdentifier(String objectIdentifier) { + userCache.remove(objectIdentifier); + } + + public boolean existsByObjectIdentifier(String objectIdentifier) { + return userCache.containsKey(objectIdentifier); + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/Config.java b/src/main/java/no/fintlabs/flyt/azure/Config.java new file mode 100644 index 0000000..e6b2705 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/Config.java @@ -0,0 +1,61 @@ +package no.fintlabs.flyt.azure; + +import com.azure.identity.ClientSecretCredential; +import com.azure.identity.ClientSecretCredentialBuilder; +import com.microsoft.graph.authentication.TokenCredentialAuthProvider; +import com.microsoft.graph.http.IHttpProvider; +import com.microsoft.graph.models.AppRoleAssignment; +import com.microsoft.graph.requests.GraphServiceClient; +import lombok.Getter; +import lombok.Setter; +import okhttp3.Request; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + + +@Getter +@Setter +@EnableAutoConfiguration +@Configuration +@ConfigurationProperties(prefix = "azure.credentials") +public class Config { + private String clientid; + private String clientsecret; + private String tenantid; + private String appid; + + private IHttpProvider httpProvider; + + @Bean + public ConfigUser configUser() { + return new ConfigUser(); + } + + @Bean + public AppRoleAssignment appRole() { + return new AppRoleAssignment(); + } + + @Bean + public GraphServiceClient graphService() { + ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() + .clientId(clientid) + .clientSecret(clientsecret) + .tenantId(tenantid) + .build(); + + TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider( + List.of("https://graph.microsoft.com/.default"), + clientSecretCredential + ); + + return GraphServiceClient + .builder() + .authenticationProvider(tokenCredentialAuthProvider) + .buildClient(); + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/ConfigUser.java b/src/main/java/no/fintlabs/flyt/azure/ConfigUser.java new file mode 100644 index 0000000..c7ac041 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/ConfigUser.java @@ -0,0 +1,24 @@ +package no.fintlabs.flyt.azure; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.Arrays; +import java.util.List; + +@Setter +@Getter +@AllArgsConstructor +public class ConfigUser { + + private static final List userAttributes = Arrays.asList( + "id", + "mail" + ); + + public List allAttributes() { + return userAttributes; + } + +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/models/sourceapplication/AcosSourceApplication.java b/src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java similarity index 89% rename from src/main/java/no/fintlabs/models/sourceapplication/AcosSourceApplication.java rename to src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java index 50604fc..f19176f 100644 --- a/src/main/java/no/fintlabs/models/sourceapplication/AcosSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.models.sourceapplication; +package no.fintlabs.flyt.models.sourceapplication; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/no/fintlabs/models/sourceapplication/DigisakSourceApplication.java b/src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java similarity index 89% rename from src/main/java/no/fintlabs/models/sourceapplication/DigisakSourceApplication.java rename to src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java index 77c3b98..573b04b 100644 --- a/src/main/java/no/fintlabs/models/sourceapplication/DigisakSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.models.sourceapplication; +package no.fintlabs.flyt.models.sourceapplication; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/no/fintlabs/models/sourceapplication/EgrunnervervSourceApplication.java b/src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java similarity index 89% rename from src/main/java/no/fintlabs/models/sourceapplication/EgrunnervervSourceApplication.java rename to src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java index b145da4..c45f204 100644 --- a/src/main/java/no/fintlabs/models/sourceapplication/EgrunnervervSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.models.sourceapplication; +package no.fintlabs.flyt.models.sourceapplication; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/no/fintlabs/models/sourceapplication/VigoSourceApplication.java b/src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java similarity index 89% rename from src/main/java/no/fintlabs/models/sourceapplication/VigoSourceApplication.java rename to src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java index 6a0f2aa..61bf5bf 100644 --- a/src/main/java/no/fintlabs/models/sourceapplication/VigoSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.models.sourceapplication; +package no.fintlabs.flyt.models.sourceapplication; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml new file mode 100644 index 0000000..5b075eb --- /dev/null +++ b/src/main/resources/application-flyt-azure.yaml @@ -0,0 +1,33 @@ +azure: + credentials: + clientid: ${fint.flyt.azure.client-id} + clientsecret: ${fint.flyt.azure.client-secret} + tenantid: ${fint.flyt.azure.tenant-id} + appid: ${fint.flyt.azure.app-id} + +spring: + security: + oauth2: + client: + registration: + azure: + client-id: ${azure.credentials.clientid} + client-secret: ${azure.credentials.clientsecret} + authorization-grant-type: client_credentials + scope: "https://graph.microsoft.com/.default" + provider: + azure: + authorization-uri: "https://login.microsoftonline.com/${azure.credentials.tenantid}/oauth2/v2.0/authorize" + token-uri: "https://login.microsoftonline.com/${azure.credentials.tenantid}/oauth2/v2.0/token" + +fint: + flyt: + azure-ad-gateway: + approles: + flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" + flyt-developer: "https://role-catalog.vigoiks.no/vigo/flyt/developer" + flyt-admin: "https://role-catalog.vigoiks.no/vigo/flyt/developer" + user-scheduler: + pull: + initial-delay-ms: 1000 + fixed-delay-ms: 600000 \ No newline at end of file diff --git a/src/main/resources/application-local-staging.yaml b/src/main/resources/application-local-staging.yaml index d1bcec0..3e5753d 100644 --- a/src/main/resources/application-local-staging.yaml +++ b/src/main/resources/application-local-staging.yaml @@ -20,4 +20,9 @@ spring: username: postgres password: password server: - port: 8086 \ No newline at end of file + port: 8086 + +logging: + level: + com.microsoft.aad.msal4j: warn + com.azure.identity: warn diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 4cfb947..1d1f680 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -3,6 +3,7 @@ fint: spring: profiles: include: + - flyt-azure - flyt-kafka - flyt-logging - flyt-postgres diff --git a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java index cab37e0..0de0e02 100644 --- a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java +++ b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java @@ -1,9 +1,9 @@ package no.fintlabs; import no.fintlabs.kafka.requestreply.ReplyProducerRecord; -import no.fintlabs.models.sourceapplication.AcosSourceApplication; -import no.fintlabs.models.sourceapplication.EgrunnervervSourceApplication; -import no.fintlabs.models.sourceapplication.DigisakSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.AcosSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.DigisakSourceApplication; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 0c9f2d13d91b40a219e411d9b14925150797f6bc Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 14:46:18 +0200 Subject: [PATCH 02/50] add caching of azure app roles and azure users based on permitted roles --- ...entAuthorizationProducerRecordBuilder.java | 6 +- .../user/UserPermissionController.java | 2 - .../user/UserPermissionService.java | 2 - .../java/no/fintlabs/flyt/azure/AppRoles.java | 12 -- .../flyt/azure/AzureADUserSyncService.java | 124 +++++++----------- ...AzureAppRoleAssignmentCacheRepository.java | 22 ++++ .../azure/AzureAppRoleCacheRepository.java | 35 +++++ .../flyt/azure/AzureAppRoleCacheService.java | 124 ++++++++++++++++++ ...eService.java => AzureAppRoleService.java} | 71 +--------- .../no/fintlabs/flyt/azure/AzureGroup.java | 22 ---- .../AzureGroupMembersCacheRepository.java | 23 ++++ .../flyt/azure/AzureUserCacheRepository.java | 13 +- .../java/no/fintlabs/flyt/azure/Config.java | 23 ++-- .../flyt/azure/PermittedAppRoles.java | 12 ++ .../resources/application-flyt-azure.yaml | 3 +- 15 files changed, 295 insertions(+), 199 deletions(-) delete mode 100644 src/main/java/no/fintlabs/flyt/azure/AppRoles.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java rename src/main/java/no/fintlabs/flyt/azure/{AzureRoleService.java => AzureAppRoleService.java} (51%) delete mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureGroup.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java diff --git a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java b/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java index 57fb3bf..7ceef4b 100644 --- a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java +++ b/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java @@ -1,18 +1,16 @@ package no.fintlabs; -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.kafka.requestreply.ReplyProducerRecord; import no.fintlabs.flyt.models.sourceapplication.AcosSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; import no.fintlabs.flyt.models.sourceapplication.DigisakSourceApplication; +import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; import no.fintlabs.flyt.models.sourceapplication.VigoSourceApplication; +import no.fintlabs.kafka.requestreply.ReplyProducerRecord; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.stereotype.Component; import java.util.Objects; @Component -@Slf4j public class ClientAuthorizationProducerRecordBuilder { public ReplyProducerRecord apply(ConsumerRecord consumerRecord) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java index 41559fa..ca9c176 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java @@ -1,6 +1,5 @@ package no.fintlabs.flyt.authorization.user; -import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.AuthorizationUtil; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -14,7 +13,6 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; -@Slf4j @RequestMapping(INTERNAL_API + "/authorization/user") @RestController public class UserPermissionController { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java index c476af3..146e810 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java @@ -1,6 +1,5 @@ package no.fintlabs.flyt.authorization.user; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -8,7 +7,6 @@ import java.util.Optional; @Service -@Slf4j public class UserPermissionService { private final UserPermissionRepository userPermissionRepository; diff --git a/src/main/java/no/fintlabs/flyt/azure/AppRoles.java b/src/main/java/no/fintlabs/flyt/azure/AppRoles.java deleted file mode 100644 index 228871e..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/AppRoles.java +++ /dev/null @@ -1,12 +0,0 @@ -package no.fintlabs.flyt.azure; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class AppRoles { - private List appRoleIds; -} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java index 40d3239..7238105 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java @@ -4,7 +4,7 @@ import com.microsoft.graph.requests.GraphServiceClient; import com.microsoft.graph.requests.UserCollectionPage; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.user.UserPermission; import no.fintlabs.flyt.authorization.user.UserPermissionRepository; import no.fintlabs.flyt.authorization.user.UserPermissionService; @@ -17,33 +17,68 @@ import java.util.Objects; @Component -@Log4j2 +@Slf4j @RequiredArgsConstructor public class AzureADUserSyncService { protected final Config config; - protected final ConfigUser configUser; + protected final PermittedAppRoles permittedAppRoles; protected final GraphServiceClient graphService; - protected final AzureRoleService azureRoleService; - + protected final AzureAppRoleService azureAppRoleService; protected final UserPermissionService userPermissionService; - protected final UserPermissionRepository userPermissionRepository; + protected final AzureAppRoleCacheService azureAppRoleCacheService; protected final AzureUserCacheRepository azureUserCacheRepository; + @Scheduled( + initialDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.initial-delay-ms}", + fixedDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.fixed-delay-ms}" + ) + private void pullUsersAndRoles() { + log.info("*** <<< Starting to pull users from Azure AD >>> ***"); + long startTime = System.currentTimeMillis(); + + azureAppRoleCacheService.storeAzureAppRoleDataInCache(config.getAppId()); + + try { + UserCollectionPage usersPage = graphService.users() + .buildRequest() + .select(String.join(",", configUser.allAttributes())) + .filter("usertype eq 'member'") + .get(); + + processUsers(usersPage); + } catch (Exception e) { + log.error("Error fetching users : {}", e.getMessage(), e); + } + + long endTime = System.currentTimeMillis(); + long elapsedTimeInSeconds = (endTime - startTime) / 1000; + long minutes = elapsedTimeInSeconds / 60; + long seconds = elapsedTimeInSeconds % 60; + + log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); + } + private void processUsers(UserCollectionPage userCollectionPage) { int usersProcessed = 0; UserCollectionPage currentPage = userCollectionPage; List userPermissionList = new ArrayList<>(); + List azureUserCaches = new ArrayList<>(); do { for (User user : currentPage.getCurrentPage()) { + + if (user.mail == null) { + continue; + } + usersProcessed++; - List userRoles = azureRoleService.getUserRoles(user.id, user.mail, config.getAppid()); + List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); - if (userRoles.contains("https://role-catalog.vigoiks.no/vigo/flyt/user") || userRoles.contains("https://role-catalog.vigoiks.no/vigo/flyt/developer")) { + if (isPermittedRole(userRoles)) { UserPermission userPermission = UserPermission .builder() .objectIdentifier(user.id) @@ -55,7 +90,7 @@ private void processUsers(UserCollectionPage userCollectionPage) { .objectIdentifier(user.id) .email(Objects.requireNonNull(user.mail).toLowerCase()) .build(); - azureUserCacheRepository.save(azureUserCache); + azureUserCaches.add(azureUserCache); } } @@ -67,75 +102,18 @@ private void processUsers(UserCollectionPage userCollectionPage) { } while (currentPage != null); userPermissionService.saveUserPermissions(userPermissionList); + azureUserCacheRepository.saveAll(azureUserCaches); log.info("{} User objects processed in Azure AD", usersProcessed); } -// public List getGroup(UUID principalId) { -// return this.pageThroughGetGroups( -// graphService.groups() -// .buildRequest() -// .select("id,members")) -// //.filter(String.format("startsWith(displayName,'%s')",configGroup.getPrefix())) -// .expand("members($select=id)") -// .get() -// ); -// } - -// private List pageThroughGetGroups(GroupCollectionPage inPage) { -// int groups = 0; -// GroupCollectionPage page = inPage; -// List retGroupList = new ArrayList(); -// do { -// for (Group group : page.getCurrentPage()) { -// -// AzureGroup newGroup; -// try { -// newGroup = new AzureGroup(group); -// } catch (NumberFormatException e) { -// log.warn("Problems converting resourceID to LONG! {}. Skipping creation of group", e); -// continue; -// } -// retGroupList.add(newGroup); -// } -// if (page.getNextPage() == null) { -// break; -// } else { -// log.debug("Processing group page"); -// page = page.getNextPage().buildRequest().get(); -// } -// } while (page != null); -// log.debug("{} Group objects detected in Microsoft Entra", groups); -// return retGroupList; -// } - - @Scheduled( - initialDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.initial-delay-ms}", - fixedDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.fixed-delay-ms}" - ) - private void pullUsers() { - log.info("*** <<< Starting to pull users from Azure AD >>> ***"); - long startTime = System.currentTimeMillis(); - - try { - UserCollectionPage usersPage = graphService.users() - .buildRequest() - .select(String.join(",", configUser.allAttributes())) - .filter("usertype eq 'member'") - .get(); - - processUsers(usersPage); - } catch (Exception e) { - log.error("Error fetching users : {}", e.getMessage(), e); + private boolean isPermittedRole(List userRoles) { + for (String role : userRoles) { + if (permittedAppRoles.getPermittedAppRoles().containsValue(role)) { + return true; + } } - - long endTime = System.currentTimeMillis(); - long elapsedTimeInSeconds = (endTime - startTime) / 1000; - long minutes = elapsedTimeInSeconds / 60; - long seconds = elapsedTimeInSeconds % 60; - - log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); + return false; } - } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java new file mode 100644 index 0000000..9a56f52 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java @@ -0,0 +1,22 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.AppRoleAssignment; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@Repository +public class AzureAppRoleAssignmentCacheRepository { + + private final List appRoleAssignmentsCache = new ArrayList<>(); + + public void saveAll(List appRoleAssignments) { + appRoleAssignmentsCache.addAll(appRoleAssignments); + } + + public List findAll() { + return appRoleAssignmentsCache; + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java new file mode 100644 index 0000000..707c379 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java @@ -0,0 +1,35 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.AppRole; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@Repository +public class AzureAppRoleCacheRepository { + + private final List appRoleCache = new ArrayList<>(); + protected final PermittedAppRoles permittedAppRoles; + + public AzureAppRoleCacheRepository(PermittedAppRoles permittedAppRoles) { + this.permittedAppRoles = permittedAppRoles; + } + + public void save(AppRole appRole) { + appRoleCache.add(appRole); + } + + public void saveAllPermittedRoles(List appRoles) { + appRoles.forEach(appRole -> { + if (permittedAppRoles.getPermittedAppRoles().containsValue(appRole.value)) { + save(appRole); + } + } + ); + } + + public List findAll() { + return appRoleCache; + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java new file mode 100644 index 0000000..f5c7698 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java @@ -0,0 +1,124 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.AppRole; +import com.microsoft.graph.models.AppRoleAssignment; +import com.microsoft.graph.models.User; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Service +@Slf4j +public class AzureAppRoleCacheService { + + protected final AzureAppRoleService azureAppRoleService; + + protected final AzureUserCacheRepository azureUserCacheRepository; + protected final AzureAppRoleCacheRepository azureAppRoleCacheRepository; + protected final AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository; + protected final AzureGroupMembersCacheRepository azureGroupMembersCacheRepository; + + public AzureAppRoleCacheService( + AzureAppRoleService azureAppRoleService, + AzureUserCacheRepository azureUserCacheRepository, + AzureAppRoleCacheRepository azureAppRoleCacheRepository, + AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository, + AzureGroupMembersCacheRepository azureGroupMembersCacheRepository + ) { + this.azureAppRoleService = azureAppRoleService; + this.azureUserCacheRepository = azureUserCacheRepository; + this.azureAppRoleCacheRepository = azureAppRoleCacheRepository; + this.azureAppRoleAssignmentCacheRepository = azureAppRoleAssignmentCacheRepository; + this.azureGroupMembersCacheRepository = azureGroupMembersCacheRepository; + } + + public void storeAzureAppRoleDataInCache(String appId) { + List appRoleAssignments = azureAppRoleService.getAppRoleAssignments(appId); + azureAppRoleAssignmentCacheRepository.saveAll(appRoleAssignments); + + appRoleAssignments.forEach(appRoleAssignment -> { + if (appRoleAssignment.principalType == null || appRoleAssignment.principalId == null) { + log.warn("Assignment principalType or principalId is null for appRoleAssignment {}", appRoleAssignment); + return; + } + + String principalType = appRoleAssignment.principalType; + String principalId = appRoleAssignment.principalId.toString(); + + if (principalType.equals("Group")) { + List groupMembers = azureAppRoleService.getGroupMembers(principalId); + azureGroupMembersCacheRepository.saveAll(principalId, groupMembers); + } + }); + + List appRoles = azureAppRoleService.getAppRoles(appId); + azureAppRoleCacheRepository.saveAllPermittedRoles(appRoles); + } + + public List getUserRoles( + String userId, + String email, + String appId + ) { + List roles = new ArrayList<>(); + + List appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAll(); + + appRoleAssignments.forEach(assignment -> { + if (assignment.principalType == null || assignment.principalId == null) { + log.warn("Assignment principalType or principalId is null for assignment {}", assignment); + return; + } + + String principalType = assignment.principalType; + String principalId = assignment.principalId.toString(); + + if (principalType.equals("User") && principalId.equals(userId)) { + addRoleIfNotNull(roles, assignment, appId); + } else if (principalType.equals("Group")) { + List groupMembers = azureGroupMembersCacheRepository.findAllByPrincipalId(principalId); + groupMembers.forEach(user -> { + if (user.id != null && user.id.equals(userId)) { + addRoleIfNotNull(roles, assignment, appId); + } + }); + } + }); + + if (roles.isEmpty()) { + log.info("User {} has no roles assigned in app {}", email, appId); + } else { + log.info("User {} has the following roles in app {}: {}", email, appId, roles); + } + + return roles; + } + + private void addRoleIfNotNull(List roles, AppRoleAssignment assignment, String appId) { + String roleName = getAppRoleValue(assignment.appRoleId, appId); + if (roleName != null) { + roles.add(roleName); + } else { + log.warn("Role name for appRoleId {} not found", assignment.appRoleId); + } + } + + private String getAppRoleValue(UUID appRoleId, String appId) { + List appRoles = azureAppRoleCacheRepository.findAll(); + if (appRoles == null) { + log.warn("App roles are null for appId: {}", appId); + return null; + } + for (AppRole appRole : appRoles) { + if (appRole.id != null && appRole.id.equals(appRoleId)) { + return appRole.value; + } + } + log.warn("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); + return null; + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java similarity index 51% rename from src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java rename to src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java index 27c2268..60bc8aa 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureRoleService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java @@ -3,23 +3,22 @@ import com.microsoft.graph.models.*; import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.requests.GraphServiceClient; -import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.UUID; import java.util.stream.Collectors; @Service -@Slf4j -public class AzureRoleService { +public class AzureAppRoleService { protected final GraphServiceClient graphService; - public AzureRoleService(GraphServiceClient graphService) { + public AzureAppRoleService( + GraphServiceClient graphService + ) { this.graphService = graphService; } @@ -62,21 +61,6 @@ public List getAppRoles(String appId) { return new ArrayList<>(); } - public String getRoleName(UUID appRoleId, String appId) { - List appRoles = getAppRoles(appId); - if (appRoles == null) { - log.warn("App roles are null for appId: {}", appId); - return null; - } - for (AppRole appRole : appRoles) { - if (appRole.id != null && appRole.id.equals(appRoleId)) { - return appRole.value; - } - } - log.warn("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); - return null; - } - public List getGroupMembers(String groupId) { List members = Objects.requireNonNull(graphService .groups(groupId) @@ -91,51 +75,4 @@ public List getGroupMembers(String groupId) { .collect(Collectors.toList()); } - public List getUserRoles(String userId, String email, String appId) { - List appRoleAssignments = getAppRoleAssignments(appId); - List roles = new ArrayList<>(); - - // Check direct user role assignments - for (AppRoleAssignment assignment : appRoleAssignments) { - - assert assignment.principalType != null; - assert assignment.principalId != null; - String principalType = assignment.principalType; - String principalId = assignment.principalId.toString(); - - if (principalType.equals("User") && principalId.equals(userId)) { - String roleName = getRoleName(assignment.appRoleId, appId); - if (roleName != null) { - roles.add(roleName); - } else { - log.warn("Role name for appRoleId {} not found", assignment.appRoleId); - } - } - } - - // Check if user is in any group that has specific roles - List groupMembers; - for (AppRoleAssignment assignment : appRoleAssignments) { - if (assignment.principalType != null && assignment.principalType.equals("Group")) { - groupMembers = getGroupMembers(String.valueOf(assignment.principalId)); - for (User user : groupMembers) { - if (user.id != null && user.id.equals(userId)) { - String roleName = getRoleName(assignment.appRoleId, appId); - if (roleName != null) { - roles.add(roleName); - } - } - } - } - } - - // Log the roles - if (roles.isEmpty()) { - log.info("User {} has no roles assigned in app {}", email, appId); - return List.of(); - } else { - log.info("User {} has the following roles in app {}: {}", email, appId, roles); - return roles; - } - } } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java b/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java deleted file mode 100644 index c9bedb9..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/AzureGroup.java +++ /dev/null @@ -1,22 +0,0 @@ -package no.fintlabs.flyt.azure; - -import com.microsoft.graph.models.Group; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@Setter -@Getter -public class AzureGroup { - protected String id; - protected List members; - - public AzureGroup(Group group) { - this.id = group.id; - this.members = new ArrayList<>(); - } -} - - diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java new file mode 100644 index 0000000..1a26374 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java @@ -0,0 +1,23 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.User; +import org.springframework.stereotype.Repository; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class AzureGroupMembersCacheRepository { + + private final Map> groupMembersCache = new HashMap<>(); + + public void saveAll(String principalId, List groupMembers) { + groupMembersCache.put(principalId, groupMembers); + } + + public List findAllByPrincipalId(String principalId) { + return groupMembersCache.get(principalId); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java index e1160c9..074db01 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java @@ -2,6 +2,7 @@ import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -9,14 +10,22 @@ public class AzureUserCacheRepository { private final Map userCache = new ConcurrentHashMap<>(); - public void save(AzureUserCache user) { - userCache.put(user.getObjectIdentifier(), user); + public void save(AzureUserCache azureUserCache) { + userCache.put(azureUserCache.getObjectIdentifier(), azureUserCache); + } + + public void saveAll(List azureUserCaches) { + azureUserCaches.forEach(this::save); } public AzureUserCache findByObjectIdentifier(String objectIdentifier) { return userCache.get(objectIdentifier); } + public Map findAll() { + return userCache; + } + public void deleteByObjectIdentifier(String objectIdentifier) { userCache.remove(objectIdentifier); } diff --git a/src/main/java/no/fintlabs/flyt/azure/Config.java b/src/main/java/no/fintlabs/flyt/azure/Config.java index e6b2705..1fd38d0 100644 --- a/src/main/java/no/fintlabs/flyt/azure/Config.java +++ b/src/main/java/no/fintlabs/flyt/azure/Config.java @@ -3,8 +3,6 @@ import com.azure.identity.ClientSecretCredential; import com.azure.identity.ClientSecretCredentialBuilder; import com.microsoft.graph.authentication.TokenCredentialAuthProvider; -import com.microsoft.graph.http.IHttpProvider; -import com.microsoft.graph.models.AppRoleAssignment; import com.microsoft.graph.requests.GraphServiceClient; import lombok.Getter; import lombok.Setter; @@ -23,12 +21,10 @@ @Configuration @ConfigurationProperties(prefix = "azure.credentials") public class Config { - private String clientid; - private String clientsecret; - private String tenantid; - private String appid; - - private IHttpProvider httpProvider; + private String clientId; + private String clientSecret; + private String tenantId; + private String appId; @Bean public ConfigUser configUser() { @@ -36,16 +32,17 @@ public ConfigUser configUser() { } @Bean - public AppRoleAssignment appRole() { - return new AppRoleAssignment(); + @ConfigurationProperties(prefix = "fint.flyt.azure-ad-gateway") + public PermittedAppRoles appRoles() { + return new PermittedAppRoles(); } @Bean public GraphServiceClient graphService() { ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() - .clientId(clientid) - .clientSecret(clientsecret) - .tenantId(tenantid) + .clientId(clientId) + .clientSecret(clientSecret) + .tenantId(tenantId) .build(); TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider( diff --git a/src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java b/src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java new file mode 100644 index 0000000..a603c56 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java @@ -0,0 +1,12 @@ +package no.fintlabs.flyt.azure; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@Getter +@Setter +public class PermittedAppRoles { + private Map permittedAppRoles; +} diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index 5b075eb..2bae8ba 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -23,10 +23,9 @@ spring: fint: flyt: azure-ad-gateway: - approles: + permitted-approles: flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" flyt-developer: "https://role-catalog.vigoiks.no/vigo/flyt/developer" - flyt-admin: "https://role-catalog.vigoiks.no/vigo/flyt/developer" user-scheduler: pull: initial-delay-ms: 1000 From ac780c84ef0f3449ffd1ce7d7b2e35ac0a900cfc Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 14:59:09 +0200 Subject: [PATCH 03/50] add deployment credentials for azure in fintlabs beta --- kustomize/base/kustomization.yaml | 3 ++- kustomize/base/onePassword.yaml | 7 +++++++ .../overlays/fintlabs-no/beta/kustomization.yaml | 12 ++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 kustomize/base/onePassword.yaml diff --git a/kustomize/base/kustomization.yaml b/kustomize/base/kustomization.yaml index 732a662..efc6227 100644 --- a/kustomize/base/kustomization.yaml +++ b/kustomize/base/kustomization.yaml @@ -1,4 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - flais.yaml \ No newline at end of file + - flais.yaml + - onePassword.yaml \ No newline at end of file diff --git a/kustomize/base/onePassword.yaml b/kustomize/base/onePassword.yaml new file mode 100644 index 0000000..1639654 --- /dev/null +++ b/kustomize/base/onePassword.yaml @@ -0,0 +1,7 @@ +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: fint-flyt-authorization-service +spec: + itemPath: "path set in overlay" + diff --git a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml index c8a6829..7a00527 100644 --- a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml +++ b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml @@ -51,6 +51,18 @@ patches: value: secretRef: name: fint-flyt-vigo-oauth2-client + - op: add + path: "/spec/envFrom/2" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-beta-vault/items/fint-flyt-authorization-service-fintlabs-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file From 878065050fa9326f176b6d94609b3e81d939b1d5 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 15:02:50 +0200 Subject: [PATCH 04/50] add deployment credentials for azure in fintlabs beta --- kustomize/overlays/fintlabs-no/beta/kustomization.yaml | 1 + kustomize/overlays/fintlabs-no/beta/onePassword.yaml | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 kustomize/overlays/fintlabs-no/beta/onePassword.yaml diff --git a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml index 7a00527..cab7227 100644 --- a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml +++ b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml @@ -6,6 +6,7 @@ resources: - ../../../base - digisak-oauth2-client.yaml - vigo-oauth2-client.yaml + - onePassword.yaml commonLabels: app.kubernetes.io/instance: fint-flyt-authorization-service_fintlabs_no diff --git a/kustomize/overlays/fintlabs-no/beta/onePassword.yaml b/kustomize/overlays/fintlabs-no/beta/onePassword.yaml new file mode 100644 index 0000000..1639654 --- /dev/null +++ b/kustomize/overlays/fintlabs-no/beta/onePassword.yaml @@ -0,0 +1,7 @@ +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: fint-flyt-authorization-service +spec: + itemPath: "path set in overlay" + From 739369f5268b043001ffceaf989e97b392f87474 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 15:04:55 +0200 Subject: [PATCH 05/50] add deployment credentials for azure in fintlabs beta --- kustomize/overlays/fintlabs-no/beta/kustomization.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml index cab7227..7a00527 100644 --- a/kustomize/overlays/fintlabs-no/beta/kustomization.yaml +++ b/kustomize/overlays/fintlabs-no/beta/kustomization.yaml @@ -6,7 +6,6 @@ resources: - ../../../base - digisak-oauth2-client.yaml - vigo-oauth2-client.yaml - - onePassword.yaml commonLabels: app.kubernetes.io/instance: fint-flyt-authorization-service_fintlabs_no From 54b22d4771d82a4d2b09aa523a6d7ce2809b8a7c Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 15:31:40 +0200 Subject: [PATCH 06/50] add sorting on email when getting userpermissions --- .../flyt/authorization/adminuser/AdminUserController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index 34be76e..713cdcc 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -14,6 +14,7 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; +import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -55,6 +56,7 @@ public Mono>> getUserPermissions( .map(userPermissions -> { List userPermissionDtos = new java.util.ArrayList<>(List.of()); userPermissions.forEach(userPermission -> userPermissionDtos.add(buildUserPermissionDto(userPermission))); + userPermissionDtos.sort(Comparator.comparing(UserPermissionDto::getEmail, Comparator.nullsLast(String::compareTo))); return ResponseEntity.ok().body(userPermissionDtos); }); } else { From 54b770fef49a9693907ed2a7171082775ce61cd5 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 15:39:57 +0200 Subject: [PATCH 07/50] add sorting on email when returning result after posting userpermissions --- .../flyt/authorization/adminuser/AdminUserController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index 713cdcc..dca03c7 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -97,7 +97,10 @@ public Mono>> setUserPermissions( } }).subscribeOn(Schedulers.boundedElastic())) .collectList() - .map(ResponseEntity::ok); + .map(userPermissionDtoList -> { + userPermissionDtoList.sort(Comparator.comparing(UserPermissionDto::getEmail, Comparator.nullsLast(String::compareTo))); + return ResponseEntity.ok().body(userPermissionDtoList); + }); } else { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } From 86cd6c82fe0d69011a0ca0e987219727bd1b08ea Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 13 Jun 2024 15:42:54 +0200 Subject: [PATCH 08/50] polling azure user data every hour --- src/main/resources/application-flyt-azure.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index 2bae8ba..6ce38d5 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -29,4 +29,4 @@ fint: user-scheduler: pull: initial-delay-ms: 1000 - fixed-delay-ms: 600000 \ No newline at end of file + fixed-delay-ms: 3600000 \ No newline at end of file From 20ab093c3de582f525a69cb1759d198b28c305a2 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 14 Jun 2024 09:49:08 +0200 Subject: [PATCH 09/50] fix bug where app role and app role assignments were duplicated in cache --- .../user/UserPermissionService.java | 14 +++++--- ...AzureAppRoleAssignmentCacheRepository.java | 13 ++++---- .../azure/AzureAppRoleCacheRepository.java | 15 ++++++--- .../flyt/azure/AzureAppRoleCacheService.java | 33 ++++++++++--------- .../resources/application-flyt-azure.yaml | 2 +- 5 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java index 146e810..26432f6 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java @@ -1,5 +1,6 @@ package no.fintlabs.flyt.authorization.user; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -7,6 +8,7 @@ import java.util.Optional; @Service +@Slf4j public class UserPermissionService { private final UserPermissionRepository userPermissionRepository; @@ -17,7 +19,7 @@ public UserPermissionService(UserPermissionRepository userPermissionRepository) public void saveUserPermissions(List userPermissions) { - nullifyUserPermissionsNotInList(userPermissions); + deleteUserPermissionsNotInList(userPermissions); List newUserPermissionList = new ArrayList<>(); @@ -32,20 +34,22 @@ public void saveUserPermissions(List userPermissions) { userPermissionRepository.saveAll(newUserPermissionList); } - private void nullifyUserPermissionsNotInList(List userPermissions) { + private void deleteUserPermissionsNotInList(List userPermissions) { List allCurrentUserPermissions = userPermissionRepository.findAll(); List inputUserPermissionIdentifiers = userPermissions.stream() .map(UserPermission::getObjectIdentifier) .toList(); - List userPermissionsToNullify = allCurrentUserPermissions.stream() + List userPermissionsToDelete = allCurrentUserPermissions.stream() .filter(userPermission -> !inputUserPermissionIdentifiers.contains(userPermission.getObjectIdentifier())) .toList(); - userPermissionsToNullify.forEach(userPermission -> userPermission.setSourceApplicationIds(null)); + userPermissionRepository.deleteAll(userPermissionsToDelete); - userPermissionRepository.saveAll(userPermissionsToNullify); + if (!userPermissionsToDelete.isEmpty()) { + log.info("Deleted {} user permissions", userPermissionsToDelete.size()); + } } } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java index 9a56f52..fd10e18 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java @@ -3,20 +3,21 @@ import com.microsoft.graph.models.AppRoleAssignment; import org.springframework.stereotype.Repository; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Repository public class AzureAppRoleAssignmentCacheRepository { - private final List appRoleAssignmentsCache = new ArrayList<>(); + private final Map> appRoleAssignmentsCache = new HashMap<>(); - public void saveAll(List appRoleAssignments) { - appRoleAssignmentsCache.addAll(appRoleAssignments); + public void saveAll(String appId, List appRoleAssignments) { + appRoleAssignmentsCache.put(appId, appRoleAssignments); } - public List findAll() { - return appRoleAssignmentsCache; + public List findAllByAppId(String appId) { + return appRoleAssignmentsCache.get(appId); } } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java index 707c379..0d1c843 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java @@ -1,15 +1,18 @@ package no.fintlabs.flyt.azure; import com.microsoft.graph.models.AppRole; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Repository +@Slf4j public class AzureAppRoleCacheRepository { - private final List appRoleCache = new ArrayList<>(); + private final Map appRoleCache = new HashMap<>(); protected final PermittedAppRoles permittedAppRoles; public AzureAppRoleCacheRepository(PermittedAppRoles permittedAppRoles) { @@ -17,7 +20,11 @@ public AzureAppRoleCacheRepository(PermittedAppRoles permittedAppRoles) { } public void save(AppRole appRole) { - appRoleCache.add(appRole); + if (appRole.id != null) { + appRoleCache.put(appRole.id.toString(), appRole); + } else { + log.warn("App role {} was not saved in cache because id is null", appRole); + } } public void saveAllPermittedRoles(List appRoles) { @@ -29,7 +36,7 @@ public void saveAllPermittedRoles(List appRoles) { ); } - public List findAll() { + public Map findAll() { return appRoleCache; } } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java index f5c7698..4feff65 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; @Service @@ -26,8 +27,7 @@ public AzureAppRoleCacheService( AzureUserCacheRepository azureUserCacheRepository, AzureAppRoleCacheRepository azureAppRoleCacheRepository, AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository, - AzureGroupMembersCacheRepository azureGroupMembersCacheRepository - ) { + AzureGroupMembersCacheRepository azureGroupMembersCacheRepository) { this.azureAppRoleService = azureAppRoleService; this.azureUserCacheRepository = azureUserCacheRepository; this.azureAppRoleCacheRepository = azureAppRoleCacheRepository; @@ -37,7 +37,7 @@ public AzureAppRoleCacheService( public void storeAzureAppRoleDataInCache(String appId) { List appRoleAssignments = azureAppRoleService.getAppRoleAssignments(appId); - azureAppRoleAssignmentCacheRepository.saveAll(appRoleAssignments); + azureAppRoleAssignmentCacheRepository.saveAll(appId, appRoleAssignments); appRoleAssignments.forEach(appRoleAssignment -> { if (appRoleAssignment.principalType == null || appRoleAssignment.principalId == null) { @@ -65,11 +65,11 @@ public List getUserRoles( ) { List roles = new ArrayList<>(); - List appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAll(); + List appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAllByAppId(appId); appRoleAssignments.forEach(assignment -> { if (assignment.principalType == null || assignment.principalId == null) { - log.warn("Assignment principalType or principalId is null for assignment {}", assignment); + log.debug("Assignment principalType or principalId is null for assignment {}", assignment); return; } @@ -89,9 +89,9 @@ public List getUserRoles( }); if (roles.isEmpty()) { - log.info("User {} has no roles assigned in app {}", email, appId); + log.debug("User with email {} has no roles assigned in app {}", email, appId); } else { - log.info("User {} has the following roles in app {}: {}", email, appId, roles); + log.info("User with email {} has the following roles in app {}: {}", email, appId, roles); } return roles; @@ -102,22 +102,25 @@ private void addRoleIfNotNull(List roles, AppRoleAssignment assignment, if (roleName != null) { roles.add(roleName); } else { - log.warn("Role name for appRoleId {} not found", assignment.appRoleId); + log.debug("Role name for appRoleId {} not found", assignment.appRoleId); } } private String getAppRoleValue(UUID appRoleId, String appId) { - List appRoles = azureAppRoleCacheRepository.findAll(); - if (appRoles == null) { - log.warn("App roles are null for appId: {}", appId); - return null; - } - for (AppRole appRole : appRoles) { + Map appRoles = azureAppRoleCacheRepository.findAll(); +// if (appRoles == null) { +// log.debug("App roles are null for appId: {}", appId); +// return null; +// } + + for (Map.Entry entry : appRoles.entrySet()) { + AppRole appRole = entry.getValue(); if (appRole.id != null && appRole.id.equals(appRoleId)) { return appRole.value; } } - log.warn("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); + + log.debug("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); return null; } diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index 6ce38d5..6c6d4eb 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -29,4 +29,4 @@ fint: user-scheduler: pull: initial-delay-ms: 1000 - fixed-delay-ms: 3600000 \ No newline at end of file + fixed-delay-ms: 900000 \ No newline at end of file From 56cd358d92ba6e761729ca53adfe5c98f0cb0527 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 14 Jun 2024 13:14:18 +0200 Subject: [PATCH 10/50] fix bug where app role and app role assignments were duplicated in cache --- .../adminuser/AdminUserController.java | 2 +- .../user/UserPermissionService.java | 6 +-- .../flyt/azure/AzureADUserSyncService.java | 13 ++++-- .../flyt/azure/AzureAppRoleCacheService.java | 9 +--- .../flyt/azure/AzureUserCacheRepository.java | 13 +++--- .../flyt/azure/AzureUserCacheService.java | 41 +++++++++++++++++++ 6 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index dca03c7..2891f2f 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -114,7 +114,7 @@ private UserPermissionDto buildUserPermissionDto(UserPermission userPermission) return UserPermissionDto .builder() .objectIdentifier(userPermission.getObjectIdentifier()) - .email(azureUserCache.getEmail()) + .email(azureUserCache != null ? azureUserCache.getEmail() : null) .sourceApplicationIds(userPermission.getSourceApplicationIds()) .build(); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java index 26432f6..5593e3c 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java @@ -17,16 +17,16 @@ public UserPermissionService(UserPermissionRepository userPermissionRepository) this.userPermissionRepository = userPermissionRepository; } - public void saveUserPermissions(List userPermissions) { + public void refreshUserPermissions(List userPermissions) { deleteUserPermissionsNotInList(userPermissions); List newUserPermissionList = new ArrayList<>(); userPermissions.forEach(userPermission -> { - Optional existingPermission = userPermissionRepository + Optional existingUserPermission = userPermissionRepository .findByObjectIdentifier(userPermission.getObjectIdentifier()); - if (existingPermission.isEmpty()) { + if (existingUserPermission.isEmpty()) { newUserPermissionList.add(userPermission); } }); diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java index 7238105..962f665 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java @@ -28,7 +28,7 @@ public class AzureADUserSyncService { protected final UserPermissionService userPermissionService; protected final UserPermissionRepository userPermissionRepository; protected final AzureAppRoleCacheService azureAppRoleCacheService; - protected final AzureUserCacheRepository azureUserCacheRepository; + protected final AzureUserCacheService azureUserCacheService; @Scheduled( initialDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.initial-delay-ms}", @@ -101,8 +101,15 @@ private void processUsers(UserCollectionPage userCollectionPage) { } } while (currentPage != null); - userPermissionService.saveUserPermissions(userPermissionList); - azureUserCacheRepository.saveAll(azureUserCaches); + if (!userPermissionList.isEmpty()) { + userPermissionService.refreshUserPermissions(userPermissionList); + userPermissionList.forEach(userPermission -> log.info("Saving user permission {} in db", userPermission.getObjectIdentifier())); + } + + if (!azureUserCaches.isEmpty()) { + azureUserCacheService.refreshAzureUserCaches(azureUserCaches); + azureUserCaches.forEach(azureUserCache -> log.debug("Saving azure user {} in cache", azureUserCache.getEmail())); + } log.info("{} User objects processed in Azure AD", usersProcessed); } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java index 4feff65..6a7f360 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java @@ -17,19 +17,16 @@ public class AzureAppRoleCacheService { protected final AzureAppRoleService azureAppRoleService; - protected final AzureUserCacheRepository azureUserCacheRepository; protected final AzureAppRoleCacheRepository azureAppRoleCacheRepository; protected final AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository; protected final AzureGroupMembersCacheRepository azureGroupMembersCacheRepository; public AzureAppRoleCacheService( AzureAppRoleService azureAppRoleService, - AzureUserCacheRepository azureUserCacheRepository, AzureAppRoleCacheRepository azureAppRoleCacheRepository, AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository, AzureGroupMembersCacheRepository azureGroupMembersCacheRepository) { this.azureAppRoleService = azureAppRoleService; - this.azureUserCacheRepository = azureUserCacheRepository; this.azureAppRoleCacheRepository = azureAppRoleCacheRepository; this.azureAppRoleAssignmentCacheRepository = azureAppRoleAssignmentCacheRepository; this.azureGroupMembersCacheRepository = azureGroupMembersCacheRepository; @@ -91,7 +88,7 @@ public List getUserRoles( if (roles.isEmpty()) { log.debug("User with email {} has no roles assigned in app {}", email, appId); } else { - log.info("User with email {} has the following roles in app {}: {}", email, appId, roles); + log.debug("User with email {} has the following roles in app {}: {}", email, appId, roles); } return roles; @@ -108,10 +105,6 @@ private void addRoleIfNotNull(List roles, AppRoleAssignment assignment, private String getAppRoleValue(UUID appRoleId, String appId) { Map appRoles = azureAppRoleCacheRepository.findAll(); -// if (appRoles == null) { -// log.debug("App roles are null for appId: {}", appId); -// return null; -// } for (Map.Entry entry : appRoles.entrySet()) { AppRole appRole = entry.getValue(); diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java index 074db01..8aa2be6 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java @@ -8,10 +8,10 @@ @Repository public class AzureUserCacheRepository { - private final Map userCache = new ConcurrentHashMap<>(); + private final Map azureUserCacheMap = new ConcurrentHashMap<>(); public void save(AzureUserCache azureUserCache) { - userCache.put(azureUserCache.getObjectIdentifier(), azureUserCache); + azureUserCacheMap.put(azureUserCache.getObjectIdentifier(), azureUserCache); } public void saveAll(List azureUserCaches) { @@ -19,18 +19,15 @@ public void saveAll(List azureUserCaches) { } public AzureUserCache findByObjectIdentifier(String objectIdentifier) { - return userCache.get(objectIdentifier); + return azureUserCacheMap.get(objectIdentifier); } public Map findAll() { - return userCache; + return azureUserCacheMap; } public void deleteByObjectIdentifier(String objectIdentifier) { - userCache.remove(objectIdentifier); + azureUserCacheMap.remove(objectIdentifier); } - public boolean existsByObjectIdentifier(String objectIdentifier) { - return userCache.containsKey(objectIdentifier); - } } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java new file mode 100644 index 0000000..6c86648 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java @@ -0,0 +1,41 @@ +package no.fintlabs.flyt.azure; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class AzureUserCacheService { + + private final AzureUserCacheRepository azureUserCacheRepository; + + public AzureUserCacheService(AzureUserCacheRepository azureUserCacheRepository) { + this.azureUserCacheRepository = azureUserCacheRepository; + } + + public void refreshAzureUserCaches(List azureUserCaches) { + deleteAzureUserCacheNotInList(azureUserCaches); + azureUserCacheRepository.saveAll(azureUserCaches); + } + + private void deleteAzureUserCacheNotInList(List azureUserCaches) { + Map allCurrentAzureUserCaches = azureUserCacheRepository.findAll(); + + List inputAzureUserCachesIdentifiers = azureUserCaches.stream() + .map(AzureUserCache::getObjectIdentifier) + .toList(); + + List azureUserCachesStringsToDelete = allCurrentAzureUserCaches.keySet().stream() + .filter(identifier -> !inputAzureUserCachesIdentifiers.contains(identifier)) + .toList(); + + azureUserCachesStringsToDelete.forEach(azureUserCacheRepository::deleteByObjectIdentifier); + + if (!azureUserCachesStringsToDelete.isEmpty()) { + log.info("Deleted {} user permissions", azureUserCachesStringsToDelete.size()); + } + } +} From 038970c4ae668e7b8f8e6eb3bf061c9c3ffeee6c Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 14 Jun 2024 13:26:58 +0200 Subject: [PATCH 11/50] refactor --- .../java/no/fintlabs/flyt/azure/AzureADUserSyncService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java index 962f665..ac1368c 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java @@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.user.UserPermission; -import no.fintlabs.flyt.authorization.user.UserPermissionRepository; import no.fintlabs.flyt.authorization.user.UserPermissionService; import okhttp3.Request; import org.springframework.scheduling.annotation.Scheduled; @@ -26,7 +25,6 @@ public class AzureADUserSyncService { protected final GraphServiceClient graphService; protected final AzureAppRoleService azureAppRoleService; protected final UserPermissionService userPermissionService; - protected final UserPermissionRepository userPermissionRepository; protected final AzureAppRoleCacheService azureAppRoleCacheService; protected final AzureUserCacheService azureUserCacheService; From 24e3545adf67d8476648d5d64b6443be6ae763d2 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 14 Jun 2024 13:43:25 +0200 Subject: [PATCH 12/50] refactor --- build.gradle | 6 ------ .../adminuser/AdminUserController.java | 4 ++-- .../java/no/fintlabs/flyt/azure/Config.java | 2 ++ .../flyt/azure/{ => models}/AzureUserCache.java | 2 +- .../flyt/azure/{ => models}/ConfigUser.java | 2 +- .../azure/{ => models}/PermittedAppRoles.java | 2 +- .../AzureAppRoleAssignmentCacheRepository.java | 2 +- .../AzureAppRoleCacheRepository.java | 3 ++- .../AzureGroupMembersCacheRepository.java | 2 +- .../AzureUserCacheRepository.java | 3 ++- .../{ => services}/AzureADUserSyncService.java | 8 ++++++-- .../AzureAppGraphService.java} | 6 +++--- .../AzureAppRoleCacheService.java | 17 ++++++++++------- .../{ => services}/AzureUserCacheService.java | 4 +++- 14 files changed, 35 insertions(+), 28 deletions(-) rename src/main/java/no/fintlabs/flyt/azure/{ => models}/AzureUserCache.java (80%) rename src/main/java/no/fintlabs/flyt/azure/{ => models}/ConfigUser.java (90%) rename src/main/java/no/fintlabs/flyt/azure/{ => models}/PermittedAppRoles.java (81%) rename src/main/java/no/fintlabs/flyt/azure/{ => repositories}/AzureAppRoleAssignmentCacheRepository.java (93%) rename src/main/java/no/fintlabs/flyt/azure/{ => repositories}/AzureAppRoleCacheRepository.java (92%) rename src/main/java/no/fintlabs/flyt/azure/{ => repositories}/AzureGroupMembersCacheRepository.java (92%) rename src/main/java/no/fintlabs/flyt/azure/{ => repositories}/AzureUserCacheRepository.java (90%) rename src/main/java/no/fintlabs/flyt/azure/{ => services}/AzureADUserSyncService.java (94%) rename src/main/java/no/fintlabs/flyt/azure/{AzureAppRoleService.java => services/AzureAppGraphService.java} (96%) rename src/main/java/no/fintlabs/flyt/azure/{ => services}/AzureAppRoleCacheService.java (86%) rename src/main/java/no/fintlabs/flyt/azure/{ => services}/AzureUserCacheService.java (89%) diff --git a/build.gradle b/build.gradle index 4e7d841..b0e2493 100644 --- a/build.gradle +++ b/build.gradle @@ -44,15 +44,9 @@ dependencies { implementation 'no.fintlabs:fint-kafka:4.0.1' implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0' -// implementation 'com.microsoft.graph:microsoft-graph:6.11.0' -// implementation 'com.azure:azure-identity:1.12.1' - implementation 'com.azure:azure-identity:1.10.2' implementation 'com.microsoft.graph:microsoft-graph:5.80.0' -// implementation 'com.azure:azure-identity:1.4.6' -// implementation 'com.microsoft.graph:microsoft-graph:5.9.0' - compileOnly 'org.projectlombok:lombok' runtimeOnly 'io.micrometer:micrometer-registry-prometheus' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index 2891f2f..07a3c95 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -3,8 +3,8 @@ import no.fintlabs.flyt.authorization.user.UserPermission; import no.fintlabs.flyt.authorization.user.UserPermissionDto; import no.fintlabs.flyt.authorization.user.UserPermissionRepository; -import no.fintlabs.flyt.azure.AzureUserCache; -import no.fintlabs.flyt.azure.AzureUserCacheRepository; +import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; diff --git a/src/main/java/no/fintlabs/flyt/azure/Config.java b/src/main/java/no/fintlabs/flyt/azure/Config.java index 1fd38d0..bdec41f 100644 --- a/src/main/java/no/fintlabs/flyt/azure/Config.java +++ b/src/main/java/no/fintlabs/flyt/azure/Config.java @@ -6,6 +6,8 @@ import com.microsoft.graph.requests.GraphServiceClient; import lombok.Getter; import lombok.Setter; +import no.fintlabs.flyt.azure.models.ConfigUser; +import no.fintlabs.flyt.azure.models.PermittedAppRoles; import okhttp3.Request; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java b/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java similarity index 80% rename from src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java rename to src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java index 8c2f7df..a424e47 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureUserCache.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.models; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/no/fintlabs/flyt/azure/ConfigUser.java b/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java similarity index 90% rename from src/main/java/no/fintlabs/flyt/azure/ConfigUser.java rename to src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java index c7ac041..f5144bc 100644 --- a/src/main/java/no/fintlabs/flyt/azure/ConfigUser.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.models; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java b/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java similarity index 81% rename from src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java rename to src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java index a603c56..b7bb0fb 100644 --- a/src/main/java/no/fintlabs/flyt/azure/PermittedAppRoles.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.models; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java similarity index 93% rename from src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java rename to src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java index fd10e18..36ad76c 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleAssignmentCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.repositories; import com.microsoft.graph.models.AppRoleAssignment; import org.springframework.stereotype.Repository; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java similarity index 92% rename from src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java rename to src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java index 0d1c843..aa384e9 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java @@ -1,7 +1,8 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.repositories; import com.microsoft.graph.models.AppRole; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.azure.models.PermittedAppRoles; import org.springframework.stereotype.Repository; import java.util.HashMap; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java similarity index 92% rename from src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java rename to src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java index 1a26374..6125f8f 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureGroupMembersCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.repositories; import com.microsoft.graph.models.User; import org.springframework.stereotype.Repository; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java similarity index 90% rename from src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java rename to src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java index 8aa2be6..a73b6da 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java @@ -1,5 +1,6 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.repositories; +import no.fintlabs.flyt.azure.models.AzureUserCache; import org.springframework.stereotype.Repository; import java.util.List; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java similarity index 94% rename from src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java rename to src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index ac1368c..1a05e54 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.services; import com.microsoft.graph.models.User; import com.microsoft.graph.requests.GraphServiceClient; @@ -7,6 +7,10 @@ import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.user.UserPermission; import no.fintlabs.flyt.authorization.user.UserPermissionService; +import no.fintlabs.flyt.azure.Config; +import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.models.ConfigUser; +import no.fintlabs.flyt.azure.models.PermittedAppRoles; import okhttp3.Request; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -23,7 +27,7 @@ public class AzureADUserSyncService { protected final ConfigUser configUser; protected final PermittedAppRoles permittedAppRoles; protected final GraphServiceClient graphService; - protected final AzureAppRoleService azureAppRoleService; + protected final AzureAppGraphService azureAppGraphService; protected final UserPermissionService userPermissionService; protected final AzureAppRoleCacheService azureAppRoleCacheService; protected final AzureUserCacheService azureUserCacheService; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java similarity index 96% rename from src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java rename to src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java index 60bc8aa..d045c16 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.services; import com.microsoft.graph.models.*; import com.microsoft.graph.options.QueryOption; @@ -12,11 +12,11 @@ import java.util.stream.Collectors; @Service -public class AzureAppRoleService { +public class AzureAppGraphService { protected final GraphServiceClient graphService; - public AzureAppRoleService( + public AzureAppGraphService( GraphServiceClient graphService ) { this.graphService = graphService; diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java similarity index 86% rename from src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java rename to src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java index 6a7f360..d1f37b8 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureAppRoleCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java @@ -1,9 +1,12 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.services; import com.microsoft.graph.models.AppRole; import com.microsoft.graph.models.AppRoleAssignment; import com.microsoft.graph.models.User; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.azure.repositories.AzureAppRoleAssignmentCacheRepository; +import no.fintlabs.flyt.azure.repositories.AzureAppRoleCacheRepository; +import no.fintlabs.flyt.azure.repositories.AzureGroupMembersCacheRepository; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -15,25 +18,25 @@ @Slf4j public class AzureAppRoleCacheService { - protected final AzureAppRoleService azureAppRoleService; + protected final AzureAppGraphService azureAppGraphService; protected final AzureAppRoleCacheRepository azureAppRoleCacheRepository; protected final AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository; protected final AzureGroupMembersCacheRepository azureGroupMembersCacheRepository; public AzureAppRoleCacheService( - AzureAppRoleService azureAppRoleService, + AzureAppGraphService azureAppGraphService, AzureAppRoleCacheRepository azureAppRoleCacheRepository, AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository, AzureGroupMembersCacheRepository azureGroupMembersCacheRepository) { - this.azureAppRoleService = azureAppRoleService; + this.azureAppGraphService = azureAppGraphService; this.azureAppRoleCacheRepository = azureAppRoleCacheRepository; this.azureAppRoleAssignmentCacheRepository = azureAppRoleAssignmentCacheRepository; this.azureGroupMembersCacheRepository = azureGroupMembersCacheRepository; } public void storeAzureAppRoleDataInCache(String appId) { - List appRoleAssignments = azureAppRoleService.getAppRoleAssignments(appId); + List appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId); azureAppRoleAssignmentCacheRepository.saveAll(appId, appRoleAssignments); appRoleAssignments.forEach(appRoleAssignment -> { @@ -46,12 +49,12 @@ public void storeAzureAppRoleDataInCache(String appId) { String principalId = appRoleAssignment.principalId.toString(); if (principalType.equals("Group")) { - List groupMembers = azureAppRoleService.getGroupMembers(principalId); + List groupMembers = azureAppGraphService.getGroupMembers(principalId); azureGroupMembersCacheRepository.saveAll(principalId, groupMembers); } }); - List appRoles = azureAppRoleService.getAppRoles(appId); + List appRoles = azureAppGraphService.getAppRoles(appId); azureAppRoleCacheRepository.saveAllPermittedRoles(appRoles); } diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java similarity index 89% rename from src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java rename to src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java index 6c86648..08cdc30 100644 --- a/src/main/java/no/fintlabs/flyt/azure/AzureUserCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java @@ -1,6 +1,8 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.services; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; +import no.fintlabs.flyt.azure.models.AzureUserCache; import org.springframework.stereotype.Service; import java.util.List; From 81fe73d4464ca0db8d2f195f4382568340386d9c Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 20 Jun 2024 10:11:37 +0200 Subject: [PATCH 13/50] handle missing azure ad app credentials --- .../java/no/fintlabs/flyt/azure/Config.java | 2 ++ .../azure/RequiredPropertiesCondition.java | 20 +++++++++++++++++++ .../services/AzureADUserSyncService.java | 16 +++++++++++++++ .../resources/application-flyt-azure.yaml | 8 ++++---- 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java diff --git a/src/main/java/no/fintlabs/flyt/azure/Config.java b/src/main/java/no/fintlabs/flyt/azure/Config.java index bdec41f..117e887 100644 --- a/src/main/java/no/fintlabs/flyt/azure/Config.java +++ b/src/main/java/no/fintlabs/flyt/azure/Config.java @@ -12,6 +12,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import java.util.List; @@ -40,6 +41,7 @@ public PermittedAppRoles appRoles() { } @Bean + @Conditional(RequiredPropertiesCondition.class) public GraphServiceClient graphService() { ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() .clientId(clientId) diff --git a/src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java b/src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java new file mode 100644 index 0000000..1276595 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java @@ -0,0 +1,20 @@ +package no.fintlabs.flyt.azure; + +import org.jetbrains.annotations.NotNull; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.StringUtils; + +public class RequiredPropertiesCondition implements Condition { + @Override + public boolean matches(ConditionContext context, @NotNull AnnotatedTypeMetadata metadata) { + String clientId = context.getEnvironment().getProperty("azure.credentials.clientid"); + String clientSecret = context.getEnvironment().getProperty("azure.credentials.clientsecret"); + String tenantId = context.getEnvironment().getProperty("azure.credentials.tenantid"); + String appId = context.getEnvironment().getProperty("azure.credentials.appid"); + + return StringUtils.hasText(clientId) && StringUtils.hasText(clientSecret) + && StringUtils.hasText(tenantId) && StringUtils.hasText(appId); + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 1a05e54..752212a 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -37,6 +37,9 @@ public class AzureADUserSyncService { fixedDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.fixed-delay-ms}" ) private void pullUsersAndRoles() { + + validateConfig(); + log.info("*** <<< Starting to pull users from Azure AD >>> ***"); long startTime = System.currentTimeMillis(); @@ -62,6 +65,19 @@ private void pullUsersAndRoles() { log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); } + private void validateConfig() { + if (isNullOrEmpty(config.getClientId()) || + isNullOrEmpty(config.getClientSecret()) || + isNullOrEmpty(config.getTenantId()) || + isNullOrEmpty(config.getAppId())) { + throw new IllegalArgumentException("Azure AD configuration is not properly set."); + } + } + + private boolean isNullOrEmpty(String value) { + return value == null || value.isBlank() || "null".equalsIgnoreCase(value); + } + private void processUsers(UserCollectionPage userCollectionPage) { int usersProcessed = 0; UserCollectionPage currentPage = userCollectionPage; diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index 6c6d4eb..df55181 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -1,9 +1,9 @@ azure: credentials: - clientid: ${fint.flyt.azure.client-id} - clientsecret: ${fint.flyt.azure.client-secret} - tenantid: ${fint.flyt.azure.tenant-id} - appid: ${fint.flyt.azure.app-id} + clientid: ${fint.flyt.azure.client-id:null} + clientsecret: ${fint.flyt.azure.client-secret:null} + tenantid: ${fint.flyt.azure.tenant-id:null} + appid: ${fint.flyt.azure.app-id:null} spring: security: From 20b4c3547e11174e9316829b4bdd175591a7173d Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 20 Jun 2024 10:54:46 +0200 Subject: [PATCH 14/50] add vestfold and telemark to fetch 1password azure credentials --- kustomize/overlays/fintlabs-no/beta/onePassword.yaml | 7 ------- .../overlays/telemarkfylke-no/api/kustomization.yaml | 12 ++++++++++++ .../overlays/vestfoldfylke-no/api/kustomization.yaml | 12 ++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) delete mode 100644 kustomize/overlays/fintlabs-no/beta/onePassword.yaml diff --git a/kustomize/overlays/fintlabs-no/beta/onePassword.yaml b/kustomize/overlays/fintlabs-no/beta/onePassword.yaml deleted file mode 100644 index 1639654..0000000 --- a/kustomize/overlays/fintlabs-no/beta/onePassword.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: onepassword.com/v1 -kind: OnePasswordItem -metadata: - name: fint-flyt-authorization-service -spec: - itemPath: "path set in overlay" - diff --git a/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml b/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml index 0485255..be2fa6d 100644 --- a/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml @@ -34,6 +34,18 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: add + path: "/spec/envFrom/0" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/telemarkfylke-no/items/FINT-Flyt-Role-TelemarkFK" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml b/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml index 06c911c..818e914 100644 --- a/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml @@ -34,6 +34,18 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: add + path: "/spec/envFrom/0" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/vestfoldfylke-no/items/FINT-Flyt-Role-VestfoldFK" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file From 4d65312e5ab1ba82b7ea200a48e9595cb2545bc7 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 21 Jun 2024 08:27:23 +0200 Subject: [PATCH 15/50] add ofk to fetch 1password azure credentials --- kustomize/overlays/ofk-no/api/kustomization.yaml | 12 ++++++++++++ kustomize/overlays/ofk-no/beta/kustomization.yaml | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/kustomize/overlays/ofk-no/api/kustomization.yaml b/kustomize/overlays/ofk-no/api/kustomization.yaml index 69151f2..152a456 100644 --- a/kustomize/overlays/ofk-no/api/kustomization.yaml +++ b/kustomize/overlays/ofk-no/api/kustomization.yaml @@ -47,6 +47,18 @@ patches: value: secretRef: name: fint-flyt-vigo-oauth2-client + - op: add + path: "/spec/envFrom/2" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-ofk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/ofk-no/beta/kustomization.yaml b/kustomize/overlays/ofk-no/beta/kustomization.yaml index 9b540aa..56c0acb 100644 --- a/kustomize/overlays/ofk-no/beta/kustomization.yaml +++ b/kustomize/overlays/ofk-no/beta/kustomization.yaml @@ -47,6 +47,18 @@ patches: value: secretRef: name: fint-flyt-vigo-oauth2-client + - op: add + path: "/spec/envFrom/2" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-beta-vault/items/fint-flyt-authorization-service-ofk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file From ebb9f99f43381f30b6b900d47ad854a5cbf825b5 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 21 Jun 2024 10:17:13 +0200 Subject: [PATCH 16/50] add afk, bfk, telemark, vestfold, innlandet to fetch 1password azure credentials --- kustomize/overlays/afk-no/api/kustomization.yaml | 12 ++++++++++++ kustomize/overlays/bfk-no/api/kustomization.yaml | 12 ++++++++++++ .../innlandetfylke-no/api/kustomization.yaml | 12 ++++++++++++ .../overlays/telemarkfylke-no/api/kustomization.yaml | 2 +- .../overlays/vestfoldfylke-no/api/kustomization.yaml | 2 +- 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/kustomize/overlays/afk-no/api/kustomization.yaml b/kustomize/overlays/afk-no/api/kustomization.yaml index d21cd97..7e83a5f 100644 --- a/kustomize/overlays/afk-no/api/kustomization.yaml +++ b/kustomize/overlays/afk-no/api/kustomization.yaml @@ -41,6 +41,18 @@ patches: value: secretRef: name: fint-flyt-acos-oauth2-client + - op: add + path: "/spec/envFrom/1" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-afk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/bfk-no/api/kustomization.yaml b/kustomize/overlays/bfk-no/api/kustomization.yaml index 901c792..6413af8 100644 --- a/kustomize/overlays/bfk-no/api/kustomization.yaml +++ b/kustomize/overlays/bfk-no/api/kustomization.yaml @@ -41,6 +41,18 @@ patches: value: secretRef: name: fint-flyt-acos-oauth2-client + - op: add + path: "/spec/envFrom/1" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-bfk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/innlandetfylke-no/api/kustomization.yaml b/kustomize/overlays/innlandetfylke-no/api/kustomization.yaml index f558d80..a281ed7 100644 --- a/kustomize/overlays/innlandetfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/innlandetfylke-no/api/kustomization.yaml @@ -34,6 +34,18 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: add + path: "/spec/envFrom/0" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-innlandetfylke-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml b/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml index be2fa6d..cef99ac 100644 --- a/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/telemarkfylke-no/api/kustomization.yaml @@ -45,7 +45,7 @@ patches: - patch: |- - op: replace path: "/spec/itemPath" - value: "vaults/telemarkfylke-no/items/FINT-Flyt-Role-TelemarkFK" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-telemarkfylke-no" target: kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml b/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml index 818e914..411fae4 100644 --- a/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/vestfoldfylke-no/api/kustomization.yaml @@ -45,7 +45,7 @@ patches: - patch: |- - op: replace path: "/spec/itemPath" - value: "vaults/vestfoldfylke-no/items/FINT-Flyt-Role-VestfoldFK" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-vestfoldfylke-no" target: kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file From 89386e9d5febc5292e87b2b687cdb8a8f6a6463a Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 24 Jun 2024 13:06:15 +0200 Subject: [PATCH 17/50] add try catch to prevent empty lists on graph error when updating user permissions --- .../services/AzureADUserSyncService.java | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 752212a..371f928 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -84,52 +84,51 @@ private void processUsers(UserCollectionPage userCollectionPage) { List userPermissionList = new ArrayList<>(); List azureUserCaches = new ArrayList<>(); + try { + do { + for (User user : currentPage.getCurrentPage()) { - do { - for (User user : currentPage.getCurrentPage()) { + if (user.mail == null) { + continue; + } - if (user.mail == null) { - continue; - } + usersProcessed++; - usersProcessed++; + List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); - List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); + if (isPermittedRole(userRoles)) { + UserPermission userPermission = UserPermission + .builder() + .objectIdentifier(user.id) + .build(); + userPermissionList.add(userPermission); - if (isPermittedRole(userRoles)) { - UserPermission userPermission = UserPermission - .builder() - .objectIdentifier(user.id) - .build(); - userPermissionList.add(userPermission); + AzureUserCache azureUserCache = AzureUserCache + .builder() + .objectIdentifier(user.id) + .email(Objects.requireNonNull(user.mail).toLowerCase()) + .build(); + azureUserCaches.add(azureUserCache); + } - AzureUserCache azureUserCache = AzureUserCache - .builder() - .objectIdentifier(user.id) - .email(Objects.requireNonNull(user.mail).toLowerCase()) - .build(); - azureUserCaches.add(azureUserCache); } + if (currentPage.getNextPage() != null) { + currentPage = currentPage.getNextPage().buildRequest().get(); + } else { + currentPage = null; + } + } while (currentPage != null); - } - if (currentPage.getNextPage() != null) { - currentPage = currentPage.getNextPage().buildRequest().get(); - } else { - currentPage = null; - } - } while (currentPage != null); - - if (!userPermissionList.isEmpty()) { userPermissionService.refreshUserPermissions(userPermissionList); userPermissionList.forEach(userPermission -> log.info("Saving user permission {} in db", userPermission.getObjectIdentifier())); - } - if (!azureUserCaches.isEmpty()) { azureUserCacheService.refreshAzureUserCaches(azureUserCaches); azureUserCaches.forEach(azureUserCache -> log.debug("Saving azure user {} in cache", azureUserCache.getEmail())); - } - log.info("{} User objects processed in Azure AD", usersProcessed); + log.info("{} User objects processed in Azure AD", usersProcessed); + } catch (Exception e) { + log.error("An error occurred while processing user permissions: {}", e.getMessage()); + } } private boolean isPermittedRole(List userRoles) { From 9b4b7c458baaa07792c1aed4ed235ccdfb653d2d Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 24 Jun 2024 13:36:09 +0200 Subject: [PATCH 18/50] add name for user in cache --- .../adminuser/AdminUserController.java | 1 + .../authorization/user/UserPermissionDto.java | 1 + .../no/fintlabs/flyt/azure/StringUtils.java | 25 +++++++++++++++++++ .../flyt/azure/models/AzureUserCache.java | 1 + .../flyt/azure/models/ConfigUser.java | 4 ++- .../services/AzureADUserSyncService.java | 4 +++ 6 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/main/java/no/fintlabs/flyt/azure/StringUtils.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index 07a3c95..0b20b78 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -115,6 +115,7 @@ private UserPermissionDto buildUserPermissionDto(UserPermission userPermission) .builder() .objectIdentifier(userPermission.getObjectIdentifier()) .email(azureUserCache != null ? azureUserCache.getEmail() : null) + .name(azureUserCache != null ? azureUserCache.getName() : null) .sourceApplicationIds(userPermission.getSourceApplicationIds()) .build(); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java index 3ec0361..43b4602 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java @@ -16,6 +16,7 @@ public class UserPermissionDto { @NotNull private String objectIdentifier; private String email; + private String name; @NotNull private List sourceApplicationIds; } diff --git a/src/main/java/no/fintlabs/flyt/azure/StringUtils.java b/src/main/java/no/fintlabs/flyt/azure/StringUtils.java new file mode 100644 index 0000000..cb0d21b --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/StringUtils.java @@ -0,0 +1,25 @@ +package no.fintlabs.flyt.azure; + +import java.util.Locale; + +public class StringUtils { + + public static String capitalizeFirstLetterOfEachWord(String input) { + if (input == null || input.isEmpty()) { + return input; + } + + String[] words = input.split("\\s+"); + StringBuilder capitalizedString = new StringBuilder(); + + for (String word : words) { + if (!word.isEmpty()) { + capitalizedString.append(word.substring(0, 1).toUpperCase(Locale.ROOT)) + .append(word.substring(1).toLowerCase(Locale.ROOT)) + .append(" "); + } + } + + return capitalizedString.toString().trim(); + } +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java b/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java index a424e47..d62114c 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java @@ -8,4 +8,5 @@ public class AzureUserCache { private String objectIdentifier; private String email; + private String name; } diff --git a/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java b/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java index f5144bc..d5cf5d1 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java @@ -14,7 +14,9 @@ public class ConfigUser { private static final List userAttributes = Arrays.asList( "id", - "mail" + "mail", + "givenname", + "surname" ); public List allAttributes() { diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 371f928..b5cc0ed 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -8,6 +8,7 @@ import no.fintlabs.flyt.authorization.user.UserPermission; import no.fintlabs.flyt.authorization.user.UserPermissionService; import no.fintlabs.flyt.azure.Config; +import no.fintlabs.flyt.azure.StringUtils; import no.fintlabs.flyt.azure.models.AzureUserCache; import no.fintlabs.flyt.azure.models.ConfigUser; import no.fintlabs.flyt.azure.models.PermittedAppRoles; @@ -96,6 +97,8 @@ private void processUsers(UserCollectionPage userCollectionPage) { List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); + log.info("Mail: {} Given name: {} Surname: {}", user.mail, user.givenName, user.surname); + if (isPermittedRole(userRoles)) { UserPermission userPermission = UserPermission .builder() @@ -107,6 +110,7 @@ private void processUsers(UserCollectionPage userCollectionPage) { .builder() .objectIdentifier(user.id) .email(Objects.requireNonNull(user.mail).toLowerCase()) + .name(StringUtils.capitalizeFirstLetterOfEachWord(user.givenName) + " " + user.surname) .build(); azureUserCaches.add(azureUserCache); } From d77946fe3596df6dfdacfca50e8afe8fb5756d1e Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 24 Jun 2024 14:15:17 +0200 Subject: [PATCH 19/50] order by name instead of mail --- .../flyt/authorization/adminuser/AdminUserController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index 0b20b78..cb3bb50 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -56,7 +56,7 @@ public Mono>> getUserPermissions( .map(userPermissions -> { List userPermissionDtos = new java.util.ArrayList<>(List.of()); userPermissions.forEach(userPermission -> userPermissionDtos.add(buildUserPermissionDto(userPermission))); - userPermissionDtos.sort(Comparator.comparing(UserPermissionDto::getEmail, Comparator.nullsLast(String::compareTo))); + userPermissionDtos.sort(Comparator.comparing(UserPermissionDto::getName, Comparator.nullsLast(String::compareTo))); return ResponseEntity.ok().body(userPermissionDtos); }); } else { @@ -98,7 +98,7 @@ public Mono>> setUserPermissions( }).subscribeOn(Schedulers.boundedElastic())) .collectList() .map(userPermissionDtoList -> { - userPermissionDtoList.sort(Comparator.comparing(UserPermissionDto::getEmail, Comparator.nullsLast(String::compareTo))); + userPermissionDtoList.sort(Comparator.comparing(UserPermissionDto::getName, Comparator.nullsLast(String::compareTo))); return ResponseEntity.ok().body(userPermissionDtoList); }); } else { From 1f7b03764583b95533f0f26a0035fb20abd62d20 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 24 Jun 2024 14:16:32 +0200 Subject: [PATCH 20/50] remove unneeded logging --- .../no/fintlabs/flyt/azure/services/AzureADUserSyncService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index b5cc0ed..088d505 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -97,8 +97,6 @@ private void processUsers(UserCollectionPage userCollectionPage) { List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); - log.info("Mail: {} Given name: {} Surname: {}", user.mail, user.givenName, user.surname); - if (isPermittedRole(userRoles)) { UserPermission userPermission = UserPermission .builder() From c611fbdf347139502f62d7039b0198fe948bcb79 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 26 Jun 2024 08:01:27 +0200 Subject: [PATCH 21/50] move success logging within try catch --- .../azure/services/AzureADUserSyncService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 088d505..61fb2bc 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -54,16 +54,16 @@ private void pullUsersAndRoles() { .get(); processUsers(usersPage); + + long endTime = System.currentTimeMillis(); + long elapsedTimeInSeconds = (endTime - startTime) / 1000; + long minutes = elapsedTimeInSeconds / 60; + long seconds = elapsedTimeInSeconds % 60; + + log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); } catch (Exception e) { log.error("Error fetching users : {}", e.getMessage(), e); } - - long endTime = System.currentTimeMillis(); - long elapsedTimeInSeconds = (endTime - startTime) / 1000; - long minutes = elapsedTimeInSeconds / 60; - long seconds = elapsedTimeInSeconds % 60; - - log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); } private void validateConfig() { From 1da53eb8590f2f97bdcb80a112271f8e7492d82b Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 26 Jun 2024 08:54:43 +0200 Subject: [PATCH 22/50] remove possibility to create new users on endpoint for setting user permissions --- .../azure/services/AzureAppGraphService.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java index d045c16..0d3c891 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java @@ -3,6 +3,7 @@ import com.microsoft.graph.models.*; import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.requests.GraphServiceClient; +import com.microsoft.graph.requests.ServicePrincipalCollectionPage; import okhttp3.Request; import org.springframework.stereotype.Service; @@ -25,11 +26,20 @@ public AzureAppGraphService( public List getAppRoleAssignments(String appId) { List requestOptions = new ArrayList<>(); requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); - List servicePrincipals = Objects.requireNonNull(graphService - .servicePrincipals() - .buildRequest(requestOptions) - .get()) - .getCurrentPage(); + + ServicePrincipalCollectionPage servicePrincipalCollectionPage = graphService + .servicePrincipals() + .buildRequest(requestOptions) + .get(); + + Objects.requireNonNull(servicePrincipalCollectionPage); + +// if (servicePrincipalCollectionPage.getCount()) { +// +// } + + List servicePrincipals = servicePrincipalCollectionPage.getCurrentPage(); + if (!servicePrincipals.isEmpty()) { ServicePrincipal servicePrincipal = servicePrincipals.get(0); if (servicePrincipal != null && servicePrincipal.id != null) { From 906b3c9fbcfe0b44457162a8f420c9b4eac38b81 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 26 Jun 2024 12:57:57 +0200 Subject: [PATCH 23/50] FFS-1102 make endpoints RESTful and refactor --- .../flyt/authorization/AuthorizationUtil.java | 32 +++++++ .../adminuser/AdminUserController.java | 84 +++++++------------ .../user/UserPermissionController.java | 21 +++-- 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java index d40d833..d2f40c1 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java +++ b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java @@ -1,11 +1,43 @@ package no.fintlabs.flyt.authorization; +import no.fintlabs.flyt.authorization.user.UserPermission; +import no.fintlabs.flyt.authorization.user.UserPermissionDto; +import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; +import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; @Service public class AuthorizationUtil { + + private final AzureUserCacheRepository azureUserCacheRepository; + + public AuthorizationUtil(AzureUserCacheRepository azureUserCacheRepository) { + this.azureUserCacheRepository = azureUserCacheRepository; + } + public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticationToken) { return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString(); } + + public Mono isAdmin(Mono authenticationMono) { + return authenticationMono + .map(authentication -> authentication != null && authentication.getAuthorities().stream() + .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); + } + + public UserPermissionDto buildUserPermissionDto(UserPermission userPermission) { + + AzureUserCache azureUserCache = azureUserCacheRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()); + + return UserPermissionDto + .builder() + .objectIdentifier(userPermission.getObjectIdentifier()) + .email(azureUserCache != null ? azureUserCache.getEmail() : null) + .name(azureUserCache != null ? azureUserCache.getName() : null) + .sourceApplicationIds(userPermission.getSourceApplicationIds()) + .build(); + } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java index cb3bb50..1e83190 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java @@ -1,10 +1,9 @@ package no.fintlabs.flyt.authorization.adminuser; +import no.fintlabs.flyt.authorization.AuthorizationUtil; import no.fintlabs.flyt.authorization.user.UserPermission; import no.fintlabs.flyt.authorization.user.UserPermissionDto; import no.fintlabs.flyt.authorization.user.UserPermissionRepository; -import no.fintlabs.flyt.azure.models.AzureUserCache; -import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -21,42 +20,41 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; @RestController -@RequestMapping(INTERNAL_API + "/authorization/adminuser") +@RequestMapping(INTERNAL_API + "/admin/authorization") public class AdminUserController { private final UserPermissionRepository userPermissionRepository; - private final AzureUserCacheRepository azureUserCacheRepository; + private final AuthorizationUtil authorizationUtil; public AdminUserController( UserPermissionRepository userPermissionRepository, - AzureUserCacheRepository azureUserCacheRepository + AuthorizationUtil authorizationUtil ) { this.userPermissionRepository = userPermissionRepository; - this.azureUserCacheRepository = azureUserCacheRepository; + this.authorizationUtil = authorizationUtil; } - - @GetMapping("check-is-admin") - public Mono> checkAdminUser( - @AuthenticationPrincipal Mono authenticationMono - ) { - return isAdmin(authenticationMono) - .map(isAdmin -> ResponseEntity.ok(AdminUser.builder().admin(isAdmin).build())); - } - - @GetMapping("userpermissions") + @GetMapping("users") public Mono>> getUserPermissions( @AuthenticationPrincipal Mono authenticationMono ) { - return isAdmin(authenticationMono) + return authorizationUtil.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (isAdmin) { return Mono.fromCallable(userPermissionRepository::findAll) .subscribeOn(Schedulers.boundedElastic()) .map(userPermissions -> { List userPermissionDtos = new java.util.ArrayList<>(List.of()); - userPermissions.forEach(userPermission -> userPermissionDtos.add(buildUserPermissionDto(userPermission))); - userPermissionDtos.sort(Comparator.comparing(UserPermissionDto::getName, Comparator.nullsLast(String::compareTo))); + userPermissions.forEach( + userPermission -> userPermissionDtos.add( + authorizationUtil.buildUserPermissionDto(userPermission) + ) + ); + userPermissionDtos.sort( + Comparator.comparing( + UserPermissionDto::getName, Comparator.nullsLast(String::compareTo) + ) + ); return ResponseEntity.ok().body(userPermissionDtos); }); } else { @@ -65,40 +63,32 @@ public Mono>> getUserPermissions( }); } - @PostMapping("userpermissions") + @PutMapping("users") public Mono>> setUserPermissions( @RequestBody List userPermissionDtos, @AuthenticationPrincipal Mono authenticationMono ) { - return isAdmin(authenticationMono) + return authorizationUtil.isAdmin(authenticationMono) .publishOn(Schedulers.boundedElastic()) .flatMap(isAdmin -> { if (isAdmin) { - return Flux.fromIterable(userPermissionDtos) .flatMap(userPermissionDto -> Mono.fromCallable(() -> { Optional userPermissionOptional = userPermissionRepository .findByObjectIdentifier(userPermissionDto.getObjectIdentifier()); - if (userPermissionOptional.isPresent()) { - UserPermission userPermission = userPermissionOptional.get(); - userPermission.setSourceApplicationIds(userPermissionDto - .getSourceApplicationIds()); + return userPermissionOptional.map(userPermission -> { + userPermission.setSourceApplicationIds(userPermissionDto.getSourceApplicationIds()); userPermissionRepository.save(userPermission); - - return buildUserPermissionDto(userPermission); - } else { - UserPermission newUserPermission = UserPermission.builder() - .objectIdentifier(userPermissionDto.getObjectIdentifier()) - .sourceApplicationIds(userPermissionDto.getSourceApplicationIds()) - .build(); - userPermissionRepository.save(newUserPermission); - - return buildUserPermissionDto(newUserPermission); - } + return authorizationUtil.buildUserPermissionDto(userPermission); + }).orElse(null); }).subscribeOn(Schedulers.boundedElastic())) .collectList() .map(userPermissionDtoList -> { - userPermissionDtoList.sort(Comparator.comparing(UserPermissionDto::getName, Comparator.nullsLast(String::compareTo))); + userPermissionDtoList.sort( + Comparator.comparing( + UserPermissionDto::getName, Comparator.nullsLast(String::compareTo) + ) + ); return ResponseEntity.ok().body(userPermissionDtoList); }); } else { @@ -107,23 +97,5 @@ public Mono>> setUserPermissions( }); } - private UserPermissionDto buildUserPermissionDto(UserPermission userPermission) { - - AzureUserCache azureUserCache = azureUserCacheRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()); - - return UserPermissionDto - .builder() - .objectIdentifier(userPermission.getObjectIdentifier()) - .email(azureUserCache != null ? azureUserCache.getEmail() : null) - .name(azureUserCache != null ? azureUserCache.getName() : null) - .sourceApplicationIds(userPermission.getSourceApplicationIds()) - .build(); - } - - private Mono isAdmin(Mono authenticationMono) { - return authenticationMono - .map(authentication -> authentication != null && authentication.getAuthorities().stream() - .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); - } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java index ca9c176..faa92e6 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java @@ -1,6 +1,7 @@ package no.fintlabs.flyt.authorization.user; import no.fintlabs.flyt.authorization.AuthorizationUtil; +import no.fintlabs.flyt.authorization.adminuser.AdminUser; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -13,7 +14,7 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; -@RequestMapping(INTERNAL_API + "/authorization/user") +@RequestMapping(INTERNAL_API + "/authorization/self-user") @RestController public class UserPermissionController { @@ -28,12 +29,20 @@ public UserPermissionController( this.authorizationUtil = authorizationUtil; } - @GetMapping("check-authorized") + @GetMapping("isAuthorized") public ResponseEntity checkAuthorization() { return ResponseEntity.ok("User authorized"); } - @GetMapping("permission") + @GetMapping("isAdmin") + public Mono> checkAdminUser( + @AuthenticationPrincipal Mono authenticationMono + ) { + return authorizationUtil.isAdmin(authenticationMono) + .map(isAdmin -> ResponseEntity.ok(AdminUser.builder().admin(isAdmin).build())); + } + + @GetMapping public Mono> getSourceApplications( @AuthenticationPrincipal Mono authenticationMono ) { @@ -43,11 +52,7 @@ public Mono> getSourceApplications( .publishOn(Schedulers.boundedElastic()) .map(userPermissionRepository::findByObjectIdentifier) .map(optionalUserPermission -> optionalUserPermission.map(userPermission -> ResponseEntity.ok( - UserPermissionDto - .builder() - .objectIdentifier(userPermission.getObjectIdentifier()) - .sourceApplicationIds(userPermission.getSourceApplicationIds()) - .build() + authorizationUtil.buildUserPermissionDto(userPermission) )).orElseGet(() -> ResponseEntity.notFound().build()) ); } From 9a71eee49e95a93670cb063ac94498a48a260bc4 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 26 Jun 2024 13:08:08 +0200 Subject: [PATCH 24/50] FFS-1113 add azure ad credentials for nfk in api --- kustomize/overlays/nfk-no/api/kustomization.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kustomize/overlays/nfk-no/api/kustomization.yaml b/kustomize/overlays/nfk-no/api/kustomization.yaml index 2ede0c2..49cf0b9 100644 --- a/kustomize/overlays/nfk-no/api/kustomization.yaml +++ b/kustomize/overlays/nfk-no/api/kustomization.yaml @@ -34,6 +34,18 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: add + path: "/spec/envFrom/0" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-nfk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file From 0127592c4d231f8c98ac79ba3b2130caebd64762 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 26 Jun 2024 15:34:16 +0200 Subject: [PATCH 25/50] wip commit --- build.gradle | 2 + .../flyt/authorization/AuthorizationUtil.java | 30 +++--- .../authorization/adminuser/AdminUser.java | 10 -- .../adminuser/AdminUserController.java | 101 ------------------ .../user/UserPermissionService.java | 55 ---------- .../MeController.java} | 15 ++- .../userpermission/RestrictedPageAccess.java | 10 ++ .../User.java} | 6 +- .../userpermission/UserController.java | 101 ++++++++++++++++++ .../userpermission/UserPermission.java | 20 ++++ .../UserPermissionEntity.java} | 5 +- .../UserPermissionRepository.java | 10 +- .../userpermission/UserPermissionService.java | 70 ++++++++++++ .../userpermission/UserService.java | 43 ++++++++ ...ureUserCache.java => UserDisplayText.java} | 3 +- .../AzureUserCacheRepository.java | 20 ++-- .../UserDisplayTextCacheConfiguration.java | 16 +++ .../services/AzureADUserSyncService.java | 26 ++--- .../azure/services/AzureUserCacheService.java | 16 +-- 19 files changed, 327 insertions(+), 232 deletions(-) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUser.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java rename src/main/java/no/fintlabs/flyt/authorization/{user/UserPermissionController.java => userpermission/MeController.java} (81%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java rename src/main/java/no/fintlabs/flyt/authorization/{user/UserPermissionDto.java => userpermission/User.java} (82%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java rename src/main/java/no/fintlabs/flyt/authorization/{user/UserPermission.java => userpermission/UserPermissionEntity.java} (86%) rename src/main/java/no/fintlabs/flyt/authorization/{user => userpermission}/UserPermissionRepository.java (59%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java rename src/main/java/no/fintlabs/flyt/azure/models/{AzureUserCache.java => UserDisplayText.java} (69%) create mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java diff --git a/build.gradle b/build.gradle index b0e2493..dee91af 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,8 @@ dependencies { implementation 'no.fintlabs:fint-kafka:4.0.1' implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0' + implementation 'no.fintlabs:fint-flyt-cache:1.2.3' + implementation 'com.azure:azure-identity:1.10.2' implementation 'com.microsoft.graph:microsoft-graph:5.80.0' diff --git a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java index d2f40c1..3c88dd2 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java +++ b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java @@ -1,8 +1,8 @@ package no.fintlabs.flyt.authorization; -import no.fintlabs.flyt.authorization.user.UserPermission; -import no.fintlabs.flyt.authorization.user.UserPermissionDto; -import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.authorization.userpermission.UserPermissionEntity; +import no.fintlabs.flyt.authorization.userpermission.UserPermission; +import no.fintlabs.flyt.azure.models.UserDisplayText; import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; @@ -28,16 +28,16 @@ public Mono isAdmin(Mono authenticationMono) { .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); } - public UserPermissionDto buildUserPermissionDto(UserPermission userPermission) { - - AzureUserCache azureUserCache = azureUserCacheRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()); - - return UserPermissionDto - .builder() - .objectIdentifier(userPermission.getObjectIdentifier()) - .email(azureUserCache != null ? azureUserCache.getEmail() : null) - .name(azureUserCache != null ? azureUserCache.getName() : null) - .sourceApplicationIds(userPermission.getSourceApplicationIds()) - .build(); - } +// public UserPermission buildUserPermissionDto(UserPermissionEntity userPermissionEntity) { +// +// UserDisplayText userDisplayText = azureUserCacheRepository.findByObjectIdentifier(userPermissionEntity.getObjectIdentifier()); +// +// return UserPermission +// .builder() +// .objectIdentifier(userPermissionEntity.getObjectIdentifier()) +// .email(userDisplayText != null ? userDisplayText.getEmail() : null) +// .name(userDisplayText != null ? userDisplayText.getName() : null) +// .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) +// .build(); +// } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUser.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUser.java deleted file mode 100644 index 9e94d74..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUser.java +++ /dev/null @@ -1,10 +0,0 @@ -package no.fintlabs.flyt.authorization.adminuser; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class AdminUser { - private boolean admin; -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java b/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java deleted file mode 100644 index 1e83190..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/adminuser/AdminUserController.java +++ /dev/null @@ -1,101 +0,0 @@ -package no.fintlabs.flyt.authorization.adminuser; - -import no.fintlabs.flyt.authorization.AuthorizationUtil; -import no.fintlabs.flyt.authorization.user.UserPermission; -import no.fintlabs.flyt.authorization.user.UserPermissionDto; -import no.fintlabs.flyt.authorization.user.UserPermissionRepository; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; - -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; - -@RestController -@RequestMapping(INTERNAL_API + "/admin/authorization") -public class AdminUserController { - - private final UserPermissionRepository userPermissionRepository; - private final AuthorizationUtil authorizationUtil; - - public AdminUserController( - UserPermissionRepository userPermissionRepository, - AuthorizationUtil authorizationUtil - ) { - this.userPermissionRepository = userPermissionRepository; - this.authorizationUtil = authorizationUtil; - } - - @GetMapping("users") - public Mono>> getUserPermissions( - @AuthenticationPrincipal Mono authenticationMono - ) { - return authorizationUtil.isAdmin(authenticationMono) - .flatMap(isAdmin -> { - if (isAdmin) { - return Mono.fromCallable(userPermissionRepository::findAll) - .subscribeOn(Schedulers.boundedElastic()) - .map(userPermissions -> { - List userPermissionDtos = new java.util.ArrayList<>(List.of()); - userPermissions.forEach( - userPermission -> userPermissionDtos.add( - authorizationUtil.buildUserPermissionDto(userPermission) - ) - ); - userPermissionDtos.sort( - Comparator.comparing( - UserPermissionDto::getName, Comparator.nullsLast(String::compareTo) - ) - ); - return ResponseEntity.ok().body(userPermissionDtos); - }); - } else { - return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); - } - }); - } - - @PutMapping("users") - public Mono>> setUserPermissions( - @RequestBody List userPermissionDtos, - @AuthenticationPrincipal Mono authenticationMono - ) { - return authorizationUtil.isAdmin(authenticationMono) - .publishOn(Schedulers.boundedElastic()) - .flatMap(isAdmin -> { - if (isAdmin) { - return Flux.fromIterable(userPermissionDtos) - .flatMap(userPermissionDto -> Mono.fromCallable(() -> { - Optional userPermissionOptional = userPermissionRepository - .findByObjectIdentifier(userPermissionDto.getObjectIdentifier()); - return userPermissionOptional.map(userPermission -> { - userPermission.setSourceApplicationIds(userPermissionDto.getSourceApplicationIds()); - userPermissionRepository.save(userPermission); - return authorizationUtil.buildUserPermissionDto(userPermission); - }).orElse(null); - }).subscribeOn(Schedulers.boundedElastic())) - .collectList() - .map(userPermissionDtoList -> { - userPermissionDtoList.sort( - Comparator.comparing( - UserPermissionDto::getName, Comparator.nullsLast(String::compareTo) - ) - ); - return ResponseEntity.ok().body(userPermissionDtoList); - }); - } else { - return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); - } - }); - } - - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java deleted file mode 100644 index 5593e3c..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ /dev/null @@ -1,55 +0,0 @@ -package no.fintlabs.flyt.authorization.user; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@Service -@Slf4j -public class UserPermissionService { - - private final UserPermissionRepository userPermissionRepository; - - public UserPermissionService(UserPermissionRepository userPermissionRepository) { - this.userPermissionRepository = userPermissionRepository; - } - - public void refreshUserPermissions(List userPermissions) { - - deleteUserPermissionsNotInList(userPermissions); - - List newUserPermissionList = new ArrayList<>(); - - userPermissions.forEach(userPermission -> { - Optional existingUserPermission = userPermissionRepository - .findByObjectIdentifier(userPermission.getObjectIdentifier()); - if (existingUserPermission.isEmpty()) { - newUserPermissionList.add(userPermission); - } - }); - - userPermissionRepository.saveAll(newUserPermissionList); - } - - private void deleteUserPermissionsNotInList(List userPermissions) { - List allCurrentUserPermissions = userPermissionRepository.findAll(); - - List inputUserPermissionIdentifiers = userPermissions.stream() - .map(UserPermission::getObjectIdentifier) - .toList(); - - List userPermissionsToDelete = allCurrentUserPermissions.stream() - .filter(userPermission -> !inputUserPermissionIdentifiers.contains(userPermission.getObjectIdentifier())) - .toList(); - - userPermissionRepository.deleteAll(userPermissionsToDelete); - - if (!userPermissionsToDelete.isEmpty()) { - log.info("Deleted {} user permissions", userPermissionsToDelete.size()); - } - } - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java similarity index 81% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java rename to src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java index faa92e6..f83df51 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java @@ -1,7 +1,6 @@ -package no.fintlabs.flyt.authorization.user; +package no.fintlabs.flyt.authorization.userpermission; import no.fintlabs.flyt.authorization.AuthorizationUtil; -import no.fintlabs.flyt.authorization.adminuser.AdminUser; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -14,14 +13,14 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; -@RequestMapping(INTERNAL_API + "/authorization/self-user") +@RequestMapping(INTERNAL_API + "/authorization/me") @RestController -public class UserPermissionController { +public class MeController { private final UserPermissionRepository userPermissionRepository; private final AuthorizationUtil authorizationUtil; - public UserPermissionController( + public MeController( UserPermissionRepository userPermissionRepository, AuthorizationUtil authorizationUtil ) { @@ -35,15 +34,15 @@ public ResponseEntity checkAuthorization() { } @GetMapping("isAdmin") - public Mono> checkAdminUser( + public Mono> checkAdminUser( @AuthenticationPrincipal Mono authenticationMono ) { return authorizationUtil.isAdmin(authenticationMono) - .map(isAdmin -> ResponseEntity.ok(AdminUser.builder().admin(isAdmin).build())); + .map(isAdmin -> ResponseEntity.ok(RestrictedPageAccess.builder().userPermission(isAdmin).build())); } @GetMapping - public Mono> getSourceApplications( + public Mono> get( @AuthenticationPrincipal Mono authenticationMono ) { return authenticationMono diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java new file mode 100644 index 0000000..d22ae7f --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java @@ -0,0 +1,10 @@ +package no.fintlabs.flyt.authorization.userpermission; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class RestrictedPageAccess { + private boolean userPermission; +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java similarity index 82% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java rename to src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java index 43b4602..775cdd2 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionDto.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user; +package no.fintlabs.flyt.authorization.userpermission; import com.sun.istack.NotNull; import lombok.Builder; @@ -12,11 +12,11 @@ @Builder @EqualsAndHashCode @Jacksonized -public class UserPermissionDto { +public class User { @NotNull private String objectIdentifier; private String email; private String name; @NotNull private List sourceApplicationIds; -} +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java new file mode 100644 index 0000000..b4ecfc2 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java @@ -0,0 +1,101 @@ +package no.fintlabs.flyt.authorization.userpermission; + +import no.fintlabs.flyt.authorization.AuthorizationUtil; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; + +@RestController +@RequestMapping(INTERNAL_API + "/authorization/users") +public class UserController { + + private final AuthorizationUtil authorizationUtil; + private final UserService userService; + + public UserController( + AuthorizationUtil authorizationUtil, UserService userService + ) { + this.authorizationUtil = authorizationUtil; + this.userService = userService; + } + + @GetMapping + public Mono>> get( + @AuthenticationPrincipal Mono authenticationMono + ) { + return authorizationUtil.isAdmin(authenticationMono) + .flatMap(isAdmin -> { + if (!isAdmin) { + return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); + } + + // todo: add pagable with sorting + return Mono.fromCallable(userService::getUsers) + .map(ResponseEntity::ok); + +// return Mono.fromCallable(userPermissionRepository::findAll) +// .subscribeOn(Schedulers.boundedElastic()) +// .map(userPermissions -> { +// List users = new java.util.ArrayList<>(List.of()); +// userPermissions.forEach( +// userPermission -> users.add( +// authorizationUtil.buildUserPermissionDto(userPermission) +// ) +// ); +// users.sort( +// Comparator.comparing( +// User::getName, Comparator.nullsLast(String::compareTo) +// ) +// ); +// return ResponseEntity.ok().body(users); +// }); + }); + } + +// @PutMapping("users") +// public Mono>> setUserPermissions( +// @RequestBody List userPermissions, +// @AuthenticationPrincipal Mono authenticationMono +// ) { +// return authorizationUtil.isAdmin(authenticationMono) +// .publishOn(Schedulers.boundedElastic()) +// .flatMap(isAdmin -> { +// if (isAdmin) { +// return Flux.fromIterable(userPermissions) +// .flatMap(userPermissionDto -> Mono.fromCallable(() -> { +// Optional userPermissionOptional = userPermissionRepository +// .findByObjectIdentifier(userPermissionDto.getObjectIdentifier()); +// return userPermissionOptional.map(userPermission -> { +// userPermission.setSourceApplicationIds(userPermissionDto.getSourceApplicationIds()); +// userPermissionRepository.save(userPermission); +// return authorizationUtil.buildUserPermissionDto(userPermission); +// }).orElse(null); +// }).subscribeOn(Schedulers.boundedElastic())) +// .collectList() +// .map(userPermissionDtoList -> { +// userPermissionDtoList.sort( +// Comparator.comparing( +// UserPermission::getName, Comparator.nullsLast(String::compareTo) +// ) +// ); +// return ResponseEntity.ok().body(userPermissionDtoList); +// }); +// } else { +// return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); +// } +// }); +// } + + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java new file mode 100644 index 0000000..ee39408 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java @@ -0,0 +1,20 @@ +package no.fintlabs.flyt.authorization.userpermission; + +import com.sun.istack.NotNull; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; + +import java.util.List; + +@Getter +@Builder +@EqualsAndHashCode +@Jacksonized +public class UserPermission { + @NotNull + private String objectIdentifier; + @NotNull + private List sourceApplicationIds; +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermission.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java similarity index 86% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserPermission.java rename to src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java index f8eac08..c5fd080 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermission.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user; +package no.fintlabs.flyt.authorization.userpermission; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.*; @@ -11,7 +11,8 @@ @Entity @NoArgsConstructor @AllArgsConstructor -public class UserPermission { +@Table(name = "user_permission") +public class UserPermissionEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @JsonIgnore diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java similarity index 59% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java rename to src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java index 797f609..5fa9d3c 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user; +package no.fintlabs.flyt.authorization.userpermission; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -10,14 +10,14 @@ @Repository -public interface UserPermissionRepository extends JpaRepository { +public interface UserPermissionRepository extends JpaRepository { - Optional findByObjectIdentifier(String sub); + Optional findByObjectIdentifier(String sub); - default Map findAllAsMapWithObjectIdentifierAsKey() { + default Map findAllAsMapWithObjectIdentifierAsKey() { return findAll().stream() .collect(Collectors.toMap( - UserPermission::getObjectIdentifier, + UserPermissionEntity::getObjectIdentifier, Function.identity() )); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java new file mode 100644 index 0000000..f1ff98b --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java @@ -0,0 +1,70 @@ +package no.fintlabs.flyt.authorization.userpermission; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Service +@Slf4j +public class UserPermissionService { + + private final UserPermissionRepository userPermissionRepository; + + public UserPermissionService(UserPermissionRepository userPermissionRepository) { + this.userPermissionRepository = userPermissionRepository; + } + + public List getAll() { + return this.userPermissionRepository.findAll() + .stream() + .map(this::mapFromEntity) + .toList(); + } + + private UserPermission mapFromEntity(UserPermissionEntity userPermissionEntity) { + return UserPermission + .builder() + .objectIdentifier(userPermissionEntity.getObjectIdentifier()) + .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) + .build(); + } + + public void refreshUserPermissions(List userPermissionEntities) { + + deleteUserPermissionsNotInList(userPermissionEntities); + + List newUserPermissionEntityList = new ArrayList<>(); + + userPermissionEntities.forEach(userPermission -> { + Optional existingUserPermission = userPermissionRepository + .findByObjectIdentifier(userPermission.getObjectIdentifier()); + if (existingUserPermission.isEmpty()) { + newUserPermissionEntityList.add(userPermission); + } + }); + + userPermissionRepository.saveAll(newUserPermissionEntityList); + } + + private void deleteUserPermissionsNotInList(List userPermissionEntities) { + List allCurrentUserPermissionEntities = userPermissionRepository.findAll(); + + List inputUserPermissionIdentifiers = userPermissionEntities.stream() + .map(UserPermissionEntity::getObjectIdentifier) + .toList(); + + List userPermissionsToDeleteEntity = allCurrentUserPermissionEntities.stream() + .filter(userPermission -> !inputUserPermissionIdentifiers.contains(userPermission.getObjectIdentifier())) + .toList(); + + userPermissionRepository.deleteAll(userPermissionsToDeleteEntity); + + if (!userPermissionsToDeleteEntity.isEmpty()) { + log.info("Deleted {} user permissions", userPermissionsToDeleteEntity.size()); + } + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java new file mode 100644 index 0000000..eeafe87 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java @@ -0,0 +1,43 @@ +package no.fintlabs.flyt.authorization.userpermission; + +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class UserService { + + private final UserPermissionService userPermissionService; + private final FintCache userDisplayTextCache; + + public UserService( + UserPermissionService userPermissionService, + FintCache userDisplayTextCache + ) { + this.userPermissionService = userPermissionService; + this.userDisplayTextCache = userDisplayTextCache; + } + + public List getUsers() { + return userPermissionService.getAll() + .stream() + .map(this::mapToUserWithDisplayText) + .toList(); + } + + private User mapToUserWithDisplayText(UserPermission userPermission) { + User.UserBuilder userBuilder = User + .builder() + .objectIdentifier(userPermission.getObjectIdentifier()) + .sourceApplicationIds(userPermission.getSourceApplicationIds()); + + userDisplayTextCache.getOptional(userPermission.getObjectIdentifier()) + .ifPresent(userDisplayText -> userBuilder + .email(userDisplayText.getEmail()) + .name(userDisplayText.getName())); + + return userBuilder.build(); + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java similarity index 69% rename from src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java rename to src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java index d62114c..cd45f96 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/AzureUserCache.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java @@ -5,8 +5,7 @@ @Getter @Builder -public class AzureUserCache { - private String objectIdentifier; +public class UserDisplayText { private String email; private String name; } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java index a73b6da..a1f8a2d 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java @@ -1,6 +1,6 @@ package no.fintlabs.flyt.azure.repositories; -import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.models.UserDisplayText; import org.springframework.stereotype.Repository; import java.util.List; @@ -9,21 +9,21 @@ @Repository public class AzureUserCacheRepository { - private final Map azureUserCacheMap = new ConcurrentHashMap<>(); + private final Map azureUserCacheMap = new ConcurrentHashMap<>(); - public void save(AzureUserCache azureUserCache) { - azureUserCacheMap.put(azureUserCache.getObjectIdentifier(), azureUserCache); - } +// public void save(UserDisplayText userDisplayText) { +// azureUserCacheMap.put(userDisplayText.getObjectIdentifier(), userDisplayText); +// } - public void saveAll(List azureUserCaches) { - azureUserCaches.forEach(this::save); - } +// public void saveAll(List userDisplayTextCaches) { +// userDisplayTextCaches.forEach(this::save); +// } - public AzureUserCache findByObjectIdentifier(String objectIdentifier) { + public UserDisplayText findByObjectIdentifier(String objectIdentifier) { return azureUserCacheMap.get(objectIdentifier); } - public Map findAll() { + public Map findAll() { return azureUserCacheMap; } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java new file mode 100644 index 0000000..cfae0b3 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java @@ -0,0 +1,16 @@ +package no.fintlabs.flyt.azure.repositories; + +import no.fintlabs.cache.FintCache; +import no.fintlabs.cache.FintCacheManager; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class UserDisplayTextCacheConfiguration { + + @Bean + public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("userDisplayText", String.class, UserDisplayText.class); + } +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 61fb2bc..2dda453 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -5,11 +5,11 @@ import com.microsoft.graph.requests.UserCollectionPage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import no.fintlabs.flyt.authorization.user.UserPermission; -import no.fintlabs.flyt.authorization.user.UserPermissionService; +import no.fintlabs.flyt.authorization.userpermission.UserPermissionEntity; +import no.fintlabs.flyt.authorization.userpermission.UserPermissionService; import no.fintlabs.flyt.azure.Config; import no.fintlabs.flyt.azure.StringUtils; -import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.models.UserDisplayText; import no.fintlabs.flyt.azure.models.ConfigUser; import no.fintlabs.flyt.azure.models.PermittedAppRoles; import okhttp3.Request; @@ -83,8 +83,8 @@ private void processUsers(UserCollectionPage userCollectionPage) { int usersProcessed = 0; UserCollectionPage currentPage = userCollectionPage; - List userPermissionList = new ArrayList<>(); - List azureUserCaches = new ArrayList<>(); + List userPermissionEntityList = new ArrayList<>(); + List userDisplayTextCaches = new ArrayList<>(); try { do { for (User user : currentPage.getCurrentPage()) { @@ -98,19 +98,19 @@ private void processUsers(UserCollectionPage userCollectionPage) { List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); if (isPermittedRole(userRoles)) { - UserPermission userPermission = UserPermission + UserPermissionEntity userPermissionEntity = UserPermissionEntity .builder() .objectIdentifier(user.id) .build(); - userPermissionList.add(userPermission); + userPermissionEntityList.add(userPermissionEntity); - AzureUserCache azureUserCache = AzureUserCache + UserDisplayText userDisplayText = UserDisplayText .builder() .objectIdentifier(user.id) .email(Objects.requireNonNull(user.mail).toLowerCase()) .name(StringUtils.capitalizeFirstLetterOfEachWord(user.givenName) + " " + user.surname) .build(); - azureUserCaches.add(azureUserCache); + userDisplayTextCaches.add(userDisplayText); } } @@ -121,11 +121,11 @@ private void processUsers(UserCollectionPage userCollectionPage) { } } while (currentPage != null); - userPermissionService.refreshUserPermissions(userPermissionList); - userPermissionList.forEach(userPermission -> log.info("Saving user permission {} in db", userPermission.getObjectIdentifier())); + userPermissionService.refreshUserPermissions(userPermissionEntityList); + userPermissionEntityList.forEach(userPermission -> log.info("Saving user permission {} in db", userPermission.getObjectIdentifier())); - azureUserCacheService.refreshAzureUserCaches(azureUserCaches); - azureUserCaches.forEach(azureUserCache -> log.debug("Saving azure user {} in cache", azureUserCache.getEmail())); + azureUserCacheService.refreshAzureUserCaches(userDisplayTextCaches); + userDisplayTextCaches.forEach(azureUserCache -> log.debug("Saving azure user {} in cache", azureUserCache.getEmail())); log.info("{} User objects processed in Azure AD", usersProcessed); } catch (Exception e) { diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java index 08cdc30..fc753aa 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java @@ -2,7 +2,7 @@ import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; -import no.fintlabs.flyt.azure.models.AzureUserCache; +import no.fintlabs.flyt.azure.models.UserDisplayText; import org.springframework.stereotype.Service; import java.util.List; @@ -18,16 +18,16 @@ public AzureUserCacheService(AzureUserCacheRepository azureUserCacheRepository) this.azureUserCacheRepository = azureUserCacheRepository; } - public void refreshAzureUserCaches(List azureUserCaches) { - deleteAzureUserCacheNotInList(azureUserCaches); - azureUserCacheRepository.saveAll(azureUserCaches); + public void refreshAzureUserCaches(List userDisplayTextCaches) { + deleteAzureUserCacheNotInList(userDisplayTextCaches); + azureUserCacheRepository.saveAll(userDisplayTextCaches); } - private void deleteAzureUserCacheNotInList(List azureUserCaches) { - Map allCurrentAzureUserCaches = azureUserCacheRepository.findAll(); + private void deleteAzureUserCacheNotInList(List userDisplayTextCaches) { + Map allCurrentAzureUserCaches = azureUserCacheRepository.findAll(); - List inputAzureUserCachesIdentifiers = azureUserCaches.stream() - .map(AzureUserCache::getObjectIdentifier) + List inputAzureUserCachesIdentifiers = userDisplayTextCaches.stream() + .map(UserDisplayText::getObjectIdentifier) .toList(); List azureUserCachesStringsToDelete = allCurrentAzureUserCaches.keySet().stream() From 9f7e472d8245386e2b78510ab484095b1d0e9053 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 27 Jun 2024 13:45:41 +0200 Subject: [PATCH 26/50] refactor and use FintCache --- .../flyt/authorization/AuthorizationUtil.java | 24 ------ .../userpermission/MeController.java | 17 ++--- .../userpermission/UserController.java | 74 +++++-------------- .../UserPermissionRepository.java | 11 --- .../userpermission/UserPermissionService.java | 25 +++++++ .../userpermission/UserService.java | 13 ++++ .../AzureRoleDataCacheConfiguration.java | 35 +++++++++ .../flyt/azure/models/UserDisplayText.java | 1 + .../wrappers/AppRoleAssignmentWrapper.java | 15 ++++ .../models/wrappers/GroupMembersWrapper.java | 15 ++++ ...AzureAppRoleAssignmentCacheRepository.java | 17 +++-- .../AzureAppRoleCacheRepository.java | 12 +-- .../AzureGroupMembersCacheRepository.java | 17 +++-- .../AzureUserCacheRepository.java | 34 --------- .../UserDisplayTextCacheConfiguration.java | 16 ---- .../UserDisplayTextCacheRepository.java | 37 ++++++++++ .../azure/services/AzureAppGraphService.java | 19 ++--- .../services/AzureAppRoleCacheService.java | 24 +++--- .../azure/services/AzureUserCacheService.java | 30 +++++--- src/main/resources/application.yaml | 3 + 20 files changed, 234 insertions(+), 205 deletions(-) create mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java index 3c88dd2..3714374 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java +++ b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java @@ -1,9 +1,5 @@ package no.fintlabs.flyt.authorization; -import no.fintlabs.flyt.authorization.userpermission.UserPermissionEntity; -import no.fintlabs.flyt.authorization.userpermission.UserPermission; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.stereotype.Service; @@ -11,13 +7,6 @@ @Service public class AuthorizationUtil { - - private final AzureUserCacheRepository azureUserCacheRepository; - - public AuthorizationUtil(AzureUserCacheRepository azureUserCacheRepository) { - this.azureUserCacheRepository = azureUserCacheRepository; - } - public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticationToken) { return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString(); } @@ -27,17 +16,4 @@ public Mono isAdmin(Mono authenticationMono) { .map(authentication -> authentication != null && authentication.getAuthorities().stream() .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); } - -// public UserPermission buildUserPermissionDto(UserPermissionEntity userPermissionEntity) { -// -// UserDisplayText userDisplayText = azureUserCacheRepository.findByObjectIdentifier(userPermissionEntity.getObjectIdentifier()); -// -// return UserPermission -// .builder() -// .objectIdentifier(userPermissionEntity.getObjectIdentifier()) -// .email(userDisplayText != null ? userDisplayText.getEmail() : null) -// .name(userDisplayText != null ? userDisplayText.getName() : null) -// .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) -// .build(); -// } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java index f83df51..15c211a 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java @@ -17,15 +17,15 @@ @RestController public class MeController { - private final UserPermissionRepository userPermissionRepository; private final AuthorizationUtil authorizationUtil; + private final UserService userService; public MeController( - UserPermissionRepository userPermissionRepository, - AuthorizationUtil authorizationUtil + AuthorizationUtil authorizationUtil, + UserService userService ) { - this.userPermissionRepository = userPermissionRepository; this.authorizationUtil = authorizationUtil; + this.userService = userService; } @GetMapping("isAuthorized") @@ -42,17 +42,16 @@ public Mono> checkAdminUser( } @GetMapping - public Mono> get( + public Mono> get( @AuthenticationPrincipal Mono authenticationMono ) { return authenticationMono .map(authentication -> authorizationUtil .getObjectIdentifierFromToken((JwtAuthenticationToken) authentication)) .publishOn(Schedulers.boundedElastic()) - .map(userPermissionRepository::findByObjectIdentifier) - .map(optionalUserPermission -> optionalUserPermission.map(userPermission -> ResponseEntity.ok( - authorizationUtil.buildUserPermissionDto(userPermission) - )).orElseGet(() -> ResponseEntity.notFound().build()) + .map(userService::getUser) + .map(optionalUserPermission -> optionalUserPermission.map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.notFound().build()) ); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java index b4ecfc2..7038117 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java @@ -6,13 +6,9 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import java.util.Comparator; import java.util.List; -import java.util.Optional; import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; @@ -24,7 +20,8 @@ public class UserController { private final UserService userService; public UserController( - AuthorizationUtil authorizationUtil, UserService userService + AuthorizationUtil authorizationUtil, + UserService userService ) { this.authorizationUtil = authorizationUtil; this.userService = userService; @@ -43,59 +40,22 @@ public Mono>> get( // todo: add pagable with sorting return Mono.fromCallable(userService::getUsers) .map(ResponseEntity::ok); - -// return Mono.fromCallable(userPermissionRepository::findAll) -// .subscribeOn(Schedulers.boundedElastic()) -// .map(userPermissions -> { -// List users = new java.util.ArrayList<>(List.of()); -// userPermissions.forEach( -// userPermission -> users.add( -// authorizationUtil.buildUserPermissionDto(userPermission) -// ) -// ); -// users.sort( -// Comparator.comparing( -// User::getName, Comparator.nullsLast(String::compareTo) -// ) -// ); -// return ResponseEntity.ok().body(users); -// }); }); } -// @PutMapping("users") -// public Mono>> setUserPermissions( -// @RequestBody List userPermissions, -// @AuthenticationPrincipal Mono authenticationMono -// ) { -// return authorizationUtil.isAdmin(authenticationMono) -// .publishOn(Schedulers.boundedElastic()) -// .flatMap(isAdmin -> { -// if (isAdmin) { -// return Flux.fromIterable(userPermissions) -// .flatMap(userPermissionDto -> Mono.fromCallable(() -> { -// Optional userPermissionOptional = userPermissionRepository -// .findByObjectIdentifier(userPermissionDto.getObjectIdentifier()); -// return userPermissionOptional.map(userPermission -> { -// userPermission.setSourceApplicationIds(userPermissionDto.getSourceApplicationIds()); -// userPermissionRepository.save(userPermission); -// return authorizationUtil.buildUserPermissionDto(userPermission); -// }).orElse(null); -// }).subscribeOn(Schedulers.boundedElastic())) -// .collectList() -// .map(userPermissionDtoList -> { -// userPermissionDtoList.sort( -// Comparator.comparing( -// UserPermission::getName, Comparator.nullsLast(String::compareTo) -// ) -// ); -// return ResponseEntity.ok().body(userPermissionDtoList); -// }); -// } else { -// return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); -// } -// }); -// } - + @PutMapping + public Mono>> setUserPermissions( + @RequestBody List userPermissions, + @AuthenticationPrincipal Mono authenticationMono + ) { + return authorizationUtil.isAdmin(authenticationMono) + .flatMap(isAdmin -> { + if (!isAdmin) { + return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); + } -} + // todo: add pagable with sorting + return Mono.just(ResponseEntity.ok(userService.putUsers(userPermissions))); + }); + } +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java index 5fa9d3c..c0c2841 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java @@ -3,10 +3,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.Map; import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; @Repository @@ -14,12 +11,4 @@ public interface UserPermissionRepository extends JpaRepository findByObjectIdentifier(String sub); - default Map findAllAsMapWithObjectIdentifierAsKey() { - return findAll().stream() - .collect(Collectors.toMap( - UserPermissionEntity::getObjectIdentifier, - Function.identity() - )); - } - } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java index f1ff98b..1eb2852 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java @@ -5,7 +5,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; @Service @Slf4j @@ -17,6 +19,11 @@ public UserPermissionService(UserPermissionRepository userPermissionRepository) this.userPermissionRepository = userPermissionRepository; } + public Optional get(String objectIdentifier) { + return this.userPermissionRepository.findByObjectIdentifier(objectIdentifier) + .map(this::mapFromEntity); + } + public List getAll() { return this.userPermissionRepository.findAll() .stream() @@ -24,6 +31,24 @@ public List getAll() { .toList(); } + public List putAll(List userPermissions) { + List existingEntities = userPermissions.stream() + .map(userPermission -> userPermissionRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()) + .map(entity -> { + entity.setSourceApplicationIds(userPermission.getSourceApplicationIds()); + return entity; + }) + .orElse(null)) + .filter(Objects::nonNull) + .toList(); + + List savedEntities = userPermissionRepository.saveAll(existingEntities); + + return savedEntities.stream() + .map(this::mapFromEntity) + .collect(Collectors.toList()); + } + private UserPermission mapFromEntity(UserPermissionEntity userPermissionEntity) { return UserPermission .builder() diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java index eeafe87..f29be6f 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; @Service public class UserService { @@ -20,6 +21,11 @@ public UserService( this.userDisplayTextCache = userDisplayTextCache; } + public Optional getUser(String objectIdentifier) { + return userPermissionService.get(objectIdentifier) + .map(this::mapToUserWithDisplayText); + } + public List getUsers() { return userPermissionService.getAll() .stream() @@ -27,6 +33,13 @@ public List getUsers() { .toList(); } + public List putUsers(List userPermissions) { + return userPermissionService.putAll(userPermissions) + .stream() + .map(this::mapToUserWithDisplayText) + .toList(); + } + private User mapToUserWithDisplayText(UserPermission userPermission) { User.UserBuilder userBuilder = User .builder() diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java new file mode 100644 index 0000000..75c709f --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java @@ -0,0 +1,35 @@ +package no.fintlabs.flyt.azure; + +import com.microsoft.graph.models.AppRole; +import no.fintlabs.cache.FintCache; +import no.fintlabs.cache.FintCacheManager; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; +import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AzureRoleDataCacheConfiguration { + + @Bean + public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("userDisplayText", String.class, UserDisplayText.class); + } + + @Bean + public FintCache appRoleCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("appRoleCache", String.class, AppRole.class); + } + + @Bean + public FintCache appRoleAssignmentsCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("appRoleAssignmentsCache", String.class, AppRoleAssignmentWrapper.class); + } + + @Bean + public FintCache groupMembersCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("groupMembersCache", String.class, GroupMembersWrapper.class); + } + +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java index cd45f96..9b97a65 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java @@ -6,6 +6,7 @@ @Getter @Builder public class UserDisplayText { + private String objectIdentifier; private String email; private String name; } diff --git a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java new file mode 100644 index 0000000..5407076 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java @@ -0,0 +1,15 @@ +package no.fintlabs.flyt.azure.models.wrappers; + +import com.microsoft.graph.models.AppRoleAssignment; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@AllArgsConstructor +@Getter +@Setter +public class AppRoleAssignmentWrapper { + private List appRoleAssignments; +} diff --git a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java new file mode 100644 index 0000000..3bf7c55 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java @@ -0,0 +1,15 @@ +package no.fintlabs.flyt.azure.models.wrappers; + +import com.microsoft.graph.models.User; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@AllArgsConstructor +@Getter +@Setter +public class GroupMembersWrapper { + private List groupMembers; +} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java index 36ad76c..d0aa244 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java @@ -1,22 +1,23 @@ package no.fintlabs.flyt.azure.repositories; -import com.microsoft.graph.models.AppRoleAssignment; +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; import org.springframework.stereotype.Repository; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - @Repository public class AzureAppRoleAssignmentCacheRepository { - private final Map> appRoleAssignmentsCache = new HashMap<>(); + private final FintCache appRoleAssignmentsCache; + + public AzureAppRoleAssignmentCacheRepository(FintCache appRoleAssignmentsCache) { + this.appRoleAssignmentsCache = appRoleAssignmentsCache; + } - public void saveAll(String appId, List appRoleAssignments) { + public void saveAll(String appId, AppRoleAssignmentWrapper appRoleAssignments) { appRoleAssignmentsCache.put(appId, appRoleAssignments); } - public List findAllByAppId(String appId) { + public AppRoleAssignmentWrapper findAllByAppId(String appId) { return appRoleAssignmentsCache.get(appId); } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java index aa384e9..8f8cddd 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java @@ -2,21 +2,23 @@ import com.microsoft.graph.models.AppRole; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.cache.FintCache; import no.fintlabs.flyt.azure.models.PermittedAppRoles; import org.springframework.stereotype.Repository; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Repository @Slf4j public class AzureAppRoleCacheRepository { - private final Map appRoleCache = new HashMap<>(); + private final FintCache appRoleCache; protected final PermittedAppRoles permittedAppRoles; - public AzureAppRoleCacheRepository(PermittedAppRoles permittedAppRoles) { + public AzureAppRoleCacheRepository( + FintCache appRoleCache, PermittedAppRoles permittedAppRoles + ) { + this.appRoleCache = appRoleCache; this.permittedAppRoles = permittedAppRoles; } @@ -37,7 +39,7 @@ public void saveAllPermittedRoles(List appRoles) { ); } - public Map findAll() { + public FintCache findAll() { return appRoleCache; } } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java index 6125f8f..702a9ab 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java @@ -1,22 +1,23 @@ package no.fintlabs.flyt.azure.repositories; -import com.microsoft.graph.models.User; +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; import org.springframework.stereotype.Repository; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - @Repository public class AzureGroupMembersCacheRepository { - private final Map> groupMembersCache = new HashMap<>(); + private final FintCache groupMembersCache; + + public AzureGroupMembersCacheRepository(FintCache groupMembersCache) { + this.groupMembersCache = groupMembersCache; + } - public void saveAll(String principalId, List groupMembers) { + public void saveAll(String principalId, GroupMembersWrapper groupMembers) { groupMembersCache.put(principalId, groupMembers); } - public List findAllByPrincipalId(String principalId) { + public GroupMembersWrapper findAllByPrincipalId(String principalId) { return groupMembersCache.get(principalId); } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java deleted file mode 100644 index a1f8a2d..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureUserCacheRepository.java +++ /dev/null @@ -1,34 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import no.fintlabs.flyt.azure.models.UserDisplayText; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Repository -public class AzureUserCacheRepository { - private final Map azureUserCacheMap = new ConcurrentHashMap<>(); - -// public void save(UserDisplayText userDisplayText) { -// azureUserCacheMap.put(userDisplayText.getObjectIdentifier(), userDisplayText); -// } - -// public void saveAll(List userDisplayTextCaches) { -// userDisplayTextCaches.forEach(this::save); -// } - - public UserDisplayText findByObjectIdentifier(String objectIdentifier) { - return azureUserCacheMap.get(objectIdentifier); - } - - public Map findAll() { - return azureUserCacheMap; - } - - public void deleteByObjectIdentifier(String objectIdentifier) { - azureUserCacheMap.remove(objectIdentifier); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java deleted file mode 100644 index cfae0b3..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import no.fintlabs.cache.FintCache; -import no.fintlabs.cache.FintCacheManager; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class UserDisplayTextCacheConfiguration { - - @Bean - public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("userDisplayText", String.class, UserDisplayText.class); - } -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java new file mode 100644 index 0000000..a1943f1 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java @@ -0,0 +1,37 @@ +package no.fintlabs.flyt.azure.repositories; + +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class UserDisplayTextCacheRepository { + private final FintCache userDisplayTextCache; + + public UserDisplayTextCacheRepository(FintCache userDisplayTextCache) { + this.userDisplayTextCache = userDisplayTextCache; + } + + public void save(UserDisplayText userDisplayText) { + userDisplayTextCache.put(userDisplayText.getObjectIdentifier(), userDisplayText); + } + + public void saveAll(List userDisplayTextCaches) { + userDisplayTextCaches.forEach(this::save); + } + + public UserDisplayText findByObjectIdentifier(String objectIdentifier) { + return userDisplayTextCache.get(objectIdentifier); + } + + public FintCache findAll() { + return userDisplayTextCache; + } + + public void deleteByObjectIdentifier(String objectIdentifier) { + userDisplayTextCache.remove(objectIdentifier); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java index 0d3c891..2244f98 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java @@ -4,6 +4,8 @@ import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.requests.GraphServiceClient; import com.microsoft.graph.requests.ServicePrincipalCollectionPage; +import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; +import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; import okhttp3.Request; import org.springframework.stereotype.Service; @@ -23,7 +25,7 @@ public AzureAppGraphService( this.graphService = graphService; } - public List getAppRoleAssignments(String appId) { + public AppRoleAssignmentWrapper getAppRoleAssignments(String appId) { List requestOptions = new ArrayList<>(); requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); @@ -34,24 +36,21 @@ public List getAppRoleAssignments(String appId) { Objects.requireNonNull(servicePrincipalCollectionPage); -// if (servicePrincipalCollectionPage.getCount()) { -// -// } - List servicePrincipals = servicePrincipalCollectionPage.getCurrentPage(); if (!servicePrincipals.isEmpty()) { ServicePrincipal servicePrincipal = servicePrincipals.get(0); if (servicePrincipal != null && servicePrincipal.id != null) { - return Objects.requireNonNull(graphService + List appRoleAssignments = Objects.requireNonNull(graphService .servicePrincipals(servicePrincipal.id) .appRoleAssignedTo() .buildRequest() .get()) .getCurrentPage(); + return new AppRoleAssignmentWrapper(appRoleAssignments); } } - return new ArrayList<>(); + return new AppRoleAssignmentWrapper(new ArrayList<>()); } public List getAppRoles(String appId) { @@ -71,7 +70,7 @@ public List getAppRoles(String appId) { return new ArrayList<>(); } - public List getGroupMembers(String groupId) { + public GroupMembersWrapper getGroupMembers(String groupId) { List members = Objects.requireNonNull(graphService .groups(groupId) .members() @@ -79,10 +78,12 @@ public List getGroupMembers(String groupId) { .get()) .getCurrentPage(); - return members.stream() + List users = members.stream() .filter(directoryObject -> directoryObject instanceof User) .map(directoryObject -> (User) directoryObject) .collect(Collectors.toList()); + + return new GroupMembersWrapper(users); } } diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java index d1f37b8..ad985c2 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java @@ -2,8 +2,10 @@ import com.microsoft.graph.models.AppRole; import com.microsoft.graph.models.AppRoleAssignment; -import com.microsoft.graph.models.User; import lombok.extern.slf4j.Slf4j; +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; +import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; import no.fintlabs.flyt.azure.repositories.AzureAppRoleAssignmentCacheRepository; import no.fintlabs.flyt.azure.repositories.AzureAppRoleCacheRepository; import no.fintlabs.flyt.azure.repositories.AzureGroupMembersCacheRepository; @@ -11,7 +13,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; @Service @@ -36,10 +37,10 @@ public AzureAppRoleCacheService( } public void storeAzureAppRoleDataInCache(String appId) { - List appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId); + AppRoleAssignmentWrapper appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId); azureAppRoleAssignmentCacheRepository.saveAll(appId, appRoleAssignments); - appRoleAssignments.forEach(appRoleAssignment -> { + appRoleAssignments.getAppRoleAssignments().forEach(appRoleAssignment -> { if (appRoleAssignment.principalType == null || appRoleAssignment.principalId == null) { log.warn("Assignment principalType or principalId is null for appRoleAssignment {}", appRoleAssignment); return; @@ -49,7 +50,7 @@ public void storeAzureAppRoleDataInCache(String appId) { String principalId = appRoleAssignment.principalId.toString(); if (principalType.equals("Group")) { - List groupMembers = azureAppGraphService.getGroupMembers(principalId); + GroupMembersWrapper groupMembers = azureAppGraphService.getGroupMembers(principalId); azureGroupMembersCacheRepository.saveAll(principalId, groupMembers); } }); @@ -65,9 +66,9 @@ public List getUserRoles( ) { List roles = new ArrayList<>(); - List appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAllByAppId(appId); + AppRoleAssignmentWrapper appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAllByAppId(appId); - appRoleAssignments.forEach(assignment -> { + appRoleAssignments.getAppRoleAssignments().forEach(assignment -> { if (assignment.principalType == null || assignment.principalId == null) { log.debug("Assignment principalType or principalId is null for assignment {}", assignment); return; @@ -79,8 +80,8 @@ public List getUserRoles( if (principalType.equals("User") && principalId.equals(userId)) { addRoleIfNotNull(roles, assignment, appId); } else if (principalType.equals("Group")) { - List groupMembers = azureGroupMembersCacheRepository.findAllByPrincipalId(principalId); - groupMembers.forEach(user -> { + GroupMembersWrapper groupMembers = azureGroupMembersCacheRepository.findAllByPrincipalId(principalId); + groupMembers.getGroupMembers().forEach(user -> { if (user.id != null && user.id.equals(userId)) { addRoleIfNotNull(roles, assignment, appId); } @@ -107,10 +108,9 @@ private void addRoleIfNotNull(List roles, AppRoleAssignment assignment, } private String getAppRoleValue(UUID appRoleId, String appId) { - Map appRoles = azureAppRoleCacheRepository.findAll(); + FintCache appRoles = azureAppRoleCacheRepository.findAll(); - for (Map.Entry entry : appRoles.entrySet()) { - AppRole appRole = entry.getValue(); + for (AppRole appRole : appRoles.getAll()) { if (appRole.id != null && appRole.id.equals(appRoleId)) { return appRole.value; } diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java index fc753aa..61f8b2d 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java @@ -1,43 +1,49 @@ package no.fintlabs.flyt.azure.services; import lombok.extern.slf4j.Slf4j; -import no.fintlabs.flyt.azure.repositories.AzureUserCacheRepository; +import no.fintlabs.cache.FintCache; import no.fintlabs.flyt.azure.models.UserDisplayText; +import no.fintlabs.flyt.azure.repositories.UserDisplayTextCacheRepository; import org.springframework.stereotype.Service; import java.util.List; -import java.util.Map; @Service @Slf4j public class AzureUserCacheService { - private final AzureUserCacheRepository azureUserCacheRepository; + private final UserDisplayTextCacheRepository userDisplayTextCacheRepository; - public AzureUserCacheService(AzureUserCacheRepository azureUserCacheRepository) { - this.azureUserCacheRepository = azureUserCacheRepository; + public AzureUserCacheService(UserDisplayTextCacheRepository userDisplayTextCacheRepository) { + this.userDisplayTextCacheRepository = userDisplayTextCacheRepository; } public void refreshAzureUserCaches(List userDisplayTextCaches) { deleteAzureUserCacheNotInList(userDisplayTextCaches); - azureUserCacheRepository.saveAll(userDisplayTextCaches); + userDisplayTextCacheRepository.saveAll(userDisplayTextCaches); } private void deleteAzureUserCacheNotInList(List userDisplayTextCaches) { - Map allCurrentAzureUserCaches = azureUserCacheRepository.findAll(); + FintCache allCurrentAzureUserCaches = userDisplayTextCacheRepository.findAll(); List inputAzureUserCachesIdentifiers = userDisplayTextCaches.stream() .map(UserDisplayText::getObjectIdentifier) .toList(); - List azureUserCachesStringsToDelete = allCurrentAzureUserCaches.keySet().stream() - .filter(identifier -> !inputAzureUserCachesIdentifiers.contains(identifier)) + List allCurrentEntries = allCurrentAzureUserCaches.getAll(); + + List allCurrentKeys = allCurrentEntries.stream() + .map(UserDisplayText::getObjectIdentifier) + .toList(); + + List keysToDelete = allCurrentKeys.stream() + .filter(key -> !inputAzureUserCachesIdentifiers.contains(key)) .toList(); - azureUserCachesStringsToDelete.forEach(azureUserCacheRepository::deleteByObjectIdentifier); + keysToDelete.forEach(allCurrentAzureUserCaches::remove); - if (!azureUserCachesStringsToDelete.isEmpty()) { - log.info("Deleted {} user permissions", azureUserCachesStringsToDelete.size()); + if (!keysToDelete.isEmpty()) { + log.info("Deleted {} user permissions", keysToDelete.size()); } } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 1d1f680..a8f7765 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,5 +1,8 @@ fint: application-id: fint-flyt-authorization-service + cache: + defaultCacheEntryTimeToLiveMillis: 600000 + defaultCacheHeapSize: 1000000 spring: profiles: include: From 9b93b599cf0f0235e243084cfcbd2ba7ea83804f Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 27 Jun 2024 13:55:14 +0200 Subject: [PATCH 27/50] refactor using UserDisplayTextCacheRepository to access and modify userDisplayTextCache --- .../authorization/userpermission/UserService.java | 11 +++++------ .../repositories/UserDisplayTextCacheRepository.java | 5 +++-- .../flyt/azure/services/AzureUserCacheService.java | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java index f29be6f..0636a96 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java @@ -1,7 +1,6 @@ package no.fintlabs.flyt.authorization.userpermission; -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.UserDisplayText; +import no.fintlabs.flyt.azure.repositories.UserDisplayTextCacheRepository; import org.springframework.stereotype.Service; import java.util.List; @@ -11,14 +10,14 @@ public class UserService { private final UserPermissionService userPermissionService; - private final FintCache userDisplayTextCache; + private final UserDisplayTextCacheRepository userDisplayTextCacheRepository; public UserService( UserPermissionService userPermissionService, - FintCache userDisplayTextCache + UserDisplayTextCacheRepository userDisplayTextCacheRepository ) { this.userPermissionService = userPermissionService; - this.userDisplayTextCache = userDisplayTextCache; + this.userDisplayTextCacheRepository = userDisplayTextCacheRepository; } public Optional getUser(String objectIdentifier) { @@ -46,7 +45,7 @@ private User mapToUserWithDisplayText(UserPermission userPermission) { .objectIdentifier(userPermission.getObjectIdentifier()) .sourceApplicationIds(userPermission.getSourceApplicationIds()); - userDisplayTextCache.getOptional(userPermission.getObjectIdentifier()) + userDisplayTextCacheRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()) .ifPresent(userDisplayText -> userBuilder .email(userDisplayText.getEmail()) .name(userDisplayText.getName())); diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java index a1943f1..77a365f 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public class UserDisplayTextCacheRepository { @@ -22,8 +23,8 @@ public void saveAll(List userDisplayTextCaches) { userDisplayTextCaches.forEach(this::save); } - public UserDisplayText findByObjectIdentifier(String objectIdentifier) { - return userDisplayTextCache.get(objectIdentifier); + public Optional findByObjectIdentifier(String objectIdentifier) { + return userDisplayTextCache.getOptional(objectIdentifier); } public FintCache findAll() { diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java index 61f8b2d..7337cdc 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java @@ -24,23 +24,23 @@ public void refreshAzureUserCaches(List userDisplayTextCaches) } private void deleteAzureUserCacheNotInList(List userDisplayTextCaches) { - FintCache allCurrentAzureUserCaches = userDisplayTextCacheRepository.findAll(); + FintCache allCurrentUserDisplayTextCaches = userDisplayTextCacheRepository.findAll(); - List inputAzureUserCachesIdentifiers = userDisplayTextCaches.stream() + List inputUserDisplayTextCachesIdentifiers = userDisplayTextCaches.stream() .map(UserDisplayText::getObjectIdentifier) .toList(); - List allCurrentEntries = allCurrentAzureUserCaches.getAll(); + List allCurrentEntries = allCurrentUserDisplayTextCaches.getAll(); List allCurrentKeys = allCurrentEntries.stream() .map(UserDisplayText::getObjectIdentifier) .toList(); List keysToDelete = allCurrentKeys.stream() - .filter(key -> !inputAzureUserCachesIdentifiers.contains(key)) + .filter(key -> !inputUserDisplayTextCachesIdentifiers.contains(key)) .toList(); - keysToDelete.forEach(allCurrentAzureUserCaches::remove); + keysToDelete.forEach(allCurrentUserDisplayTextCaches::remove); if (!keysToDelete.isEmpty()) { log.info("Deleted {} user permissions", keysToDelete.size()); From 2e0d6407308f7db7295b316979e09fae58c25db2 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 27 Jun 2024 14:00:19 +0200 Subject: [PATCH 28/50] refactor using UserDisplayTextCacheRepository to access and modify userDisplayTextCache --- .../no/fintlabs/flyt/azure/services/AzureUserCacheService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java index 7337cdc..7d75320 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java @@ -40,7 +40,7 @@ private void deleteAzureUserCacheNotInList(List userDisplayText .filter(key -> !inputUserDisplayTextCachesIdentifiers.contains(key)) .toList(); - keysToDelete.forEach(allCurrentUserDisplayTextCaches::remove); + keysToDelete.forEach(userDisplayTextCacheRepository::deleteByObjectIdentifier); if (!keysToDelete.isEmpty()) { log.info("Deleted {} user permissions", keysToDelete.size()); From 35b7907470ef654163e244e8f7ddf3b9e3ef638d Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Thu, 27 Jun 2024 15:21:41 +0200 Subject: [PATCH 29/50] wip commit --- .../flyt/azure/models/PermittedAppRoles.java | 2 +- .../UserDisplayTextCacheRepository.java | 1 - .../services/AzureADUserSyncService.java | 1 - .../azure/services/AzureAppGraphService.java | 5 ++++ .../services/AzureAppRoleCacheService.java | 23 +++++++++++++++++++ .../resources/application-flyt-azure.yaml | 1 - 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java b/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java index b7bb0fb..f6945b9 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java @@ -8,5 +8,5 @@ @Getter @Setter public class PermittedAppRoles { - private Map permittedAppRoles; + private String flytUser; } diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java index 77a365f..d01d88b 100644 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java +++ b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java @@ -34,5 +34,4 @@ public FintCache findAll() { public void deleteByObjectIdentifier(String objectIdentifier) { userDisplayTextCache.remove(objectIdentifier); } - } diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java index 2dda453..8ffd085 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java @@ -28,7 +28,6 @@ public class AzureADUserSyncService { protected final ConfigUser configUser; protected final PermittedAppRoles permittedAppRoles; protected final GraphServiceClient graphService; - protected final AzureAppGraphService azureAppGraphService; protected final UserPermissionService userPermissionService; protected final AzureAppRoleCacheService azureAppRoleCacheService; protected final AzureUserCacheService azureUserCacheService; diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java index 2244f98..4b0b037 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.UUID; import java.util.stream.Collectors; @Service @@ -86,4 +87,8 @@ public GroupMembersWrapper getGroupMembers(String groupId) { return new GroupMembersWrapper(users); } + public GroupMembersWrapper getGroupMembers(List groupIds) { + graphService. + } + } diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java index ad985c2..be96ca5 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java +++ b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java @@ -4,6 +4,7 @@ import com.microsoft.graph.models.AppRoleAssignment; import lombok.extern.slf4j.Slf4j; import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.azure.models.PermittedAppRoles; import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; import no.fintlabs.flyt.azure.repositories.AzureAppRoleAssignmentCacheRepository; @@ -13,7 +14,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Service @Slf4j @@ -36,6 +39,26 @@ public AzureAppRoleCacheService( this.azureGroupMembersCacheRepository = azureGroupMembersCacheRepository; } + public void getAbc(String appId, Set permittedAppRoleIds) { + + // todo: remove wrapper + List appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId) + .getAppRoleAssignments(); + + List permittedGroupIds = appRoleAssignments.stream() + .filter(appRoleAssignment -> "Group".equals(appRoleAssignment.principalType)) + .filter(appRoleAssignment -> permittedAppRoleIds.contains(appRoleAssignment.appRoleId)) + .map(appRoleAssignment -> appRoleAssignment.principalId) + .toList(); + + azureAppGraphService.getGroupMembers() + + appRoleAssignments.stream() + .filter(appRoleAssignment -> "User".equals(appRoleAssignment.principalType)) + .filter(appRoleAssignment -> permittedAppRoleIds.) + + } + public void storeAzureAppRoleDataInCache(String appId) { AppRoleAssignmentWrapper appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId); azureAppRoleAssignmentCacheRepository.saveAll(appId, appRoleAssignments); diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index df55181..f044f71 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -25,7 +25,6 @@ fint: azure-ad-gateway: permitted-approles: flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" - flyt-developer: "https://role-catalog.vigoiks.no/vigo/flyt/developer" user-scheduler: pull: initial-delay-ms: 1000 From ecf614a5080157432e7da0806a383da3232f1af7 Mon Sep 17 00:00:00 2001 From: Eivind Morch Date: Thu, 27 Jun 2024 23:26:38 +0200 Subject: [PATCH 30/50] Remove unecessary caching and add separate graph services --- build.gradle | 3 + .../flyt/authorization/user/UserService.java | 102 ++++++++++++ .../controller}/MeController.java | 26 +++- .../controller}/UserController.java | 9 +- .../{userpermission => user/model}/User.java | 5 +- .../RestrictedPageAuthorization.java | 10 ++ .../permission/UserPermissionRepository.java | 26 ++++ .../permission/UserPermissionService.java | 89 +++++++++++ .../permission/model}/UserPermission.java | 5 +- .../model}/UserPermissionEntity.java | 15 +- .../userpermission/RestrictedPageAccess.java | 10 -- .../UserPermissionRepository.java | 14 -- .../userpermission/UserPermissionService.java | 95 ------------ .../userpermission/UserService.java | 55 ------- .../AzureRoleDataCacheConfiguration.java | 35 ----- .../no/fintlabs/flyt/azure/StringUtils.java | 1 + .../AzureAdGatewayConfiguration.java | 24 +++ .../AzureCredentialsConfiguration.java | 28 ++++ .../GraphServiceConfiguration.java} | 33 +--- .../PermittedAppRoles.java | 5 +- .../RequiredPropertiesCondition.java | 3 +- .../UserDisplayTextCacheConfiguration.java | 20 +++ .../flyt/azure/models/ConfigUser.java | 26 ---- .../flyt/azure/models/GraphUserInfo.java | 14 ++ .../flyt/azure/models/UserDisplayText.java | 4 +- .../wrappers/AppRoleAssignmentWrapper.java | 15 -- .../models/wrappers/GroupMembersWrapper.java | 15 -- ...AzureAppRoleAssignmentCacheRepository.java | 24 --- .../AzureAppRoleCacheRepository.java | 45 ------ .../AzureGroupMembersCacheRepository.java | 24 --- .../UserDisplayTextCacheRepository.java | 37 ----- .../services/AzureADUserSyncService.java | 144 ----------------- .../azure/services/AzureAppGraphService.java | 94 ----------- .../services/AzureAppRoleCacheService.java | 146 ------------------ .../azure/services/AzureUserCacheService.java | 49 ------ .../azure/services/GraphAppRoleService.java | 94 +++++++++++ .../azure/services/GraphGroupService.java | 58 +++++++ .../flyt/azure/services/GraphService.java | 75 +++++++++ .../GraphServicePrincipalService.java | 43 ++++++ .../flyt/azure/services/GraphUserService.java | 50 ++++++ 40 files changed, 694 insertions(+), 876 deletions(-) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserService.java rename src/main/java/no/fintlabs/flyt/authorization/{userpermission => user/controller}/MeController.java (65%) rename src/main/java/no/fintlabs/flyt/authorization/{userpermission => user/controller}/UserController.java (85%) rename src/main/java/no/fintlabs/flyt/authorization/{userpermission => user/model}/User.java (77%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java rename src/main/java/no/fintlabs/flyt/authorization/{userpermission => user/permission/model}/UserPermission.java (73%) rename src/main/java/no/fintlabs/flyt/authorization/{userpermission => user/permission/model}/UserPermissionEntity.java (68%) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java rename src/main/java/no/fintlabs/flyt/azure/{Config.java => configuration/GraphServiceConfiguration.java} (58%) rename src/main/java/no/fintlabs/flyt/azure/{models => configuration}/PermittedAppRoles.java (54%) rename src/main/java/no/fintlabs/flyt/azure/{ => configuration}/RequiredPropertiesCondition.java (89%) create mode 100644 src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java create mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java diff --git a/build.gradle b/build.gradle index dee91af..7370a20 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,9 @@ dependencies { implementation 'no.fintlabs:fint-flyt-cache:1.2.3' + implementation 'javax.validation:validation-api' + implementation 'org.hibernate.validator:hibernate-validator' + implementation 'com.azure:azure-identity:1.10.2' implementation 'com.microsoft.graph:microsoft-graph:5.80.0' diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java new file mode 100644 index 0000000..9bc35c2 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java @@ -0,0 +1,102 @@ +package no.fintlabs.flyt.authorization.user; + +import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.authorization.user.model.User; +import no.fintlabs.flyt.authorization.user.permission.UserPermissionService; +import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; +import no.fintlabs.flyt.azure.models.GraphUserInfo; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import no.fintlabs.flyt.azure.services.GraphService; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toMap; + +@Service +public class UserService { + + private final GraphService graphService; + + private final UserPermissionService userPermissionService; + + private final FintCache userDisplayTextCache; + + public UserService( + UserPermissionService userPermissionService, + FintCache userDisplayTextCache, + GraphService graphService) { + this.userPermissionService = userPermissionService; + this.userDisplayTextCache = userDisplayTextCache; + this.graphService = graphService; + } + + // TODO eivindmorch 27/06/2024 : Schedule this + public void syncUsers() { + updateUsers(graphService.getPermittedUsersInfo()); + } + + private void updateUsers(Collection permittedUsersGraphUserInfo) { + Set objectIdentifiers = permittedUsersGraphUserInfo.stream() + .map(GraphUserInfo::getId) + .collect(Collectors.toSet()); + + userPermissionService.updateUserPermissions(objectIdentifiers); + + Set objectIdentifiersAlreadyCachedButNotInNewUserInfo = userDisplayTextCache.getAll() + .stream() + .map(UserDisplayText::getObjectIdentifier) + .filter(objectIdentifier -> !objectIdentifiers.contains(objectIdentifier)) + .collect(Collectors.toSet()); + + userDisplayTextCache.remove(objectIdentifiersAlreadyCachedButNotInNewUserInfo); + userDisplayTextCache.put(permittedUsersGraphUserInfo + .stream() + .collect(toMap( + GraphUserInfo::getId, + graphUserInfo -> UserDisplayText + .builder() + .objectIdentifier(graphUserInfo.getId()) + .name(graphUserInfo.getDisplayName()) + .email(graphUserInfo.getMail()) + .build() + )) + ); + + } + + public Optional getUser(UUID objectIdentifier) { + return userPermissionService.find(objectIdentifier) + .map(this::mapToUserWithDisplayText); + } + + public List getUsers() { + return userPermissionService.getAll() + .stream() + .map(this::mapToUserWithDisplayText) + .toList(); + } + + public List putUsers(List userPermissions) { + return userPermissionService.putAll(userPermissions) + .stream() + .map(this::mapToUserWithDisplayText) + .toList(); + } + + private User mapToUserWithDisplayText(UserPermission userPermission) { + User.UserBuilder userBuilder = User + .builder() + .objectIdentifier(userPermission.getObjectIdentifier()) + .sourceApplicationIds(userPermission.getSourceApplicationIds()); + + userDisplayTextCache.getOptional(userPermission.getObjectIdentifier()) + .ifPresent(userDisplayText -> userBuilder + .email(userDisplayText.getEmail()) + .name(userDisplayText.getName())); + + return userBuilder.build(); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java similarity index 65% rename from src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java rename to src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 15c211a..9a60352 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -1,6 +1,9 @@ -package no.fintlabs.flyt.authorization.userpermission; +package no.fintlabs.flyt.authorization.user.controller; import no.fintlabs.flyt.authorization.AuthorizationUtil; +import no.fintlabs.flyt.authorization.user.UserService; +import no.fintlabs.flyt.authorization.user.model.User; +import no.fintlabs.flyt.authorization.user.permission.RestrictedPageAuthorization; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -11,6 +14,8 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; +import java.util.UUID; + import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; @RequestMapping(INTERNAL_API + "/authorization/me") @@ -28,17 +33,22 @@ public MeController( this.userService = userService; } - @GetMapping("isAuthorized") + @GetMapping("is-authorized") public ResponseEntity checkAuthorization() { return ResponseEntity.ok("User authorized"); } - @GetMapping("isAdmin") - public Mono> checkAdminUser( + @GetMapping("restricted-page-authorization") + public Mono> getRestrictedPageAuthorization( @AuthenticationPrincipal Mono authenticationMono ) { return authorizationUtil.isAdmin(authenticationMono) - .map(isAdmin -> ResponseEntity.ok(RestrictedPageAccess.builder().userPermission(isAdmin).build())); + .map(isAdmin -> ResponseEntity.ok( + RestrictedPageAuthorization + .builder() + .userPermissionPage(isAdmin) + .build()) + ); } @GetMapping @@ -49,9 +59,9 @@ public Mono> get( .map(authentication -> authorizationUtil .getObjectIdentifierFromToken((JwtAuthenticationToken) authentication)) .publishOn(Schedulers.boundedElastic()) - .map(userService::getUser) - .map(optionalUserPermission -> optionalUserPermission.map(ResponseEntity::ok) - .orElseGet(() -> ResponseEntity.notFound().build()) + .map(objectIdentifierString -> userService.getUser(UUID.fromString(objectIdentifierString)) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()) ); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java similarity index 85% rename from src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java rename to src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index 7038117..5d15811 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -1,6 +1,9 @@ -package no.fintlabs.flyt.authorization.userpermission; +package no.fintlabs.flyt.authorization.user.controller; import no.fintlabs.flyt.authorization.AuthorizationUtil; +import no.fintlabs.flyt.authorization.user.model.User; +import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; +import no.fintlabs.flyt.authorization.user.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -37,7 +40,7 @@ public Mono>> get( return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } - // todo: add pagable with sorting + // TODO: add pagable with sorting return Mono.fromCallable(userService::getUsers) .map(ResponseEntity::ok); }); @@ -54,7 +57,7 @@ public Mono>> setUserPermissions( return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } - // todo: add pagable with sorting + // TODO: add pagable with sorting return Mono.just(ResponseEntity.ok(userService.putUsers(userPermissions))); }); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java similarity index 77% rename from src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java rename to src/main/java/no/fintlabs/flyt/authorization/user/model/User.java index 775cdd2..7fc2856 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/User.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.userpermission; +package no.fintlabs.flyt.authorization.user.model; import com.sun.istack.NotNull; import lombok.Builder; @@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized; import java.util.List; +import java.util.UUID; @Getter @Builder @@ -14,7 +15,7 @@ @Jacksonized public class User { @NotNull - private String objectIdentifier; + private UUID objectIdentifier; private String email; private String name; @NotNull diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java b/src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java new file mode 100644 index 0000000..39e5569 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java @@ -0,0 +1,10 @@ +package no.fintlabs.flyt.authorization.user.permission; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class RestrictedPageAuthorization { + private boolean userPermissionPage; +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java new file mode 100644 index 0000000..158f8de --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java @@ -0,0 +1,26 @@ +package no.fintlabs.flyt.authorization.user.permission; + +import no.fintlabs.flyt.authorization.user.permission.model.UserPermissionEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.*; + + +@Repository +public interface UserPermissionRepository extends JpaRepository { + + // TODO eivindmorch 27/06/2024 : Index on object identifier + Optional findByObjectIdentifier(UUID sub); + + List findAllByObjectIdentifierIn(Collection objectIdentifiers); + + interface ObjectIdentifierSelection { + UUID getObjectIdentifier(); + } + + List findObjectIdentifiersByObjectIdentifierIn(Collection objectIdentifiers); + + void deleteByObjectIdentifierNotIn(Set objectIdentifiers); + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java new file mode 100644 index 0000000..0a81d32 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java @@ -0,0 +1,89 @@ +package no.fintlabs.flyt.authorization.user.permission; + +import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; +import no.fintlabs.flyt.authorization.user.permission.model.UserPermissionEntity; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toMap; + +@Service +@Slf4j +public class UserPermissionService { + + // TODO eivindmorch 27/06/2024 : Azure sync og tjeneste som leverer til frontend burde være separate + // ettersom horisontal skalering av synkingen vil skape problemer + + private final UserPermissionRepository userPermissionRepository; + + public UserPermissionService(UserPermissionRepository userPermissionRepository) { + this.userPermissionRepository = userPermissionRepository; + } + + public void updateUserPermissions(Set permittedUsersObjectIdentifiers) { + userPermissionRepository.deleteByObjectIdentifierNotIn(permittedUsersObjectIdentifiers); // TODO eivindmorch 27/06/2024 : Soft delete? + + Set objectIdentifiersAlreadyPersisted = + userPermissionRepository.findObjectIdentifiersByObjectIdentifierIn(permittedUsersObjectIdentifiers) + .stream() + .map(UserPermissionRepository.ObjectIdentifierSelection::getObjectIdentifier) + .collect(Collectors.toSet()); + Set objectIdentifiersNotAlreadyPersisted = new HashSet<>(permittedUsersObjectIdentifiers); + objectIdentifiersNotAlreadyPersisted.removeAll(objectIdentifiersAlreadyPersisted); + userPermissionRepository.saveAll( + objectIdentifiersNotAlreadyPersisted.stream() + .map(objectIdentifier -> UserPermissionEntity + .builder() + .objectIdentifier(objectIdentifier) + .build()) + .toList() + ); + } + + public Optional find(UUID objectIdentifier) { + return this.userPermissionRepository.findByObjectIdentifier(objectIdentifier) + .map(this::mapFromEntity); + } + + public List getAll() { + return this.userPermissionRepository.findAll() + .stream() + .map(this::mapFromEntity) + .toList(); + } + + public List putAll(List userPermissions) { + Map> sourceApplicationIdsPerObjectIdentifier = userPermissions.stream() + .collect(toMap( + UserPermission::getObjectIdentifier, + UserPermission::getSourceApplicationIds + )); + + List entities = userPermissionRepository.findAllByObjectIdentifierIn( + userPermissions + .stream() + .map(UserPermission::getObjectIdentifier) + .toList() + ); + + entities.forEach(entity -> entity.setSourceApplicationIds( + sourceApplicationIdsPerObjectIdentifier.get(entity.getObjectIdentifier()) + )); + + return userPermissionRepository.saveAll(entities).stream() + .map(this::mapFromEntity) + .collect(Collectors.toList()); + } + + private UserPermission mapFromEntity(UserPermissionEntity userPermissionEntity) { + return UserPermission + .builder() + .objectIdentifier(userPermissionEntity.getObjectIdentifier()) + .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) + .build(); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java b/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java similarity index 73% rename from src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java rename to src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java index ee39408..5920529 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermission.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.userpermission; +package no.fintlabs.flyt.authorization.user.permission.model; import com.sun.istack.NotNull; import lombok.Builder; @@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized; import java.util.List; +import java.util.UUID; @Getter @Builder @@ -14,7 +15,7 @@ @Jacksonized public class UserPermission { @NotNull - private String objectIdentifier; + private UUID objectIdentifier; @NotNull private List sourceApplicationIds; } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java b/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java similarity index 68% rename from src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java rename to src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java index c5fd080..ac90e83 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionEntity.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java @@ -1,10 +1,13 @@ -package no.fintlabs.flyt.authorization.userpermission; +package no.fintlabs.flyt.authorization.user.permission.model; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.*; +import org.hibernate.annotations.NaturalId; import javax.persistence.*; +import java.util.ArrayList; import java.util.List; +import java.util.UUID; @Getter @Builder @@ -18,9 +21,12 @@ public class UserPermissionEntity { @JsonIgnore @Setter(AccessLevel.NONE) private long id; + @Setter - @Column(unique = true, nullable = false) - private String objectIdentifier; + @NaturalId + @Column(nullable = false, unique = true) + private UUID objectIdentifier; + @ElementCollection @CollectionTable( name = "user_permission_source_application_ids", @@ -29,5 +35,6 @@ public class UserPermissionEntity { ) @Setter @Column(name = "source_application_ids") - private List sourceApplicationIds; + @Builder.Default + private List sourceApplicationIds = new ArrayList<>(); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java deleted file mode 100644 index d22ae7f..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/RestrictedPageAccess.java +++ /dev/null @@ -1,10 +0,0 @@ -package no.fintlabs.flyt.authorization.userpermission; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class RestrictedPageAccess { - private boolean userPermission; -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java deleted file mode 100644 index c0c2841..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package no.fintlabs.flyt.authorization.userpermission; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - - -@Repository -public interface UserPermissionRepository extends JpaRepository { - - Optional findByObjectIdentifier(String sub); - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java deleted file mode 100644 index 1eb2852..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserPermissionService.java +++ /dev/null @@ -1,95 +0,0 @@ -package no.fintlabs.flyt.authorization.userpermission; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -@Service -@Slf4j -public class UserPermissionService { - - private final UserPermissionRepository userPermissionRepository; - - public UserPermissionService(UserPermissionRepository userPermissionRepository) { - this.userPermissionRepository = userPermissionRepository; - } - - public Optional get(String objectIdentifier) { - return this.userPermissionRepository.findByObjectIdentifier(objectIdentifier) - .map(this::mapFromEntity); - } - - public List getAll() { - return this.userPermissionRepository.findAll() - .stream() - .map(this::mapFromEntity) - .toList(); - } - - public List putAll(List userPermissions) { - List existingEntities = userPermissions.stream() - .map(userPermission -> userPermissionRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()) - .map(entity -> { - entity.setSourceApplicationIds(userPermission.getSourceApplicationIds()); - return entity; - }) - .orElse(null)) - .filter(Objects::nonNull) - .toList(); - - List savedEntities = userPermissionRepository.saveAll(existingEntities); - - return savedEntities.stream() - .map(this::mapFromEntity) - .collect(Collectors.toList()); - } - - private UserPermission mapFromEntity(UserPermissionEntity userPermissionEntity) { - return UserPermission - .builder() - .objectIdentifier(userPermissionEntity.getObjectIdentifier()) - .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) - .build(); - } - - public void refreshUserPermissions(List userPermissionEntities) { - - deleteUserPermissionsNotInList(userPermissionEntities); - - List newUserPermissionEntityList = new ArrayList<>(); - - userPermissionEntities.forEach(userPermission -> { - Optional existingUserPermission = userPermissionRepository - .findByObjectIdentifier(userPermission.getObjectIdentifier()); - if (existingUserPermission.isEmpty()) { - newUserPermissionEntityList.add(userPermission); - } - }); - - userPermissionRepository.saveAll(newUserPermissionEntityList); - } - - private void deleteUserPermissionsNotInList(List userPermissionEntities) { - List allCurrentUserPermissionEntities = userPermissionRepository.findAll(); - - List inputUserPermissionIdentifiers = userPermissionEntities.stream() - .map(UserPermissionEntity::getObjectIdentifier) - .toList(); - - List userPermissionsToDeleteEntity = allCurrentUserPermissionEntities.stream() - .filter(userPermission -> !inputUserPermissionIdentifiers.contains(userPermission.getObjectIdentifier())) - .toList(); - - userPermissionRepository.deleteAll(userPermissionsToDeleteEntity); - - if (!userPermissionsToDeleteEntity.isEmpty()) { - log.info("Deleted {} user permissions", userPermissionsToDeleteEntity.size()); - } - } - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java deleted file mode 100644 index 0636a96..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/userpermission/UserService.java +++ /dev/null @@ -1,55 +0,0 @@ -package no.fintlabs.flyt.authorization.userpermission; - -import no.fintlabs.flyt.azure.repositories.UserDisplayTextCacheRepository; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; - -@Service -public class UserService { - - private final UserPermissionService userPermissionService; - private final UserDisplayTextCacheRepository userDisplayTextCacheRepository; - - public UserService( - UserPermissionService userPermissionService, - UserDisplayTextCacheRepository userDisplayTextCacheRepository - ) { - this.userPermissionService = userPermissionService; - this.userDisplayTextCacheRepository = userDisplayTextCacheRepository; - } - - public Optional getUser(String objectIdentifier) { - return userPermissionService.get(objectIdentifier) - .map(this::mapToUserWithDisplayText); - } - - public List getUsers() { - return userPermissionService.getAll() - .stream() - .map(this::mapToUserWithDisplayText) - .toList(); - } - - public List putUsers(List userPermissions) { - return userPermissionService.putAll(userPermissions) - .stream() - .map(this::mapToUserWithDisplayText) - .toList(); - } - - private User mapToUserWithDisplayText(UserPermission userPermission) { - User.UserBuilder userBuilder = User - .builder() - .objectIdentifier(userPermission.getObjectIdentifier()) - .sourceApplicationIds(userPermission.getSourceApplicationIds()); - - userDisplayTextCacheRepository.findByObjectIdentifier(userPermission.getObjectIdentifier()) - .ifPresent(userDisplayText -> userBuilder - .email(userDisplayText.getEmail()) - .name(userDisplayText.getName())); - - return userBuilder.build(); - } -} diff --git a/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java deleted file mode 100644 index 75c709f..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/AzureRoleDataCacheConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -package no.fintlabs.flyt.azure; - -import com.microsoft.graph.models.AppRole; -import no.fintlabs.cache.FintCache; -import no.fintlabs.cache.FintCacheManager; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; -import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class AzureRoleDataCacheConfiguration { - - @Bean - public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("userDisplayText", String.class, UserDisplayText.class); - } - - @Bean - public FintCache appRoleCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("appRoleCache", String.class, AppRole.class); - } - - @Bean - public FintCache appRoleAssignmentsCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("appRoleAssignmentsCache", String.class, AppRoleAssignmentWrapper.class); - } - - @Bean - public FintCache groupMembersCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("groupMembersCache", String.class, GroupMembersWrapper.class); - } - -} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/azure/StringUtils.java b/src/main/java/no/fintlabs/flyt/azure/StringUtils.java index cb0d21b..49d2262 100644 --- a/src/main/java/no/fintlabs/flyt/azure/StringUtils.java +++ b/src/main/java/no/fintlabs/flyt/azure/StringUtils.java @@ -4,6 +4,7 @@ public class StringUtils { + // TODO eivindmorch 27/06/2024 : Trengs denne? public static String capitalizeFirstLetterOfEachWord(String input) { if (input == null || input.isEmpty()) { return input; diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java new file mode 100644 index 0000000..fc46a1a --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java @@ -0,0 +1,24 @@ +package no.fintlabs.flyt.azure.configuration; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; + + +@Getter +@Setter +@Validated +@EnableAutoConfiguration +@Configuration +@ConfigurationProperties(prefix = "fint.flyt.azure-ad-gateway") +public class AzureAdGatewayConfiguration { + + @Valid + private PermittedAppRoles permittedAppRoles; + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java new file mode 100644 index 0000000..79298c6 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java @@ -0,0 +1,28 @@ +package no.fintlabs.flyt.azure.configuration; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotEmpty; + + +@Getter +@Setter +@Validated +@EnableAutoConfiguration +@Configuration +@ConfigurationProperties(prefix = "azure.credentials") +public class AzureCredentialsConfiguration { + @NotEmpty + private String clientId; + @NotEmpty + private String clientSecret; + @NotEmpty + private String tenantId; + @NotEmpty + private String appId; +} diff --git a/src/main/java/no/fintlabs/flyt/azure/Config.java b/src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java similarity index 58% rename from src/main/java/no/fintlabs/flyt/azure/Config.java rename to src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java index 117e887..d4d768a 100644 --- a/src/main/java/no/fintlabs/flyt/azure/Config.java +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.configuration; import com.azure.identity.ClientSecretCredential; import com.azure.identity.ClientSecretCredentialBuilder; @@ -6,47 +6,30 @@ import com.microsoft.graph.requests.GraphServiceClient; import lombok.Getter; import lombok.Setter; -import no.fintlabs.flyt.azure.models.ConfigUser; -import no.fintlabs.flyt.azure.models.PermittedAppRoles; import okhttp3.Request; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; import java.util.List; @Getter @Setter +@Validated @EnableAutoConfiguration @Configuration -@ConfigurationProperties(prefix = "azure.credentials") -public class Config { - private String clientId; - private String clientSecret; - private String tenantId; - private String appId; - - @Bean - public ConfigUser configUser() { - return new ConfigUser(); - } - - @Bean - @ConfigurationProperties(prefix = "fint.flyt.azure-ad-gateway") - public PermittedAppRoles appRoles() { - return new PermittedAppRoles(); - } +public class GraphServiceConfiguration { @Bean @Conditional(RequiredPropertiesCondition.class) - public GraphServiceClient graphService() { + public GraphServiceClient graphServiceClient(AzureCredentialsConfiguration azureCredentialsConfiguration) { ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() - .clientId(clientId) - .clientSecret(clientSecret) - .tenantId(tenantId) + .clientId(azureCredentialsConfiguration.getClientId()) + .clientSecret(azureCredentialsConfiguration.getClientSecret()) + .tenantId(azureCredentialsConfiguration.getTenantId()) .build(); TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider( diff --git a/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java b/src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java similarity index 54% rename from src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java rename to src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java index f6945b9..d610545 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/PermittedAppRoles.java +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java @@ -1,12 +1,13 @@ -package no.fintlabs.flyt.azure.models; +package no.fintlabs.flyt.azure.configuration; import lombok.Getter; import lombok.Setter; -import java.util.Map; +import javax.validation.constraints.NotEmpty; @Getter @Setter public class PermittedAppRoles { + @NotEmpty private String flytUser; } diff --git a/src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java b/src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java similarity index 89% rename from src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java rename to src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java index 1276595..0db9ac5 100644 --- a/src/main/java/no/fintlabs/flyt/azure/RequiredPropertiesCondition.java +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.azure.configuration; import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Condition; @@ -6,6 +6,7 @@ import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.StringUtils; +// TODO eivindmorch 27/06/2024 : Kan dette gjøres med bean-avhengighet? public class RequiredPropertiesCondition implements Condition { @Override public boolean matches(ConditionContext context, @NotNull AnnotatedTypeMetadata metadata) { diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java new file mode 100644 index 0000000..a6de572 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java @@ -0,0 +1,20 @@ +package no.fintlabs.flyt.azure.configuration; + +import no.fintlabs.cache.FintCache; +import no.fintlabs.cache.FintCacheManager; +import no.fintlabs.flyt.azure.models.UserDisplayText; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.UUID; + + +@Configuration +public class UserDisplayTextCacheConfiguration { + + @Bean + public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { + return fintCacheManager.createCache("userDisplayText", UUID.class, UserDisplayText.class); + } + +} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java b/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java deleted file mode 100644 index d5cf5d1..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/models/ConfigUser.java +++ /dev/null @@ -1,26 +0,0 @@ -package no.fintlabs.flyt.azure.models; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.Arrays; -import java.util.List; - -@Setter -@Getter -@AllArgsConstructor -public class ConfigUser { - - private static final List userAttributes = Arrays.asList( - "id", - "mail", - "givenname", - "surname" - ); - - public List allAttributes() { - return userAttributes; - } - -} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java b/src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java new file mode 100644 index 0000000..8e55a23 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java @@ -0,0 +1,14 @@ +package no.fintlabs.flyt.azure.models; + +import lombok.Builder; +import lombok.Getter; + +import java.util.UUID; + +@Builder +@Getter +public class GraphUserInfo { + private final UUID id; + private final String displayName; + private final String mail; +} diff --git a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java index 9b97a65..6a400bb 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java +++ b/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java @@ -3,10 +3,12 @@ import lombok.Builder; import lombok.Getter; +import java.util.UUID; + @Getter @Builder public class UserDisplayText { - private String objectIdentifier; + private UUID objectIdentifier; private String email; private String name; } diff --git a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java deleted file mode 100644 index 5407076..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/AppRoleAssignmentWrapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package no.fintlabs.flyt.azure.models.wrappers; - -import com.microsoft.graph.models.AppRoleAssignment; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@AllArgsConstructor -@Getter -@Setter -public class AppRoleAssignmentWrapper { - private List appRoleAssignments; -} diff --git a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java b/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java deleted file mode 100644 index 3bf7c55..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/models/wrappers/GroupMembersWrapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package no.fintlabs.flyt.azure.models.wrappers; - -import com.microsoft.graph.models.User; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@AllArgsConstructor -@Getter -@Setter -public class GroupMembersWrapper { - private List groupMembers; -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java deleted file mode 100644 index d0aa244..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleAssignmentCacheRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; -import org.springframework.stereotype.Repository; - -@Repository -public class AzureAppRoleAssignmentCacheRepository { - - private final FintCache appRoleAssignmentsCache; - - public AzureAppRoleAssignmentCacheRepository(FintCache appRoleAssignmentsCache) { - this.appRoleAssignmentsCache = appRoleAssignmentsCache; - } - - public void saveAll(String appId, AppRoleAssignmentWrapper appRoleAssignments) { - appRoleAssignmentsCache.put(appId, appRoleAssignments); - } - - public AppRoleAssignmentWrapper findAllByAppId(String appId) { - return appRoleAssignmentsCache.get(appId); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java deleted file mode 100644 index 8f8cddd..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureAppRoleCacheRepository.java +++ /dev/null @@ -1,45 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import com.microsoft.graph.models.AppRole; -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.PermittedAppRoles; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -@Slf4j -public class AzureAppRoleCacheRepository { - - private final FintCache appRoleCache; - protected final PermittedAppRoles permittedAppRoles; - - public AzureAppRoleCacheRepository( - FintCache appRoleCache, PermittedAppRoles permittedAppRoles - ) { - this.appRoleCache = appRoleCache; - this.permittedAppRoles = permittedAppRoles; - } - - public void save(AppRole appRole) { - if (appRole.id != null) { - appRoleCache.put(appRole.id.toString(), appRole); - } else { - log.warn("App role {} was not saved in cache because id is null", appRole); - } - } - - public void saveAllPermittedRoles(List appRoles) { - appRoles.forEach(appRole -> { - if (permittedAppRoles.getPermittedAppRoles().containsValue(appRole.value)) { - save(appRole); - } - } - ); - } - - public FintCache findAll() { - return appRoleCache; - } -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java deleted file mode 100644 index 702a9ab..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/AzureGroupMembersCacheRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; -import org.springframework.stereotype.Repository; - -@Repository -public class AzureGroupMembersCacheRepository { - - private final FintCache groupMembersCache; - - public AzureGroupMembersCacheRepository(FintCache groupMembersCache) { - this.groupMembersCache = groupMembersCache; - } - - public void saveAll(String principalId, GroupMembersWrapper groupMembers) { - groupMembersCache.put(principalId, groupMembers); - } - - public GroupMembersWrapper findAllByPrincipalId(String principalId) { - return groupMembersCache.get(principalId); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java b/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java deleted file mode 100644 index d01d88b..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/repositories/UserDisplayTextCacheRepository.java +++ /dev/null @@ -1,37 +0,0 @@ -package no.fintlabs.flyt.azure.repositories; - -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public class UserDisplayTextCacheRepository { - private final FintCache userDisplayTextCache; - - public UserDisplayTextCacheRepository(FintCache userDisplayTextCache) { - this.userDisplayTextCache = userDisplayTextCache; - } - - public void save(UserDisplayText userDisplayText) { - userDisplayTextCache.put(userDisplayText.getObjectIdentifier(), userDisplayText); - } - - public void saveAll(List userDisplayTextCaches) { - userDisplayTextCaches.forEach(this::save); - } - - public Optional findByObjectIdentifier(String objectIdentifier) { - return userDisplayTextCache.getOptional(objectIdentifier); - } - - public FintCache findAll() { - return userDisplayTextCache; - } - - public void deleteByObjectIdentifier(String objectIdentifier) { - userDisplayTextCache.remove(objectIdentifier); - } -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java deleted file mode 100644 index 8ffd085..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureADUserSyncService.java +++ /dev/null @@ -1,144 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import com.microsoft.graph.models.User; -import com.microsoft.graph.requests.GraphServiceClient; -import com.microsoft.graph.requests.UserCollectionPage; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.flyt.authorization.userpermission.UserPermissionEntity; -import no.fintlabs.flyt.authorization.userpermission.UserPermissionService; -import no.fintlabs.flyt.azure.Config; -import no.fintlabs.flyt.azure.StringUtils; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import no.fintlabs.flyt.azure.models.ConfigUser; -import no.fintlabs.flyt.azure.models.PermittedAppRoles; -import okhttp3.Request; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -@Component -@Slf4j -@RequiredArgsConstructor -public class AzureADUserSyncService { - protected final Config config; - protected final ConfigUser configUser; - protected final PermittedAppRoles permittedAppRoles; - protected final GraphServiceClient graphService; - protected final UserPermissionService userPermissionService; - protected final AzureAppRoleCacheService azureAppRoleCacheService; - protected final AzureUserCacheService azureUserCacheService; - - @Scheduled( - initialDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.initial-delay-ms}", - fixedDelayString = "${fint.flyt.azure-ad-gateway.user-scheduler.pull.fixed-delay-ms}" - ) - private void pullUsersAndRoles() { - - validateConfig(); - - log.info("*** <<< Starting to pull users from Azure AD >>> ***"); - long startTime = System.currentTimeMillis(); - - azureAppRoleCacheService.storeAzureAppRoleDataInCache(config.getAppId()); - - try { - UserCollectionPage usersPage = graphService.users() - .buildRequest() - .select(String.join(",", configUser.allAttributes())) - .filter("usertype eq 'member'") - .get(); - - processUsers(usersPage); - - long endTime = System.currentTimeMillis(); - long elapsedTimeInSeconds = (endTime - startTime) / 1000; - long minutes = elapsedTimeInSeconds / 60; - long seconds = elapsedTimeInSeconds % 60; - - log.info("*** <<< Finished pulling users from Azure AD in {} minutes and {} seconds >>> *** ", minutes, seconds); - } catch (Exception e) { - log.error("Error fetching users : {}", e.getMessage(), e); - } - } - - private void validateConfig() { - if (isNullOrEmpty(config.getClientId()) || - isNullOrEmpty(config.getClientSecret()) || - isNullOrEmpty(config.getTenantId()) || - isNullOrEmpty(config.getAppId())) { - throw new IllegalArgumentException("Azure AD configuration is not properly set."); - } - } - - private boolean isNullOrEmpty(String value) { - return value == null || value.isBlank() || "null".equalsIgnoreCase(value); - } - - private void processUsers(UserCollectionPage userCollectionPage) { - int usersProcessed = 0; - UserCollectionPage currentPage = userCollectionPage; - - List userPermissionEntityList = new ArrayList<>(); - List userDisplayTextCaches = new ArrayList<>(); - try { - do { - for (User user : currentPage.getCurrentPage()) { - - if (user.mail == null) { - continue; - } - - usersProcessed++; - - List userRoles = azureAppRoleCacheService.getUserRoles(user.id, user.mail, config.getAppId()); - - if (isPermittedRole(userRoles)) { - UserPermissionEntity userPermissionEntity = UserPermissionEntity - .builder() - .objectIdentifier(user.id) - .build(); - userPermissionEntityList.add(userPermissionEntity); - - UserDisplayText userDisplayText = UserDisplayText - .builder() - .objectIdentifier(user.id) - .email(Objects.requireNonNull(user.mail).toLowerCase()) - .name(StringUtils.capitalizeFirstLetterOfEachWord(user.givenName) + " " + user.surname) - .build(); - userDisplayTextCaches.add(userDisplayText); - } - - } - if (currentPage.getNextPage() != null) { - currentPage = currentPage.getNextPage().buildRequest().get(); - } else { - currentPage = null; - } - } while (currentPage != null); - - userPermissionService.refreshUserPermissions(userPermissionEntityList); - userPermissionEntityList.forEach(userPermission -> log.info("Saving user permission {} in db", userPermission.getObjectIdentifier())); - - azureUserCacheService.refreshAzureUserCaches(userDisplayTextCaches); - userDisplayTextCaches.forEach(azureUserCache -> log.debug("Saving azure user {} in cache", azureUserCache.getEmail())); - - log.info("{} User objects processed in Azure AD", usersProcessed); - } catch (Exception e) { - log.error("An error occurred while processing user permissions: {}", e.getMessage()); - } - } - - private boolean isPermittedRole(List userRoles) { - for (String role : userRoles) { - if (permittedAppRoles.getPermittedAppRoles().containsValue(role)) { - return true; - } - } - return false; - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java deleted file mode 100644 index 4b0b037..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppGraphService.java +++ /dev/null @@ -1,94 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import com.microsoft.graph.models.*; -import com.microsoft.graph.options.QueryOption; -import com.microsoft.graph.requests.GraphServiceClient; -import com.microsoft.graph.requests.ServicePrincipalCollectionPage; -import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; -import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; -import okhttp3.Request; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.stream.Collectors; - -@Service -public class AzureAppGraphService { - - protected final GraphServiceClient graphService; - - public AzureAppGraphService( - GraphServiceClient graphService - ) { - this.graphService = graphService; - } - - public AppRoleAssignmentWrapper getAppRoleAssignments(String appId) { - List requestOptions = new ArrayList<>(); - requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); - - ServicePrincipalCollectionPage servicePrincipalCollectionPage = graphService - .servicePrincipals() - .buildRequest(requestOptions) - .get(); - - Objects.requireNonNull(servicePrincipalCollectionPage); - - List servicePrincipals = servicePrincipalCollectionPage.getCurrentPage(); - - if (!servicePrincipals.isEmpty()) { - ServicePrincipal servicePrincipal = servicePrincipals.get(0); - if (servicePrincipal != null && servicePrincipal.id != null) { - List appRoleAssignments = Objects.requireNonNull(graphService - .servicePrincipals(servicePrincipal.id) - .appRoleAssignedTo() - .buildRequest() - .get()) - .getCurrentPage(); - return new AppRoleAssignmentWrapper(appRoleAssignments); - } - } - return new AppRoleAssignmentWrapper(new ArrayList<>()); - } - - public List getAppRoles(String appId) { - List requestOptions = new ArrayList<>(); - requestOptions.add(new QueryOption("$filter", "appId eq '" + appId + "'")); - List servicePrincipals = Objects.requireNonNull(graphService - .servicePrincipals() - .buildRequest(requestOptions) - .get()) - .getCurrentPage(); - if (!servicePrincipals.isEmpty()) { - ServicePrincipal servicePrincipal = servicePrincipals.get(0); - if (servicePrincipal != null && servicePrincipal.id != null) { - return servicePrincipal.appRoles; - } - } - return new ArrayList<>(); - } - - public GroupMembersWrapper getGroupMembers(String groupId) { - List members = Objects.requireNonNull(graphService - .groups(groupId) - .members() - .buildRequest() - .get()) - .getCurrentPage(); - - List users = members.stream() - .filter(directoryObject -> directoryObject instanceof User) - .map(directoryObject -> (User) directoryObject) - .collect(Collectors.toList()); - - return new GroupMembersWrapper(users); - } - - public GroupMembersWrapper getGroupMembers(List groupIds) { - graphService. - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java deleted file mode 100644 index be96ca5..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureAppRoleCacheService.java +++ /dev/null @@ -1,146 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import com.microsoft.graph.models.AppRole; -import com.microsoft.graph.models.AppRoleAssignment; -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.PermittedAppRoles; -import no.fintlabs.flyt.azure.models.wrappers.AppRoleAssignmentWrapper; -import no.fintlabs.flyt.azure.models.wrappers.GroupMembersWrapper; -import no.fintlabs.flyt.azure.repositories.AzureAppRoleAssignmentCacheRepository; -import no.fintlabs.flyt.azure.repositories.AzureAppRoleCacheRepository; -import no.fintlabs.flyt.azure.repositories.AzureGroupMembersCacheRepository; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -@Service -@Slf4j -public class AzureAppRoleCacheService { - - protected final AzureAppGraphService azureAppGraphService; - - protected final AzureAppRoleCacheRepository azureAppRoleCacheRepository; - protected final AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository; - protected final AzureGroupMembersCacheRepository azureGroupMembersCacheRepository; - - public AzureAppRoleCacheService( - AzureAppGraphService azureAppGraphService, - AzureAppRoleCacheRepository azureAppRoleCacheRepository, - AzureAppRoleAssignmentCacheRepository azureAppRoleAssignmentCacheRepository, - AzureGroupMembersCacheRepository azureGroupMembersCacheRepository) { - this.azureAppGraphService = azureAppGraphService; - this.azureAppRoleCacheRepository = azureAppRoleCacheRepository; - this.azureAppRoleAssignmentCacheRepository = azureAppRoleAssignmentCacheRepository; - this.azureGroupMembersCacheRepository = azureGroupMembersCacheRepository; - } - - public void getAbc(String appId, Set permittedAppRoleIds) { - - // todo: remove wrapper - List appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId) - .getAppRoleAssignments(); - - List permittedGroupIds = appRoleAssignments.stream() - .filter(appRoleAssignment -> "Group".equals(appRoleAssignment.principalType)) - .filter(appRoleAssignment -> permittedAppRoleIds.contains(appRoleAssignment.appRoleId)) - .map(appRoleAssignment -> appRoleAssignment.principalId) - .toList(); - - azureAppGraphService.getGroupMembers() - - appRoleAssignments.stream() - .filter(appRoleAssignment -> "User".equals(appRoleAssignment.principalType)) - .filter(appRoleAssignment -> permittedAppRoleIds.) - - } - - public void storeAzureAppRoleDataInCache(String appId) { - AppRoleAssignmentWrapper appRoleAssignments = azureAppGraphService.getAppRoleAssignments(appId); - azureAppRoleAssignmentCacheRepository.saveAll(appId, appRoleAssignments); - - appRoleAssignments.getAppRoleAssignments().forEach(appRoleAssignment -> { - if (appRoleAssignment.principalType == null || appRoleAssignment.principalId == null) { - log.warn("Assignment principalType or principalId is null for appRoleAssignment {}", appRoleAssignment); - return; - } - - String principalType = appRoleAssignment.principalType; - String principalId = appRoleAssignment.principalId.toString(); - - if (principalType.equals("Group")) { - GroupMembersWrapper groupMembers = azureAppGraphService.getGroupMembers(principalId); - azureGroupMembersCacheRepository.saveAll(principalId, groupMembers); - } - }); - - List appRoles = azureAppGraphService.getAppRoles(appId); - azureAppRoleCacheRepository.saveAllPermittedRoles(appRoles); - } - - public List getUserRoles( - String userId, - String email, - String appId - ) { - List roles = new ArrayList<>(); - - AppRoleAssignmentWrapper appRoleAssignments = this.azureAppRoleAssignmentCacheRepository.findAllByAppId(appId); - - appRoleAssignments.getAppRoleAssignments().forEach(assignment -> { - if (assignment.principalType == null || assignment.principalId == null) { - log.debug("Assignment principalType or principalId is null for assignment {}", assignment); - return; - } - - String principalType = assignment.principalType; - String principalId = assignment.principalId.toString(); - - if (principalType.equals("User") && principalId.equals(userId)) { - addRoleIfNotNull(roles, assignment, appId); - } else if (principalType.equals("Group")) { - GroupMembersWrapper groupMembers = azureGroupMembersCacheRepository.findAllByPrincipalId(principalId); - groupMembers.getGroupMembers().forEach(user -> { - if (user.id != null && user.id.equals(userId)) { - addRoleIfNotNull(roles, assignment, appId); - } - }); - } - }); - - if (roles.isEmpty()) { - log.debug("User with email {} has no roles assigned in app {}", email, appId); - } else { - log.debug("User with email {} has the following roles in app {}: {}", email, appId, roles); - } - - return roles; - } - - private void addRoleIfNotNull(List roles, AppRoleAssignment assignment, String appId) { - String roleName = getAppRoleValue(assignment.appRoleId, appId); - if (roleName != null) { - roles.add(roleName); - } else { - log.debug("Role name for appRoleId {} not found", assignment.appRoleId); - } - } - - private String getAppRoleValue(UUID appRoleId, String appId) { - FintCache appRoles = azureAppRoleCacheRepository.findAll(); - - for (AppRole appRole : appRoles.getAll()) { - if (appRole.id != null && appRole.id.equals(appRoleId)) { - return appRole.value; - } - } - - log.debug("Role with appRoleId: {} not found in appId: {}", appRoleId, appId); - return null; - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java b/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java deleted file mode 100644 index 7d75320..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/AzureUserCacheService.java +++ /dev/null @@ -1,49 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.cache.FintCache; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import no.fintlabs.flyt.azure.repositories.UserDisplayTextCacheRepository; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -@Slf4j -public class AzureUserCacheService { - - private final UserDisplayTextCacheRepository userDisplayTextCacheRepository; - - public AzureUserCacheService(UserDisplayTextCacheRepository userDisplayTextCacheRepository) { - this.userDisplayTextCacheRepository = userDisplayTextCacheRepository; - } - - public void refreshAzureUserCaches(List userDisplayTextCaches) { - deleteAzureUserCacheNotInList(userDisplayTextCaches); - userDisplayTextCacheRepository.saveAll(userDisplayTextCaches); - } - - private void deleteAzureUserCacheNotInList(List userDisplayTextCaches) { - FintCache allCurrentUserDisplayTextCaches = userDisplayTextCacheRepository.findAll(); - - List inputUserDisplayTextCachesIdentifiers = userDisplayTextCaches.stream() - .map(UserDisplayText::getObjectIdentifier) - .toList(); - - List allCurrentEntries = allCurrentUserDisplayTextCaches.getAll(); - - List allCurrentKeys = allCurrentEntries.stream() - .map(UserDisplayText::getObjectIdentifier) - .toList(); - - List keysToDelete = allCurrentKeys.stream() - .filter(key -> !inputUserDisplayTextCachesIdentifiers.contains(key)) - .toList(); - - keysToDelete.forEach(userDisplayTextCacheRepository::deleteByObjectIdentifier); - - if (!keysToDelete.isEmpty()) { - log.info("Deleted {} user permissions", keysToDelete.size()); - } - } -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java new file mode 100644 index 0000000..a317fa9 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java @@ -0,0 +1,94 @@ +package no.fintlabs.flyt.azure.services; + +import com.microsoft.graph.models.AppRoleAssignment; +import com.microsoft.graph.models.ServicePrincipal; +import com.microsoft.graph.requests.AppRoleAssignmentCollectionPage; +import com.microsoft.graph.requests.GraphServiceClient; +import lombok.AllArgsConstructor; +import lombok.Getter; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toMap; + + +@Service +public class GraphAppRoleService { + + private final GraphServiceClient graphServiceClient; + + public GraphAppRoleService(GraphServiceClient graphServiceClient) { + this.graphServiceClient = graphServiceClient; + } + + public Set getAppRoleIdsFromAppRoleValues(UUID servicePrincipalId, Set appRoleValues) { + ServicePrincipal servicePrincipal = graphServiceClient.servicePrincipals(servicePrincipalId.toString()) + .buildRequest() + .select("appRoles") + .get(); + + if (servicePrincipal == null) { + throw new IllegalStateException("Service principal is null"); + } + if (servicePrincipal.appRoles == null) { + throw new IllegalStateException("Service principal has no roles"); + } + + Map appRoleIdPerValue = servicePrincipal.appRoles + .stream() + .filter(appRole -> Objects.nonNull(appRole.id)) + .filter(appRole -> appRoleValues.contains(appRole.value)) + .collect(toMap( + appRole -> appRole.value, + appRole -> appRole.id.toString() + )); + + Set appRoleValuesThatCouldNotBeFound = new HashSet<>(appRoleValues); + appRoleValuesThatCouldNotBeFound.removeAll(appRoleIdPerValue.keySet()); + if (!appRoleValuesThatCouldNotBeFound.isEmpty()) { + throw new IllegalStateException("Could not find id for app roles with values: " + appRoleValuesThatCouldNotBeFound); + } + + return appRoleIdPerValue.values().stream() + .map(UUID::fromString) + .collect(Collectors.toSet()); + } + + @Getter + @AllArgsConstructor + public static class AppRoleIdAndPrincipalSelection { + private final UUID appRoleId; + private final String principalType; + private final UUID principalId; + } + + public List getAppRoleAssignments(UUID servicePrincipalId) { + AppRoleAssignmentCollectionPage appRoleAssignmentCollectionPage = graphServiceClient + .servicePrincipals(servicePrincipalId.toString()) + .appRoleAssignedTo() + .buildRequest() + .select("appRoleId,principalType,principalId") + .get(); + + if (appRoleAssignmentCollectionPage == null) { + throw new IllegalStateException("Could not retrieve app role assignments"); + } + + return appRoleAssignmentCollectionPage.getCurrentPage() + .stream() + .map(this::fromAppRoleAssignment) + .toList(); // TODO eivindmorch 27/06/2024 : Handle multi page + } + + private AppRoleIdAndPrincipalSelection fromAppRoleAssignment(AppRoleAssignment appRoleAssignment) { + return new AppRoleIdAndPrincipalSelection( + appRoleAssignment.appRoleId, + appRoleAssignment.principalType, + appRoleAssignment.principalId + ); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java new file mode 100644 index 0000000..4d4ce3b --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java @@ -0,0 +1,58 @@ +package no.fintlabs.flyt.azure.services; + +import com.microsoft.graph.requests.GraphServiceClient; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +public class GraphGroupService { + + private final GraphServiceClient graphServiceClient; + + public GraphGroupService(GraphServiceClient graphServiceClient) { + this.graphServiceClient = graphServiceClient; + } + + public Set getGroupUserMemberIds(Collection groupIds) { + return groupIds.stream() + .map(this::getGroupUserMemberIds) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + // TODO eivindmorch 27/06/2024 : Only include of principal type User + public Set getGroupUserMemberIds(UUID groupId) { + return graphServiceClient + .groups(groupId.toString()) + .members() + .buildRequest() + .select("id") + .get().getCurrentPage() // TODO eivindmorch 27/06/2024 : Get all pages + .stream() + .map(directoryObject -> directoryObject.id) + .filter(Objects::nonNull) + .map(UUID::fromString) + .collect(Collectors.toSet()); + } + + + // TODO eivindmorch 27/06/2024 : Move to util +// public List getContentFromCurrentAndNextPages(DirectoryObjectGetByIdsCollectionRequestBuilder baseActionCollectionRequest) { +// DirectoryObjectGetByIdsCollectionPage postResult = baseActionCollectionRequest.buildRequest().post(); +// if (postResult == null) { +// throw new IllegalStateException("Post result is null"); +// } +// ArrayList currentContent = new ArrayList<>(postResult.getCurrentPage()); +// if (postResult.getNextPage() != null) { +// currentContent.addAll(getContentFromCurrentAndNextPages(postResult.getNextPage())); +// } +// +// } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java new file mode 100644 index 0000000..7dbcb4a --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java @@ -0,0 +1,75 @@ +package no.fintlabs.flyt.azure.services; + +import no.fintlabs.flyt.azure.configuration.AzureAdGatewayConfiguration; +import no.fintlabs.flyt.azure.models.GraphUserInfo; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +public class GraphService { + + private final AzureAdGatewayConfiguration azureAdGatewayConfiguration; + + private final GraphAppRoleService graphAppRoleService; + + private final GraphGroupService graphGroupService; + + private final GraphUserService graphUserService; + + public GraphService( + AzureAdGatewayConfiguration azureAdGatewayConfiguration, + GraphAppRoleService graphAppRoleService, + GraphGroupService graphGroupService, + GraphUserService graphUserService + ) { + this.azureAdGatewayConfiguration = azureAdGatewayConfiguration; + this.graphAppRoleService = graphAppRoleService; + this.graphGroupService = graphGroupService; + this.graphUserService = graphUserService; + } + + public List getPermittedUsersInfo() { +// UUID servicePrincipalId = graphServicePrincipalService.getServicePrincipalId(config.getAppId()); + // TODO eivindmorch 27/06/2024 : Hvorfor bruker vi app id og ikke service principal id direkte? + UUID servicePrincipalId = UUID.fromString(""); // TODO eivindmorch 27/06/2024 : Get from config + + Set permittedAppRoleIds = graphAppRoleService.getAppRoleIdsFromAppRoleValues( + servicePrincipalId, + Set.of(azureAdGatewayConfiguration.getPermittedAppRoles().getFlytUser()) + ); + + Set permittedUserIds = getPermittedUserIds( + servicePrincipalId, + permittedAppRoleIds + ); + + return graphUserService.getUserInfo(permittedUserIds); + } + + private Set getPermittedUserIds(UUID servicePrincipalId, Set permittedAppRoleIds) { + List appRoleAssignments = + graphAppRoleService.getAppRoleAssignments(servicePrincipalId); + + Map> idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType = appRoleAssignments.stream() + .filter(appRoleAssignment -> permittedAppRoleIds.contains(appRoleAssignment.getAppRoleId())) + .collect(Collectors.groupingBy( + GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalType, + Collectors.mapping( + GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalId, + Collectors.toSet() + ) + )); + + return Stream.concat( + graphGroupService.getGroupUserMemberIds(idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("Group")).stream(), + idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("User").stream() + ).collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java new file mode 100644 index 0000000..69a439d --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java @@ -0,0 +1,43 @@ +package no.fintlabs.flyt.azure.services; + +import com.microsoft.graph.models.AppRoleAssignment; +import com.microsoft.graph.options.QueryOption; +import com.microsoft.graph.requests.AppRoleAssignmentCollectionPage; +import com.microsoft.graph.requests.GraphServiceClient; +import com.microsoft.graph.requests.ServicePrincipalCollectionPage; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class GraphServicePrincipalService { + // TODO eivindmorch 27/06/2024 : Rename service? + private final GraphServiceClient graphServiceClient; + + public GraphServicePrincipalService(GraphServiceClient graphServiceClient) { + this.graphServiceClient = graphServiceClient; + } + + // TODO eivindmorch 27/06/2024 : Remove? + public UUID getServicePrincipalId(UUID appId) { + ServicePrincipalCollectionPage servicePrincipalSearchResult = graphServiceClient + .servicePrincipals() + .buildRequest(new QueryOption("$filter", "appId eq '" + appId.toString() + "'")) + .get();// TODO eivindmorch 27/06/2024 : Select only needed data + + if (servicePrincipalSearchResult == null || servicePrincipalSearchResult.getCurrentPage().isEmpty()) { + throw new IllegalStateException("No principal for application found"); + } + if (servicePrincipalSearchResult.getCurrentPage().size() > 1 || servicePrincipalSearchResult.getNextPage() != null) { + throw new IllegalStateException("Found multiple service principals for application"); + } + String servicePrincipalId = servicePrincipalSearchResult.getCurrentPage().get(0).id; + if (servicePrincipalId == null) { + throw new IllegalStateException("Service principal id is null"); + } + return UUID.fromString(servicePrincipalId); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java new file mode 100644 index 0000000..901dc84 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java @@ -0,0 +1,50 @@ +package no.fintlabs.flyt.azure.services; + +import com.microsoft.graph.models.DirectoryObjectGetByIdsParameterSet; +import com.microsoft.graph.models.User; +import com.microsoft.graph.requests.GraphServiceClient; +import no.fintlabs.flyt.azure.models.GraphUserInfo; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +@Service +public class GraphUserService { + + private final GraphServiceClient graphServiceClient; + + public GraphUserService(GraphServiceClient graphServiceClient) { + this.graphServiceClient = graphServiceClient; + } + + // TODO eivindmorch 27/06/2024 : Do pull and cache of display info here + public List getUserInfo(Collection userIds) { + return graphServiceClient.users() + .getByIds(DirectoryObjectGetByIdsParameterSet + .newBuilder() + .withIds(userIds.stream().map(UUID::toString).toList()) + .build() + ).buildRequest() + .select("id,mail,displayName") + .post() +// .setRawObject(new DefaultSerializer(), UserInfo.class) + .getCurrentPage() // TODO eivindmorch 27/06/2024 : handle multi page + .stream() + .filter(user -> user instanceof User) + .map(user -> (User) user) + .filter(user -> Objects.nonNull(user.id)) + .map(user -> GraphUserInfo + .builder() + .id(UUID.fromString(user.id)) + .displayName(user.displayName) + .mail(user.mail) + .build() + ) + .toList(); + } + +} From e9367a99b0400e708013bf1c12cd6d64b3a9d989 Mon Sep 17 00:00:00 2001 From: Eivind Morch Date: Fri, 28 Jun 2024 13:50:33 +0200 Subject: [PATCH 31/50] cleanup --- .../flyt/authorization/AuthorizationUtil.java | 19 ---- .../client}/ClientAuthorization.java | 4 +- ...entAuthorizationProducerRecordBuilder.java | 12 +-- ...ientAuthorizationRequestConfiguration.java | 2 +- .../AcosSourceApplication.java | 4 +- .../DigisakSourceApplication.java | 4 +- .../EgrunnervervSourceApplication.java | 4 +- .../VigoSourceApplication.java | 4 +- ...e.java => UserAuthorizationComponent.java} | 45 ++++++--- .../UserPermissionRepository.java | 5 +- .../UserPermissionService.java | 11 ++- .../user}/azure/StringUtils.java | 2 +- .../AzureAdGatewayConfiguration.java | 5 +- .../AzureCredentialsConfiguration.java | 2 +- .../GraphServiceConfiguration.java | 8 +- .../PermittedAppRolesProperties.java} | 4 +- .../RequiredPropertiesCondition.java | 3 +- .../UserDisplayTextCacheConfiguration.java | 4 +- .../user}/azure/models/GraphUserInfo.java | 2 +- .../user}/azure/models/UserDisplayText.java | 2 +- .../azure/services/GraphAppRoleService.java | 47 +++++---- .../azure/services/GraphGroupService.java | 60 ++++++++++++ .../services/GraphPageWalkerService.java | 95 ++++++++++++++++++ .../user/azure/services/GraphService.java | 96 +++++++++++++++++++ .../GraphServicePrincipalService.java | 5 +- .../user/azure/services/GraphUserService.java | 59 ++++++++++++ .../user/controller/MeController.java | 85 +++++++++++----- .../user/controller/UserController.java | 28 +++--- .../controller/utils/TokenParsingUtils.java | 33 +++++++ .../RestrictedPageAuthorization.java | 2 +- .../flyt/authorization/user/model/User.java | 2 +- .../model/UserPermission.java | 4 +- .../model/UserPermissionEntity.java | 4 +- .../azure/services/GraphGroupService.java | 58 ----------- .../flyt/azure/services/GraphService.java | 75 --------------- .../flyt/azure/services/GraphUserService.java | 50 ---------- .../resources/application-flyt-azure.yaml | 7 +- ...uthorizationProducerRecordBuilderTest.java | 9 +- 38 files changed, 538 insertions(+), 327 deletions(-) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java rename src/main/java/no/fintlabs/{ => flyt/authorization/client}/ClientAuthorization.java (51%) rename src/main/java/no/fintlabs/{ => flyt/authorization/client}/ClientAuthorizationProducerRecordBuilder.java (82%) rename src/main/java/no/fintlabs/{ => flyt/authorization/client}/ClientAuthorizationRequestConfiguration.java (97%) rename src/main/java/no/fintlabs/flyt/{models/sourceapplication => authorization/client/sourceapplications}/AcosSourceApplication.java (74%) rename src/main/java/no/fintlabs/flyt/{models/sourceapplication => authorization/client/sourceapplications}/DigisakSourceApplication.java (75%) rename src/main/java/no/fintlabs/flyt/{models/sourceapplication => authorization/client/sourceapplications}/EgrunnervervSourceApplication.java (75%) rename src/main/java/no/fintlabs/flyt/{models/sourceapplication => authorization/client/sourceapplications}/VigoSourceApplication.java (74%) rename src/main/java/no/fintlabs/flyt/authorization/user/{UserService.java => UserAuthorizationComponent.java} (69%) rename src/main/java/no/fintlabs/flyt/authorization/user/{permission => }/UserPermissionRepository.java (77%) rename src/main/java/no/fintlabs/flyt/authorization/user/{permission => }/UserPermissionService.java (89%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/StringUtils.java (93%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/configuration/AzureAdGatewayConfiguration.java (73%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/configuration/AzureCredentialsConfiguration.java (91%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/configuration/GraphServiceConfiguration.java (85%) rename src/main/java/no/fintlabs/flyt/{azure/configuration/PermittedAppRoles.java => authorization/user/azure/configuration/PermittedAppRolesProperties.java} (58%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/configuration/RequiredPropertiesCondition.java (94%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/configuration/UserDisplayTextCacheConfiguration.java (78%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/models/GraphUserInfo.java (78%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/models/UserDisplayText.java (78%) rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/services/GraphAppRoleService.java (61%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java rename src/main/java/no/fintlabs/flyt/{ => authorization/user}/azure/services/GraphServicePrincipalService.java (90%) create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java rename src/main/java/no/fintlabs/flyt/authorization/user/{permission => model}/RestrictedPageAuthorization.java (72%) rename src/main/java/no/fintlabs/flyt/authorization/user/{permission => }/model/UserPermission.java (75%) rename src/main/java/no/fintlabs/flyt/authorization/user/{permission => }/model/UserPermissionEntity.java (88%) delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphService.java delete mode 100644 src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java b/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java deleted file mode 100644 index 3714374..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/AuthorizationUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package no.fintlabs.flyt.authorization; - -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.stereotype.Service; -import reactor.core.publisher.Mono; - -@Service -public class AuthorizationUtil { - public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticationToken) { - return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString(); - } - - public Mono isAdmin(Mono authenticationMono) { - return authenticationMono - .map(authentication -> authentication != null && authentication.getAuthorities().stream() - .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); - } -} diff --git a/src/main/java/no/fintlabs/ClientAuthorization.java b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java similarity index 51% rename from src/main/java/no/fintlabs/ClientAuthorization.java rename to src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java index 8b87ef6..583a780 100644 --- a/src/main/java/no/fintlabs/ClientAuthorization.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java @@ -1,4 +1,4 @@ -package no.fintlabs; +package no.fintlabs.flyt.authorization.client; import lombok.Builder; import lombok.Getter; @@ -8,5 +8,5 @@ public class ClientAuthorization { private final boolean authorized; private final String clientId; - private final String sourceApplicationId; + private final Long sourceApplicationId; // TODO eivindmorch 28/06/2024 : Endret til long. Fikse andre steder? } diff --git a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationProducerRecordBuilder.java similarity index 82% rename from src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java rename to src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationProducerRecordBuilder.java index 7ceef4b..0cefbee 100644 --- a/src/main/java/no/fintlabs/ClientAuthorizationProducerRecordBuilder.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationProducerRecordBuilder.java @@ -1,9 +1,9 @@ -package no.fintlabs; +package no.fintlabs.flyt.authorization.client; -import no.fintlabs.flyt.models.sourceapplication.AcosSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.DigisakSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.VigoSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.AcosSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.DigisakSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.EgrunnervervSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.VigoSourceApplication; import no.fintlabs.kafka.requestreply.ReplyProducerRecord; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.stereotype.Component; @@ -34,7 +34,7 @@ public ReplyProducerRecord apply(ConsumerRecord buildReplyProducerRecord(String clientId, String sourceApplicationId) { + private ReplyProducerRecord buildReplyProducerRecord(String clientId, Long sourceApplicationId) { return ReplyProducerRecord.builder() .value(ClientAuthorization .builder() diff --git a/src/main/java/no/fintlabs/ClientAuthorizationRequestConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationRequestConfiguration.java similarity index 97% rename from src/main/java/no/fintlabs/ClientAuthorizationRequestConfiguration.java rename to src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationRequestConfiguration.java index 6287ba6..2974b32 100644 --- a/src/main/java/no/fintlabs/ClientAuthorizationRequestConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorizationRequestConfiguration.java @@ -1,4 +1,4 @@ -package no.fintlabs; +package no.fintlabs.flyt.authorization.client; import no.fintlabs.kafka.common.topic.TopicCleanupPolicyParameters; import no.fintlabs.kafka.requestreply.RequestConsumerFactoryService; diff --git a/src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/AcosSourceApplication.java similarity index 74% rename from src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java rename to src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/AcosSourceApplication.java index f19176f..8b78f09 100644 --- a/src/main/java/no/fintlabs/flyt/models/sourceapplication/AcosSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/AcosSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.models.sourceapplication; +package no.fintlabs.flyt.authorization.client.sourceapplications; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; @@ -8,7 +8,7 @@ @Getter public class AcosSourceApplication { public static String CLIENT_ID; - public static final String SOURCE_APPLICATION_ID = "1"; + public static final long SOURCE_APPLICATION_ID = 1L; @Value("${fint.flyt.acos.sso.client-id:#{null}}") public void setClientId(String clientId) { diff --git a/src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/DigisakSourceApplication.java similarity index 75% rename from src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java rename to src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/DigisakSourceApplication.java index 573b04b..71efdbc 100644 --- a/src/main/java/no/fintlabs/flyt/models/sourceapplication/DigisakSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/DigisakSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.models.sourceapplication; +package no.fintlabs.flyt.authorization.client.sourceapplications; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; @@ -8,7 +8,7 @@ @Getter public class DigisakSourceApplication { public static String CLIENT_ID; - public static final String SOURCE_APPLICATION_ID = "3"; + public static final long SOURCE_APPLICATION_ID = 3L; @Value("${fint.flyt.digisak.sso.client-id:#{null}}") public void setClientId(String clientId) { diff --git a/src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/EgrunnervervSourceApplication.java similarity index 75% rename from src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java rename to src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/EgrunnervervSourceApplication.java index c45f204..61686dc 100644 --- a/src/main/java/no/fintlabs/flyt/models/sourceapplication/EgrunnervervSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/EgrunnervervSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.models.sourceapplication; +package no.fintlabs.flyt.authorization.client.sourceapplications; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; @@ -8,7 +8,7 @@ @Getter public class EgrunnervervSourceApplication { public static String CLIENT_ID; - public static final String SOURCE_APPLICATION_ID = "2"; + public static final long SOURCE_APPLICATION_ID = 2L; @Value("${fint.flyt.egrunnerverv.sso.client-id:#{null}}") public void setClientId(String clientId) { diff --git a/src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/VigoSourceApplication.java similarity index 74% rename from src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java rename to src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/VigoSourceApplication.java index 61bf5bf..45f11fe 100644 --- a/src/main/java/no/fintlabs/flyt/models/sourceapplication/VigoSourceApplication.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/sourceapplications/VigoSourceApplication.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.models.sourceapplication; +package no.fintlabs.flyt.authorization.client.sourceapplications; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; @@ -8,7 +8,7 @@ @Getter public class VigoSourceApplication { public static String CLIENT_ID; - public static final String SOURCE_APPLICATION_ID = "4"; + public static final long SOURCE_APPLICATION_ID = 4L; @Value("${fint.flyt.vigo.sso.client-id:#{null}}") public void setClientId(String clientId) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java similarity index 69% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserService.java rename to src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java index 9bc35c2..5c6225f 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java @@ -1,21 +1,25 @@ package no.fintlabs.flyt.authorization.user; +import lombok.extern.slf4j.Slf4j; import no.fintlabs.cache.FintCache; +import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; +import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; +import no.fintlabs.flyt.authorization.user.azure.services.GraphService; import no.fintlabs.flyt.authorization.user.model.User; -import no.fintlabs.flyt.authorization.user.permission.UserPermissionService; -import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; -import no.fintlabs.flyt.azure.models.GraphUserInfo; -import no.fintlabs.flyt.azure.models.UserDisplayText; -import no.fintlabs.flyt.azure.services.GraphService; -import org.springframework.stereotype.Service; +import no.fintlabs.flyt.authorization.user.model.UserPermission; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; import java.util.*; import java.util.stream.Collectors; import static java.util.stream.Collectors.toMap; -@Service -public class UserService { +@ConditionalOnBean(GraphService.class) +@Component +@Slf4j +public class UserAuthorizationComponent { private final GraphService graphService; @@ -23,21 +27,28 @@ public class UserService { private final FintCache userDisplayTextCache; - public UserService( - UserPermissionService userPermissionService, - FintCache userDisplayTextCache, - GraphService graphService) { + public UserAuthorizationComponent( + GraphService graphService, UserPermissionService userPermissionService, + FintCache userDisplayTextCache + ) { + this.graphService = graphService; this.userPermissionService = userPermissionService; this.userDisplayTextCache = userDisplayTextCache; - this.graphService = graphService; } - // TODO eivindmorch 27/06/2024 : Schedule this + @Scheduled( + initialDelayString = "${fint.flyt.azure-ad-gateway.sync-schedule.initial-delay-ms}", + fixedDelayString = "${fint.flyt.azure-ad-gateway.sync-schedule.fixed-delay-ms}" + ) public void syncUsers() { + log.info("Syncing users"); updateUsers(graphService.getPermittedUsersInfo()); + log.info("Successfully synced users"); } private void updateUsers(Collection permittedUsersGraphUserInfo) { + log.info("Updating users based on {} permitted user info", permittedUsersGraphUserInfo.size()); + Set objectIdentifiers = permittedUsersGraphUserInfo.stream() .map(GraphUserInfo::getId) .collect(Collectors.toSet()); @@ -50,7 +61,12 @@ private void updateUsers(Collection permittedUsersGraphUserInfo) .filter(objectIdentifier -> !objectIdentifiers.contains(objectIdentifier)) .collect(Collectors.toSet()); + log.info("Removing {} user display text with objectIdentifiers: {}", + objectIdentifiersAlreadyCachedButNotInNewUserInfo.size(), + objectIdentifiersAlreadyCachedButNotInNewUserInfo + ); userDisplayTextCache.remove(objectIdentifiersAlreadyCachedButNotInNewUserInfo); + log.info("Putting {} user display text", permittedUsersGraphUserInfo); userDisplayTextCache.put(permittedUsersGraphUserInfo .stream() .collect(toMap( @@ -63,7 +79,6 @@ private void updateUsers(Collection permittedUsersGraphUserInfo) .build() )) ); - } public Optional getUser(UUID objectIdentifier) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java similarity index 77% rename from src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java rename to src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java index 158f8de..137802d 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionRepository.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java @@ -1,6 +1,6 @@ -package no.fintlabs.flyt.authorization.user.permission; +package no.fintlabs.flyt.authorization.user; -import no.fintlabs.flyt.authorization.user.permission.model.UserPermissionEntity; +import no.fintlabs.flyt.authorization.user.model.UserPermissionEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -10,7 +10,6 @@ @Repository public interface UserPermissionRepository extends JpaRepository { - // TODO eivindmorch 27/06/2024 : Index on object identifier Optional findByObjectIdentifier(UUID sub); List findAllByObjectIdentifierIn(Collection objectIdentifiers); diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java similarity index 89% rename from src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java rename to src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java index 0a81d32..3adf4c7 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/permission/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java @@ -1,8 +1,8 @@ -package no.fintlabs.flyt.authorization.user.permission; +package no.fintlabs.flyt.authorization.user; import lombok.extern.slf4j.Slf4j; -import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; -import no.fintlabs.flyt.authorization.user.permission.model.UserPermissionEntity; +import no.fintlabs.flyt.authorization.user.model.UserPermission; +import no.fintlabs.flyt.authorization.user.model.UserPermissionEntity; import org.springframework.stereotype.Service; import java.util.*; @@ -24,6 +24,8 @@ public UserPermissionService(UserPermissionRepository userPermissionRepository) } public void updateUserPermissions(Set permittedUsersObjectIdentifiers) { + log.info("Updating user permissions"); + userPermissionRepository.deleteByObjectIdentifierNotIn(permittedUsersObjectIdentifiers); // TODO eivindmorch 27/06/2024 : Soft delete? Set objectIdentifiersAlreadyPersisted = @@ -41,6 +43,7 @@ public void updateUserPermissions(Set permittedUsersObjectIdentifiers) { .build()) .toList() ); + log.info("Successfully updated user permissions"); } public Optional find(UUID objectIdentifier) { @@ -56,7 +59,7 @@ public List getAll() { } public List putAll(List userPermissions) { - Map> sourceApplicationIdsPerObjectIdentifier = userPermissions.stream() + Map> sourceApplicationIdsPerObjectIdentifier = userPermissions.stream() .collect(toMap( UserPermission::getObjectIdentifier, UserPermission::getSourceApplicationIds diff --git a/src/main/java/no/fintlabs/flyt/azure/StringUtils.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java similarity index 93% rename from src/main/java/no/fintlabs/flyt/azure/StringUtils.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java index 49d2262..d6fb214 100644 --- a/src/main/java/no/fintlabs/flyt/azure/StringUtils.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure; +package no.fintlabs.flyt.authorization.user.azure; import java.util.Locale; diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java similarity index 73% rename from src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java index fc46a1a..899dc29 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureAdGatewayConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import lombok.Getter; import lombok.Setter; @@ -18,7 +18,8 @@ @ConfigurationProperties(prefix = "fint.flyt.azure-ad-gateway") public class AzureAdGatewayConfiguration { + // TODO eivindmorch 28/06/2024 : Split properties? @Valid - private PermittedAppRoles permittedAppRoles; + private PermittedAppRolesProperties permittedAppRolesProperties; } diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureCredentialsConfiguration.java similarity index 91% rename from src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureCredentialsConfiguration.java index 79298c6..3db5426 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/AzureCredentialsConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureCredentialsConfiguration.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java similarity index 85% rename from src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java index d4d768a..389ac84 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/GraphServiceConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import com.azure.identity.ClientSecretCredential; import com.azure.identity.ClientSecretCredentialBuilder; @@ -8,8 +8,8 @@ import lombok.Setter; import okhttp3.Request; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.validation.annotation.Validated; @@ -24,7 +24,8 @@ public class GraphServiceConfiguration { @Bean - @Conditional(RequiredPropertiesCondition.class) + @ConditionalOnBean(AzureCredentialsConfiguration.class) +// @Conditional(RequiredPropertiesCondition.class) public GraphServiceClient graphServiceClient(AzureCredentialsConfiguration azureCredentialsConfiguration) { ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() .clientId(azureCredentialsConfiguration.getClientId()) @@ -42,4 +43,5 @@ public GraphServiceClient graphServiceClient(AzureCredentialsConfigurat .authenticationProvider(tokenCredentialAuthProvider) .buildClient(); } + } diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java similarity index 58% rename from src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java index d610545..3bc202f 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/PermittedAppRoles.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import lombok.Getter; import lombok.Setter; @@ -7,7 +7,7 @@ @Getter @Setter -public class PermittedAppRoles { +public class PermittedAppRolesProperties { @NotEmpty private String flytUser; } diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java similarity index 94% rename from src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java index 0db9ac5..825f613 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/RequiredPropertiesCondition.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Condition; @@ -18,4 +18,5 @@ public boolean matches(ConditionContext context, @NotNull AnnotatedTypeMetadata return StringUtils.hasText(clientId) && StringUtils.hasText(clientSecret) && StringUtils.hasText(tenantId) && StringUtils.hasText(appId); } + } diff --git a/src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java similarity index 78% rename from src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java index a6de572..694f726 100644 --- a/src/main/java/no/fintlabs/flyt/azure/configuration/UserDisplayTextCacheConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java @@ -1,8 +1,8 @@ -package no.fintlabs.flyt.azure.configuration; +package no.fintlabs.flyt.authorization.user.azure.configuration; import no.fintlabs.cache.FintCache; import no.fintlabs.cache.FintCacheManager; -import no.fintlabs.flyt.azure.models.UserDisplayText; +import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/GraphUserInfo.java similarity index 78% rename from src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/models/GraphUserInfo.java index 8e55a23..9a0ae02 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/GraphUserInfo.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/GraphUserInfo.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.models; +package no.fintlabs.flyt.authorization.user.azure.models; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java similarity index 78% rename from src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java index 6a400bb..836755e 100644 --- a/src/main/java/no/fintlabs/flyt/azure/models/UserDisplayText.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.azure.models; +package no.fintlabs.flyt.authorization.user.azure.models; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java similarity index 61% rename from src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java index a317fa9..095c6aa 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/GraphAppRoleService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java @@ -1,11 +1,11 @@ -package no.fintlabs.flyt.azure.services; +package no.fintlabs.flyt.authorization.user.azure.services; import com.microsoft.graph.models.AppRoleAssignment; import com.microsoft.graph.models.ServicePrincipal; -import com.microsoft.graph.requests.AppRoleAssignmentCollectionPage; import com.microsoft.graph.requests.GraphServiceClient; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.springframework.stereotype.Service; @@ -16,15 +16,23 @@ @Service +@Slf4j public class GraphAppRoleService { private final GraphServiceClient graphServiceClient; - public GraphAppRoleService(GraphServiceClient graphServiceClient) { + private final GraphPageWalkerService graphPageWalkerService; + + public GraphAppRoleService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { this.graphServiceClient = graphServiceClient; + this.graphPageWalkerService = graphPageWalkerService; } public Set getAppRoleIdsFromAppRoleValues(UUID servicePrincipalId, Set appRoleValues) { + log.info("Retrieving app role ids from app role values: {}", appRoleValues); ServicePrincipal servicePrincipal = graphServiceClient.servicePrincipals(servicePrincipalId.toString()) .buildRequest() .select("appRoles") @@ -52,9 +60,11 @@ public Set getAppRoleIdsFromAppRoleValues(UUID servicePrincipalId, Set appRoleIds = appRoleIdPerValue.values().stream() .map(UUID::fromString) .collect(Collectors.toSet()); + log.info("Successfully retrieved {} app role ids: {}", appRoleIds.size(), appRoleIds); + return appRoleIds; } @Getter @@ -65,22 +75,23 @@ public static class AppRoleIdAndPrincipalSelection { private final UUID principalId; } - public List getAppRoleAssignments(UUID servicePrincipalId) { - AppRoleAssignmentCollectionPage appRoleAssignmentCollectionPage = graphServiceClient - .servicePrincipals(servicePrincipalId.toString()) - .appRoleAssignedTo() - .buildRequest() - .select("appRoleId,principalType,principalId") - .get(); - - if (appRoleAssignmentCollectionPage == null) { - throw new IllegalStateException("Could not retrieve app role assignments"); - } - - return appRoleAssignmentCollectionPage.getCurrentPage() + public List getAppRoleAssignmentsWithAppRoleIds(UUID servicePrincipalId, Set appRoleIds) { + log.info("Retrieving app role assignments with app role ids: {}", appRoleIds); + List appRoleAssignments = graphPageWalkerService.getContentFromCurrentAndNextPages( + graphServiceClient + .servicePrincipals(servicePrincipalId.toString()) + .appRoleAssignedTo() + .buildRequest() + .select("appRoleId,principalType,principalId"), + pageContent -> pageContent.stream() + .filter(appRoleAssignment -> appRoleIds.contains(appRoleAssignment.appRoleId)) + .toList() + ) .stream() .map(this::fromAppRoleAssignment) - .toList(); // TODO eivindmorch 27/06/2024 : Handle multi page + .toList(); + log.info("Successfully retrieved {} app role assignments: {}", appRoleAssignments.size(), appRoleAssignments); + return appRoleAssignments; } private AppRoleIdAndPrincipalSelection fromAppRoleAssignment(AppRoleAssignment appRoleAssignment) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java new file mode 100644 index 0000000..2d2e723 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java @@ -0,0 +1,60 @@ +package no.fintlabs.flyt.authorization.user.azure.services; + +import com.microsoft.graph.requests.GraphServiceClient; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class GraphGroupService { + + private final GraphServiceClient graphServiceClient; + private final GraphPageWalkerService graphPageWalkerService; + + public GraphGroupService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { + this.graphServiceClient = graphServiceClient; + this.graphPageWalkerService = graphPageWalkerService; + } + + public Set getGroupUserMemberIds(Collection groupIds) { + log.info("Retrieving group member ids for groups with ids: {}", groupIds); + Set groupUserMemberIds = groupIds.stream() + .map(this::getGroupUserMemberIds) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + log.info("Successfully retrieved {} group members' ids", groupUserMemberIds.size()); + return groupUserMemberIds; + } + + // TODO eivindmorch 27/06/2024 : Only include of principal type User + public Set getGroupUserMemberIds(UUID groupId) { + log.info("Retrieving group member ids for group with id: {}", groupId); + Set groupMemberIds = graphPageWalkerService.getContentFromCurrentAndNextPages( + graphServiceClient + .groups(groupId.toString()) + .members() + .buildRequest() + .select("id"), + pageContent -> pageContent.stream() + .map(directoryObject -> directoryObject.id) + .filter(Objects::nonNull) + .toList() + ) + .stream() + .map(UUID::fromString) + .collect(Collectors.toSet()); + log.info("Successfully retrieved {} group member ids", groupMemberIds.size()); + return groupMemberIds; + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java new file mode 100644 index 0000000..79c5aa7 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java @@ -0,0 +1,95 @@ +package no.fintlabs.flyt.authorization.user.azure.services; + +import com.microsoft.graph.http.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +@Service +@Slf4j +public class GraphPageWalkerService { + + public < + T, + R, + REQUEST_BUILDER extends BaseRequestBuilder, + COLLECTION_RESPONSE extends BaseCollectionResponse, + COLLECTION_REQUEST_BUILDER extends BaseCollectionRequestBuilder< + T, + REQUEST_BUILDER, + COLLECTION_RESPONSE, + COLLECTION_PAGE, + COLLECTION_REQUEST + >, + COLLECTION_PAGE extends BaseCollectionPage, + COLLECTION_REQUEST extends BaseEntityCollectionRequest + > + List getContentFromCurrentAndNextPages( + COLLECTION_REQUEST collectionRequest, + Function, List> contentProcessing + ) { + return getContentFromCurrentAndNextPages(collectionRequest, BaseEntityCollectionRequest::get, contentProcessing); + } + + public < + T, + R, + REQUEST_BUILDER extends BaseRequestBuilder, + COLLECTION_RESPONSE extends BaseCollectionResponse, + COLLECTION_REQUEST_BUILDER extends BaseCollectionRequestBuilder< + T, + REQUEST_BUILDER, + COLLECTION_RESPONSE, + COLLECTION_PAGE, + COLLECTION_REQUEST + >, + COLLECTION_PAGE extends BaseCollectionPage, + COLLECTION_REQUEST extends BaseActionCollectionRequest + > + List getContentFromCurrentAndNextPages( + COLLECTION_REQUEST collectionRequest, + Function, List> contentProcessing + ) { + return getContentFromCurrentAndNextPages(collectionRequest, BaseActionCollectionRequest::post, contentProcessing); + } + + public < + T, + R, + REQUEST_BUILDER extends BaseRequestBuilder, + COLLECTION_RESPONSE extends BaseCollectionResponse, + COLLECTION_REQUEST_BUILDER extends BaseCollectionRequestBuilder< + T, + REQUEST_BUILDER, + COLLECTION_RESPONSE, + COLLECTION_PAGE, + COLLECTION_REQUEST + >, + COLLECTION_PAGE extends BaseCollectionPage, + COLLECTION_REQUEST extends BaseCollectionRequest + > + List getContentFromCurrentAndNextPages( + COLLECTION_REQUEST collectionRequest, + Function performRequest, + Function, List> contentProcessing + ) { + COLLECTION_PAGE collectionPage = performRequest.apply(collectionRequest); + if (collectionPage == null) { + throw new IllegalStateException("Page is null"); + } + List content = contentProcessing.apply(new ArrayList<>(collectionPage.getCurrentPage())); + COLLECTION_REQUEST_BUILDER nextBuilder = collectionPage.getNextPage(); + if (nextBuilder != null) { + content.addAll(getContentFromCurrentAndNextPages( + collectionPage.getNextPage().buildRequest(), + performRequest, + contentProcessing + )); + } + return content; + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java new file mode 100644 index 0000000..0f4c9d5 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java @@ -0,0 +1,96 @@ +package no.fintlabs.flyt.authorization.user.azure.services; + +import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.authorization.user.azure.configuration.AzureAdGatewayConfiguration; +import no.fintlabs.flyt.authorization.user.azure.configuration.AzureCredentialsConfiguration; +import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Slf4j +@ConditionalOnBean(AzureCredentialsConfiguration.class) +public class GraphService { + + private final AzureCredentialsConfiguration azureCredentialsConfiguration; + + private final AzureAdGatewayConfiguration azureAdGatewayConfiguration; + + private final GraphServicePrincipalService graphServicePrincipalService; + + private final GraphAppRoleService graphAppRoleService; + + private final GraphGroupService graphGroupService; + + private final GraphUserService graphUserService; + + public GraphService( + AzureCredentialsConfiguration azureCredentialsConfiguration, + AzureAdGatewayConfiguration azureAdGatewayConfiguration, + GraphServicePrincipalService graphServicePrincipalService, + GraphAppRoleService graphAppRoleService, + GraphGroupService graphGroupService, + GraphUserService graphUserService + ) { + this.azureCredentialsConfiguration = azureCredentialsConfiguration; + this.azureAdGatewayConfiguration = azureAdGatewayConfiguration; + this.graphServicePrincipalService = graphServicePrincipalService; + this.graphAppRoleService = graphAppRoleService; + this.graphGroupService = graphGroupService; + this.graphUserService = graphUserService; + } + + public List getPermittedUsersInfo() { + log.info("Retrieving permitted users info"); + + // TODO eivindmorch 27/06/2024 : Hvorfor bruker vi app id og ikke service principal id direkte? +// UUID servicePrincipalId = UUID.fromString(""); // TODO eivindmorch 27/06/2024 : Get from config + UUID servicePrincipalId = graphServicePrincipalService.getServicePrincipalId( + UUID.fromString(azureCredentialsConfiguration.getAppId()) + ); + + Set permittedAppRoleIds = graphAppRoleService.getAppRoleIdsFromAppRoleValues( + servicePrincipalId, + Set.of(azureAdGatewayConfiguration.getPermittedAppRolesProperties().getFlytUser()) + ); + + Set permittedUserIds = getPermittedUserIds( + servicePrincipalId, + permittedAppRoleIds + ); + + List userInfo = graphUserService.getUserInfo(permittedUserIds); + log.info("Successfully retrieved {} permitted users", permittedUserIds.size()); + return userInfo; + } + + private Set getPermittedUserIds(UUID servicePrincipalId, Set permittedAppRoleIds) { + List appRoleAssignments = + graphAppRoleService.getAppRoleAssignmentsWithAppRoleIds(servicePrincipalId, permittedAppRoleIds); + + Map> idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType = + appRoleAssignments.stream() + .collect(Collectors.groupingBy( + GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalType, + Collectors.mapping( + GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalId, + Collectors.toSet() + ) + )); + + return Stream.concat( + graphGroupService.getGroupUserMemberIds( + idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("Group") + ).stream(), + idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("User").stream() + ).collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java similarity index 90% rename from src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java rename to src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java index 69a439d..2c94313 100644 --- a/src/main/java/no/fintlabs/flyt/azure/services/GraphServicePrincipalService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java @@ -1,14 +1,11 @@ -package no.fintlabs.flyt.azure.services; +package no.fintlabs.flyt.authorization.user.azure.services; -import com.microsoft.graph.models.AppRoleAssignment; import com.microsoft.graph.options.QueryOption; -import com.microsoft.graph.requests.AppRoleAssignmentCollectionPage; import com.microsoft.graph.requests.GraphServiceClient; import com.microsoft.graph.requests.ServicePrincipalCollectionPage; import okhttp3.Request; import org.springframework.stereotype.Service; -import java.util.List; import java.util.UUID; @Service diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java new file mode 100644 index 0000000..4e4ca59 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java @@ -0,0 +1,59 @@ +package no.fintlabs.flyt.authorization.user.azure.services; + +import com.microsoft.graph.models.DirectoryObjectGetByIdsParameterSet; +import com.microsoft.graph.models.User; +import com.microsoft.graph.requests.GraphServiceClient; +import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; +import okhttp3.Request; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +@Service +@Slf4j +public class GraphUserService { + + private final GraphServiceClient graphServiceClient; + + private final GraphPageWalkerService graphPageWalkerService; + + public GraphUserService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { + this.graphServiceClient = graphServiceClient; + this.graphPageWalkerService = graphPageWalkerService; + } + + public List getUserInfo(Collection userIds) { + log.info("Retrieving user information for {} user ids", userIds.size()); + List graphUserInfos = + graphPageWalkerService.getContentFromCurrentAndNextPages( + graphServiceClient.users() + .getByIds(DirectoryObjectGetByIdsParameterSet + .newBuilder() + .withIds(userIds.stream().map(UUID::toString).toList()) + .build() + ).buildRequest() + .select("id,mail,displayName"), + pageContent -> pageContent.stream() + .filter(user -> user instanceof User) + .map(user -> (User) user) + .filter(user -> Objects.nonNull(user.id)) + .map(user -> GraphUserInfo + .builder() + .id(UUID.fromString(user.id)) + .displayName(user.displayName) + .mail(user.mail) + .build() + ).toList() + ); + log.info("Successfully retrieved user information for {} users", graphUserInfos.size()); + return graphUserInfos; + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 9a60352..25d81c5 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -1,9 +1,15 @@ package no.fintlabs.flyt.authorization.user.controller; -import no.fintlabs.flyt.authorization.AuthorizationUtil; -import no.fintlabs.flyt.authorization.user.UserService; +import no.fintlabs.flyt.authorization.client.sourceapplications.AcosSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.DigisakSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.EgrunnervervSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.VigoSourceApplication; +import no.fintlabs.flyt.authorization.user.UserAuthorizationComponent; +import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; +import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; +import no.fintlabs.flyt.authorization.user.model.RestrictedPageAuthorization; import no.fintlabs.flyt.authorization.user.model.User; -import no.fintlabs.flyt.authorization.user.permission.RestrictedPageAuthorization; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -12,8 +18,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; +import java.util.List; +import java.util.Optional; import java.util.UUID; import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; @@ -22,15 +29,16 @@ @RestController public class MeController { - private final AuthorizationUtil authorizationUtil; - private final UserService userService; + private final TokenParsingUtils tokenParsingUtils; + + private final UserAuthorizationComponent userAuthorizationComponent; public MeController( - AuthorizationUtil authorizationUtil, - UserService userService + TokenParsingUtils tokenParsingUtils, + @Autowired(required = false) UserAuthorizationComponent userAuthorizationComponent ) { - this.authorizationUtil = authorizationUtil; - this.userService = userService; + this.tokenParsingUtils = tokenParsingUtils; + this.userAuthorizationComponent = userAuthorizationComponent; } @GetMapping("is-authorized") @@ -42,27 +50,56 @@ public ResponseEntity checkAuthorization() { public Mono> getRestrictedPageAuthorization( @AuthenticationPrincipal Mono authenticationMono ) { - return authorizationUtil.isAdmin(authenticationMono) - .map(isAdmin -> ResponseEntity.ok( - RestrictedPageAuthorization - .builder() - .userPermissionPage(isAdmin) - .build()) - ); + return (userAuthorizationComponent == null + ? Mono.just(RestrictedPageAuthorization.builder().build()) + : tokenParsingUtils.isAdmin(authenticationMono) + .map(isAdmin -> RestrictedPageAuthorization + .builder() + .userPermissionPage(isAdmin) + .build() + )) + .map(ResponseEntity::ok); } @GetMapping - public Mono> get( + public Mono> getSourceApplicationIds( @AuthenticationPrincipal Mono authenticationMono ) { return authenticationMono - .map(authentication -> authorizationUtil - .getObjectIdentifierFromToken((JwtAuthenticationToken) authentication)) - .publishOn(Schedulers.boundedElastic()) - .map(objectIdentifierString -> userService.getUser(UUID.fromString(objectIdentifierString)) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()) + .map(authentication -> (JwtAuthenticationToken) authentication) + .map(authentication -> + userAuthorizationComponent == null + ? ResponseEntity.ok(createUserWithFullAccessFromToken(authentication)) + : getUserFromUserAuthorizationComponent(authentication) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()) ); } + private User createUserWithFullAccessFromToken(JwtAuthenticationToken token) { + UserDisplayText userDisplayTextFromToken = + tokenParsingUtils.getUserDisplayTextFromToken(token); + return User.builder() + .objectIdentifier(userDisplayTextFromToken.getObjectIdentifier()) + .name(userDisplayTextFromToken.getName()) + .email(userDisplayTextFromToken.getEmail()) + .sourceApplicationIds(sourceApplicationsWithoutUserPermissionSetup()) + .build(); + } + + private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationToken token) { + return userAuthorizationComponent.getUser( + UUID.fromString(tokenParsingUtils.getObjectIdentifierFromToken(token)) + ); + } + + private List sourceApplicationsWithoutUserPermissionSetup() { + return List.of( + AcosSourceApplication.SOURCE_APPLICATION_ID, + DigisakSourceApplication.SOURCE_APPLICATION_ID, + EgrunnervervSourceApplication.SOURCE_APPLICATION_ID, + VigoSourceApplication.SOURCE_APPLICATION_ID + ); + } + } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index 5d15811..106bad5 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -1,9 +1,10 @@ package no.fintlabs.flyt.authorization.user.controller; -import no.fintlabs.flyt.authorization.AuthorizationUtil; +import no.fintlabs.flyt.authorization.user.UserAuthorizationComponent; +import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; import no.fintlabs.flyt.authorization.user.model.User; -import no.fintlabs.flyt.authorization.user.permission.model.UserPermission; -import no.fintlabs.flyt.authorization.user.UserService; +import no.fintlabs.flyt.authorization.user.model.UserPermission; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -17,31 +18,32 @@ @RestController @RequestMapping(INTERNAL_API + "/authorization/users") +@ConditionalOnBean(UserAuthorizationComponent.class) public class UserController { - private final AuthorizationUtil authorizationUtil; - private final UserService userService; + private final TokenParsingUtils tokenParsingUtils; + private final UserAuthorizationComponent userAuthorizationComponent; public UserController( - AuthorizationUtil authorizationUtil, - UserService userService + TokenParsingUtils tokenParsingUtils, + UserAuthorizationComponent userAuthorizationComponent ) { - this.authorizationUtil = authorizationUtil; - this.userService = userService; + this.tokenParsingUtils = tokenParsingUtils; + this.userAuthorizationComponent = userAuthorizationComponent; } @GetMapping public Mono>> get( @AuthenticationPrincipal Mono authenticationMono ) { - return authorizationUtil.isAdmin(authenticationMono) + return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } // TODO: add pagable with sorting - return Mono.fromCallable(userService::getUsers) + return Mono.fromCallable(userAuthorizationComponent::getUsers) .map(ResponseEntity::ok); }); } @@ -51,14 +53,14 @@ public Mono>> setUserPermissions( @RequestBody List userPermissions, @AuthenticationPrincipal Mono authenticationMono ) { - return authorizationUtil.isAdmin(authenticationMono) + return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } // TODO: add pagable with sorting - return Mono.just(ResponseEntity.ok(userService.putUsers(userPermissions))); + return Mono.just(ResponseEntity.ok(userAuthorizationComponent.putUsers(userPermissions))); }); } } \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java new file mode 100644 index 0000000..0bf2a9a --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java @@ -0,0 +1,33 @@ +package no.fintlabs.flyt.authorization.user.controller.utils; + +import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import java.util.Map; +import java.util.UUID; + +@Service +public class TokenParsingUtils { + public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticationToken) { + return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString(); + } + + public UserDisplayText getUserDisplayTextFromToken(JwtAuthenticationToken jwtAuthenticationToken) { + Map tokenAttributes = jwtAuthenticationToken.getTokenAttributes(); + return UserDisplayText + .builder() + .objectIdentifier(UUID.fromString(tokenAttributes.get("objectidentifier").toString())) + .name(tokenAttributes.get("").toString()) // TODO eivindmorch 28/06/2024 : + .email(tokenAttributes.get("").toString()) // TODO eivindmorch 28/06/2024 : + .build(); + } + + public Mono isAdmin(Mono authenticationMono) { + return authenticationMono + .map(authentication -> authentication != null && authentication.getAuthorities().stream() + .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN"))); + } +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/RestrictedPageAuthorization.java similarity index 72% rename from src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java rename to src/main/java/no/fintlabs/flyt/authorization/user/model/RestrictedPageAuthorization.java index 39e5569..4ca53e2 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/permission/RestrictedPageAuthorization.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/RestrictedPageAuthorization.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user.permission; +package no.fintlabs.flyt.authorization.user.model; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java index 7fc2856..3688934 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java @@ -19,5 +19,5 @@ public class User { private String email; private String name; @NotNull - private List sourceApplicationIds; + private List sourceApplicationIds; } \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java similarity index 75% rename from src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java rename to src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java index 5920529..655f315 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermission.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user.permission.model; +package no.fintlabs.flyt.authorization.user.model; import com.sun.istack.NotNull; import lombok.Builder; @@ -17,5 +17,5 @@ public class UserPermission { @NotNull private UUID objectIdentifier; @NotNull - private List sourceApplicationIds; + private List sourceApplicationIds; } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java similarity index 88% rename from src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java rename to src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java index ac90e83..d812780 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/permission/model/UserPermissionEntity.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java @@ -1,4 +1,4 @@ -package no.fintlabs.flyt.authorization.user.permission.model; +package no.fintlabs.flyt.authorization.user.model; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.*; @@ -36,5 +36,5 @@ public class UserPermissionEntity { @Setter @Column(name = "source_application_ids") @Builder.Default - private List sourceApplicationIds = new ArrayList<>(); + private List sourceApplicationIds = new ArrayList<>(); } diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java deleted file mode 100644 index 4d4ce3b..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/GraphGroupService.java +++ /dev/null @@ -1,58 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import com.microsoft.graph.requests.GraphServiceClient; -import okhttp3.Request; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -@Service -public class GraphGroupService { - - private final GraphServiceClient graphServiceClient; - - public GraphGroupService(GraphServiceClient graphServiceClient) { - this.graphServiceClient = graphServiceClient; - } - - public Set getGroupUserMemberIds(Collection groupIds) { - return groupIds.stream() - .map(this::getGroupUserMemberIds) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); - } - - // TODO eivindmorch 27/06/2024 : Only include of principal type User - public Set getGroupUserMemberIds(UUID groupId) { - return graphServiceClient - .groups(groupId.toString()) - .members() - .buildRequest() - .select("id") - .get().getCurrentPage() // TODO eivindmorch 27/06/2024 : Get all pages - .stream() - .map(directoryObject -> directoryObject.id) - .filter(Objects::nonNull) - .map(UUID::fromString) - .collect(Collectors.toSet()); - } - - - // TODO eivindmorch 27/06/2024 : Move to util -// public List getContentFromCurrentAndNextPages(DirectoryObjectGetByIdsCollectionRequestBuilder baseActionCollectionRequest) { -// DirectoryObjectGetByIdsCollectionPage postResult = baseActionCollectionRequest.buildRequest().post(); -// if (postResult == null) { -// throw new IllegalStateException("Post result is null"); -// } -// ArrayList currentContent = new ArrayList<>(postResult.getCurrentPage()); -// if (postResult.getNextPage() != null) { -// currentContent.addAll(getContentFromCurrentAndNextPages(postResult.getNextPage())); -// } -// -// } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java deleted file mode 100644 index 7dbcb4a..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/GraphService.java +++ /dev/null @@ -1,75 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import no.fintlabs.flyt.azure.configuration.AzureAdGatewayConfiguration; -import no.fintlabs.flyt.azure.models.GraphUserInfo; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Service -public class GraphService { - - private final AzureAdGatewayConfiguration azureAdGatewayConfiguration; - - private final GraphAppRoleService graphAppRoleService; - - private final GraphGroupService graphGroupService; - - private final GraphUserService graphUserService; - - public GraphService( - AzureAdGatewayConfiguration azureAdGatewayConfiguration, - GraphAppRoleService graphAppRoleService, - GraphGroupService graphGroupService, - GraphUserService graphUserService - ) { - this.azureAdGatewayConfiguration = azureAdGatewayConfiguration; - this.graphAppRoleService = graphAppRoleService; - this.graphGroupService = graphGroupService; - this.graphUserService = graphUserService; - } - - public List getPermittedUsersInfo() { -// UUID servicePrincipalId = graphServicePrincipalService.getServicePrincipalId(config.getAppId()); - // TODO eivindmorch 27/06/2024 : Hvorfor bruker vi app id og ikke service principal id direkte? - UUID servicePrincipalId = UUID.fromString(""); // TODO eivindmorch 27/06/2024 : Get from config - - Set permittedAppRoleIds = graphAppRoleService.getAppRoleIdsFromAppRoleValues( - servicePrincipalId, - Set.of(azureAdGatewayConfiguration.getPermittedAppRoles().getFlytUser()) - ); - - Set permittedUserIds = getPermittedUserIds( - servicePrincipalId, - permittedAppRoleIds - ); - - return graphUserService.getUserInfo(permittedUserIds); - } - - private Set getPermittedUserIds(UUID servicePrincipalId, Set permittedAppRoleIds) { - List appRoleAssignments = - graphAppRoleService.getAppRoleAssignments(servicePrincipalId); - - Map> idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType = appRoleAssignments.stream() - .filter(appRoleAssignment -> permittedAppRoleIds.contains(appRoleAssignment.getAppRoleId())) - .collect(Collectors.groupingBy( - GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalType, - Collectors.mapping( - GraphAppRoleService.AppRoleIdAndPrincipalSelection::getPrincipalId, - Collectors.toSet() - ) - )); - - return Stream.concat( - graphGroupService.getGroupUserMemberIds(idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("Group")).stream(), - idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("User").stream() - ).collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java b/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java deleted file mode 100644 index 901dc84..0000000 --- a/src/main/java/no/fintlabs/flyt/azure/services/GraphUserService.java +++ /dev/null @@ -1,50 +0,0 @@ -package no.fintlabs.flyt.azure.services; - -import com.microsoft.graph.models.DirectoryObjectGetByIdsParameterSet; -import com.microsoft.graph.models.User; -import com.microsoft.graph.requests.GraphServiceClient; -import no.fintlabs.flyt.azure.models.GraphUserInfo; -import okhttp3.Request; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -@Service -public class GraphUserService { - - private final GraphServiceClient graphServiceClient; - - public GraphUserService(GraphServiceClient graphServiceClient) { - this.graphServiceClient = graphServiceClient; - } - - // TODO eivindmorch 27/06/2024 : Do pull and cache of display info here - public List getUserInfo(Collection userIds) { - return graphServiceClient.users() - .getByIds(DirectoryObjectGetByIdsParameterSet - .newBuilder() - .withIds(userIds.stream().map(UUID::toString).toList()) - .build() - ).buildRequest() - .select("id,mail,displayName") - .post() -// .setRawObject(new DefaultSerializer(), UserInfo.class) - .getCurrentPage() // TODO eivindmorch 27/06/2024 : handle multi page - .stream() - .filter(user -> user instanceof User) - .map(user -> (User) user) - .filter(user -> Objects.nonNull(user.id)) - .map(user -> GraphUserInfo - .builder() - .id(UUID.fromString(user.id)) - .displayName(user.displayName) - .mail(user.mail) - .build() - ) - .toList(); - } - -} diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index f044f71..869c93a 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -25,7 +25,6 @@ fint: azure-ad-gateway: permitted-approles: flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" - user-scheduler: - pull: - initial-delay-ms: 1000 - fixed-delay-ms: 900000 \ No newline at end of file + sync-schedule: + initial-delay-ms: 1000 + fixed-delay-ms: 900000 \ No newline at end of file diff --git a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java index 0de0e02..9f85300 100644 --- a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java +++ b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java @@ -1,12 +1,15 @@ package no.fintlabs; +import no.fintlabs.flyt.authorization.client.ClientAuthorization; +import no.fintlabs.flyt.authorization.client.ClientAuthorizationProducerRecordBuilder; +import no.fintlabs.flyt.authorization.client.sourceapplications.AcosSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.DigisakSourceApplication; +import no.fintlabs.flyt.authorization.client.sourceapplications.EgrunnervervSourceApplication; import no.fintlabs.kafka.requestreply.ReplyProducerRecord; -import no.fintlabs.flyt.models.sourceapplication.AcosSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.EgrunnervervSourceApplication; -import no.fintlabs.flyt.models.sourceapplication.DigisakSourceApplication; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.*; class ClientAuthorizationProducerRecordBuilderTest { From eeda2b99f5170f674843b66475408333525651d1 Mon Sep 17 00:00:00 2001 From: Eivind Morch Date: Fri, 28 Jun 2024 14:41:13 +0200 Subject: [PATCH 32/50] add dependency to enable property --- ...=> AzureAdUserAuthorizationComponent.java} | 8 +-- .../AzureAdGatewayConfiguration.java | 13 +++- .../GraphServiceConfiguration.java | 59 ++++++++++++++++++- .../PermittedAppRolesProperties.java | 13 ---- .../RequiredPropertiesCondition.java | 22 ------- .../UserDisplayTextCacheConfiguration.java | 2 +- .../azure/services/GraphAppRoleService.java | 2 - .../azure/services/GraphGroupService.java | 2 - .../services/GraphPageWalkerService.java | 2 - .../user/azure/services/GraphService.java | 4 -- .../GraphServicePrincipalService.java | 14 +++-- .../user/azure/services/GraphUserService.java | 2 - .../user/controller/MeController.java | 45 +++++++------- .../user/controller/UserController.java | 16 ++--- .../resources/application-flyt-azure.yaml | 1 + 15 files changed, 115 insertions(+), 90 deletions(-) rename src/main/java/no/fintlabs/flyt/authorization/user/{UserAuthorizationComponent.java => AzureAdUserAuthorizationComponent.java} (95%) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java similarity index 95% rename from src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java rename to src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java index 5c6225f..0db9991 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserAuthorizationComponent.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java @@ -7,7 +7,7 @@ import no.fintlabs.flyt.authorization.user.azure.services.GraphService; import no.fintlabs.flyt.authorization.user.model.User; import no.fintlabs.flyt.authorization.user.model.UserPermission; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -16,10 +16,10 @@ import static java.util.stream.Collectors.toMap; -@ConditionalOnBean(GraphService.class) @Component +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") @Slf4j -public class UserAuthorizationComponent { +public class AzureAdUserAuthorizationComponent { private final GraphService graphService; @@ -27,7 +27,7 @@ public class UserAuthorizationComponent { private final FintCache userDisplayTextCache; - public UserAuthorizationComponent( + public AzureAdUserAuthorizationComponent( GraphService graphService, UserPermissionService userPermissionService, FintCache userDisplayTextCache ) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java index 899dc29..c251780 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java @@ -8,6 +8,8 @@ import org.springframework.validation.annotation.Validated; import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; @Getter @@ -18,8 +20,17 @@ @ConfigurationProperties(prefix = "fint.flyt.azure-ad-gateway") public class AzureAdGatewayConfiguration { - // TODO eivindmorch 28/06/2024 : Split properties? + @NotNull + private Boolean enable; + @Valid private PermittedAppRolesProperties permittedAppRolesProperties; + @Getter + @Setter + public static class PermittedAppRolesProperties { + @NotEmpty + private String flytUser; + } + } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java index 389ac84..c97db05 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java @@ -6,9 +6,10 @@ import com.microsoft.graph.requests.GraphServiceClient; import lombok.Getter; import lombok.Setter; +import no.fintlabs.flyt.authorization.user.azure.services.*; import okhttp3.Request; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.validation.annotation.Validated; @@ -21,11 +22,63 @@ @Validated @EnableAutoConfiguration @Configuration +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") public class GraphServiceConfiguration { @Bean - @ConditionalOnBean(AzureCredentialsConfiguration.class) -// @Conditional(RequiredPropertiesCondition.class) + public GraphService graphService( + AzureCredentialsConfiguration azureCredentialsConfiguration, + AzureAdGatewayConfiguration azureAdGatewayConfiguration, + GraphServicePrincipalService graphServicePrincipalService, + GraphAppRoleService graphAppRoleService, + GraphGroupService graphGroupService, + GraphUserService graphUserService + ) { + return new GraphService( + azureCredentialsConfiguration, + azureAdGatewayConfiguration, + graphServicePrincipalService, + graphAppRoleService, + graphGroupService, + graphUserService + ); + } + + @Bean + public GraphUserService graphUserService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { + return new GraphUserService(graphServiceClient, graphPageWalkerService); + } + + @Bean + public GraphAppRoleService graphAppRoleService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { + return new GraphAppRoleService(graphServiceClient, graphPageWalkerService); + } + + @Bean + public GraphGroupService graphGroupService( + GraphServiceClient graphServiceClient, + GraphPageWalkerService graphPageWalkerService + ) { + return new GraphGroupService(graphServiceClient, graphPageWalkerService); + } + + @Bean + public GraphServicePrincipalService graphServicePrincipalService(GraphServiceClient graphServiceClient) { + return new GraphServicePrincipalService(graphServiceClient); + } + + @Bean + public GraphPageWalkerService graphPageWalkerService() { + return new GraphPageWalkerService(); + } + + @Bean public GraphServiceClient graphServiceClient(AzureCredentialsConfiguration azureCredentialsConfiguration) { ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() .clientId(azureCredentialsConfiguration.getClientId()) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java deleted file mode 100644 index 3bc202f..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/PermittedAppRolesProperties.java +++ /dev/null @@ -1,13 +0,0 @@ -package no.fintlabs.flyt.authorization.user.azure.configuration; - -import lombok.Getter; -import lombok.Setter; - -import javax.validation.constraints.NotEmpty; - -@Getter -@Setter -public class PermittedAppRolesProperties { - @NotEmpty - private String flytUser; -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java deleted file mode 100644 index 825f613..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/RequiredPropertiesCondition.java +++ /dev/null @@ -1,22 +0,0 @@ -package no.fintlabs.flyt.authorization.user.azure.configuration; - -import org.jetbrains.annotations.NotNull; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.util.StringUtils; - -// TODO eivindmorch 27/06/2024 : Kan dette gjøres med bean-avhengighet? -public class RequiredPropertiesCondition implements Condition { - @Override - public boolean matches(ConditionContext context, @NotNull AnnotatedTypeMetadata metadata) { - String clientId = context.getEnvironment().getProperty("azure.credentials.clientid"); - String clientSecret = context.getEnvironment().getProperty("azure.credentials.clientsecret"); - String tenantId = context.getEnvironment().getProperty("azure.credentials.tenantid"); - String appId = context.getEnvironment().getProperty("azure.credentials.appid"); - - return StringUtils.hasText(clientId) && StringUtils.hasText(clientSecret) - && StringUtils.hasText(tenantId) && StringUtils.hasText(appId); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java index 694f726..c0c67c8 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java @@ -17,4 +17,4 @@ public FintCache userDisplayTextCache(FintCacheManager fi return fintCacheManager.createCache("userDisplayText", UUID.class, UserDisplayText.class); } -} \ No newline at end of file +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java index 095c6aa..f0339e7 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java @@ -7,7 +7,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import okhttp3.Request; -import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; @@ -15,7 +14,6 @@ import static java.util.stream.Collectors.toMap; -@Service @Slf4j public class GraphAppRoleService { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java index 2d2e723..07233bc 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java @@ -3,7 +3,6 @@ import com.microsoft.graph.requests.GraphServiceClient; import lombok.extern.slf4j.Slf4j; import okhttp3.Request; -import org.springframework.stereotype.Service; import java.util.Collection; import java.util.Objects; @@ -11,7 +10,6 @@ import java.util.UUID; import java.util.stream.Collectors; -@Service @Slf4j public class GraphGroupService { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java index 79c5aa7..109e61d 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java @@ -2,13 +2,11 @@ import com.microsoft.graph.http.*; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.function.Function; -@Service @Slf4j public class GraphPageWalkerService { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java index 0f4c9d5..e5349bc 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java @@ -4,8 +4,6 @@ import no.fintlabs.flyt.authorization.user.azure.configuration.AzureAdGatewayConfiguration; import no.fintlabs.flyt.authorization.user.azure.configuration.AzureCredentialsConfiguration; import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; @@ -14,9 +12,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -@Service @Slf4j -@ConditionalOnBean(AzureCredentialsConfiguration.class) public class GraphService { private final AzureCredentialsConfiguration azureCredentialsConfiguration; diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java index 2c94313..ec928e8 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java @@ -3,12 +3,12 @@ import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.requests.GraphServiceClient; import com.microsoft.graph.requests.ServicePrincipalCollectionPage; +import lombok.extern.slf4j.Slf4j; import okhttp3.Request; -import org.springframework.stereotype.Service; import java.util.UUID; -@Service +@Slf4j public class GraphServicePrincipalService { // TODO eivindmorch 27/06/2024 : Rename service? private final GraphServiceClient graphServiceClient; @@ -19,6 +19,7 @@ public GraphServicePrincipalService(GraphServiceClient graphServiceClie // TODO eivindmorch 27/06/2024 : Remove? public UUID getServicePrincipalId(UUID appId) { + log.info("Retrieving service principal id for application with id: {}", appId); ServicePrincipalCollectionPage servicePrincipalSearchResult = graphServiceClient .servicePrincipals() .buildRequest(new QueryOption("$filter", "appId eq '" + appId.toString() + "'")) @@ -30,11 +31,14 @@ public UUID getServicePrincipalId(UUID appId) { if (servicePrincipalSearchResult.getCurrentPage().size() > 1 || servicePrincipalSearchResult.getNextPage() != null) { throw new IllegalStateException("Found multiple service principals for application"); } - String servicePrincipalId = servicePrincipalSearchResult.getCurrentPage().get(0).id; - if (servicePrincipalId == null) { + String servicePrincipalIdString = servicePrincipalSearchResult.getCurrentPage().get(0).id; + if (servicePrincipalIdString == null) { throw new IllegalStateException("Service principal id is null"); } - return UUID.fromString(servicePrincipalId); + + UUID servicePrincipalId = UUID.fromString(servicePrincipalIdString); + log.info("Successfully retrieved service principal id: {}", servicePrincipalId); + return servicePrincipalId; } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java index 4e4ca59..c4c41ae 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphUserService.java @@ -6,14 +6,12 @@ import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; import okhttp3.Request; -import org.springframework.stereotype.Service; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.UUID; -@Service @Slf4j public class GraphUserService { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 25d81c5..9c3e819 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -4,12 +4,13 @@ import no.fintlabs.flyt.authorization.client.sourceapplications.DigisakSourceApplication; import no.fintlabs.flyt.authorization.client.sourceapplications.EgrunnervervSourceApplication; import no.fintlabs.flyt.authorization.client.sourceapplications.VigoSourceApplication; -import no.fintlabs.flyt.authorization.user.UserAuthorizationComponent; +import no.fintlabs.flyt.authorization.user.AzureAdUserAuthorizationComponent; import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; import no.fintlabs.flyt.authorization.user.model.RestrictedPageAuthorization; import no.fintlabs.flyt.authorization.user.model.User; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -25,20 +26,23 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; -@RequestMapping(INTERNAL_API + "/authorization/me") @RestController +@RequestMapping(INTERNAL_API + "/authorization/me") public class MeController { private final TokenParsingUtils tokenParsingUtils; - private final UserAuthorizationComponent userAuthorizationComponent; + private final Boolean azureAdUserAuthorizationEnabled; + private final AzureAdUserAuthorizationComponent azureAdUserAuthorizationComponent; public MeController( TokenParsingUtils tokenParsingUtils, - @Autowired(required = false) UserAuthorizationComponent userAuthorizationComponent + @Value("${fint.flyt.azure-ad-gateway.enable}") Boolean azureAdUserAuthorizationEnabled, + @Autowired(required = false) AzureAdUserAuthorizationComponent azureAdUserAuthorizationComponent ) { this.tokenParsingUtils = tokenParsingUtils; - this.userAuthorizationComponent = userAuthorizationComponent; + this.azureAdUserAuthorizationEnabled = azureAdUserAuthorizationEnabled; + this.azureAdUserAuthorizationComponent = azureAdUserAuthorizationComponent; } @GetMapping("is-authorized") @@ -50,32 +54,37 @@ public ResponseEntity checkAuthorization() { public Mono> getRestrictedPageAuthorization( @AuthenticationPrincipal Mono authenticationMono ) { - return (userAuthorizationComponent == null - ? Mono.just(RestrictedPageAuthorization.builder().build()) - : tokenParsingUtils.isAdmin(authenticationMono) + return (azureAdUserAuthorizationEnabled + ? tokenParsingUtils.isAdmin(authenticationMono) .map(isAdmin -> RestrictedPageAuthorization .builder() .userPermissionPage(isAdmin) - .build() - )) - .map(ResponseEntity::ok); + .build()) + : Mono.just(RestrictedPageAuthorization.builder().build()) + ).map(ResponseEntity::ok); } @GetMapping - public Mono> getSourceApplicationIds( + public Mono> get( @AuthenticationPrincipal Mono authenticationMono ) { return authenticationMono .map(authentication -> (JwtAuthenticationToken) authentication) .map(authentication -> - userAuthorizationComponent == null - ? ResponseEntity.ok(createUserWithFullAccessFromToken(authentication)) - : getUserFromUserAuthorizationComponent(authentication) + azureAdUserAuthorizationEnabled + ? getUserFromUserAuthorizationComponent(authentication) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()) + : ResponseEntity.ok(createUserWithFullAccessFromToken(authentication)) ); } + private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationToken token) { + return azureAdUserAuthorizationComponent.getUser( + UUID.fromString(tokenParsingUtils.getObjectIdentifierFromToken(token)) + ); + } + private User createUserWithFullAccessFromToken(JwtAuthenticationToken token) { UserDisplayText userDisplayTextFromToken = tokenParsingUtils.getUserDisplayTextFromToken(token); @@ -87,12 +96,6 @@ private User createUserWithFullAccessFromToken(JwtAuthenticationToken token) { .build(); } - private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationToken token) { - return userAuthorizationComponent.getUser( - UUID.fromString(tokenParsingUtils.getObjectIdentifierFromToken(token)) - ); - } - private List sourceApplicationsWithoutUserPermissionSetup() { return List.of( AcosSourceApplication.SOURCE_APPLICATION_ID, diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index 106bad5..d8b15be 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -1,10 +1,10 @@ package no.fintlabs.flyt.authorization.user.controller; -import no.fintlabs.flyt.authorization.user.UserAuthorizationComponent; +import no.fintlabs.flyt.authorization.user.AzureAdUserAuthorizationComponent; import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; import no.fintlabs.flyt.authorization.user.model.User; import no.fintlabs.flyt.authorization.user.model.UserPermission; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -16,20 +16,20 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") @RestController @RequestMapping(INTERNAL_API + "/authorization/users") -@ConditionalOnBean(UserAuthorizationComponent.class) public class UserController { private final TokenParsingUtils tokenParsingUtils; - private final UserAuthorizationComponent userAuthorizationComponent; + private final AzureAdUserAuthorizationComponent azureAdUserAuthorizationComponent; public UserController( TokenParsingUtils tokenParsingUtils, - UserAuthorizationComponent userAuthorizationComponent + AzureAdUserAuthorizationComponent azureAdUserAuthorizationComponent ) { this.tokenParsingUtils = tokenParsingUtils; - this.userAuthorizationComponent = userAuthorizationComponent; + this.azureAdUserAuthorizationComponent = azureAdUserAuthorizationComponent; } @GetMapping @@ -43,7 +43,7 @@ public Mono>> get( } // TODO: add pagable with sorting - return Mono.fromCallable(userAuthorizationComponent::getUsers) + return Mono.fromCallable(azureAdUserAuthorizationComponent::getUsers) .map(ResponseEntity::ok); }); } @@ -60,7 +60,7 @@ public Mono>> setUserPermissions( } // TODO: add pagable with sorting - return Mono.just(ResponseEntity.ok(userAuthorizationComponent.putUsers(userPermissions))); + return Mono.just(ResponseEntity.ok(azureAdUserAuthorizationComponent.putUsers(userPermissions))); }); } } \ No newline at end of file diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index 869c93a..d1ba513 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -23,6 +23,7 @@ spring: fint: flyt: azure-ad-gateway: + enable: true permitted-approles: flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" sync-schedule: From 6ebaa516a5c6539df018749465798aee8397b455 Mon Sep 17 00:00:00 2001 From: Eivind Morch Date: Fri, 28 Jun 2024 15:09:10 +0200 Subject: [PATCH 33/50] Add pageable and change put to action post --- .../client/ClientAuthorization.java | 2 +- .../AzureAdUserAuthorizationComponent.java | 12 ++++---- .../user/UserPermissionService.java | 10 +++---- .../authorization/user/azure/StringUtils.java | 26 ---------------- .../azure/services/GraphGroupService.java | 5 ++-- .../GraphServicePrincipalService.java | 6 ++-- .../user/controller/MeController.java | 4 +-- .../user/controller/UserController.java | 30 ++++++++++--------- .../resources/application-flyt-azure.yaml | 1 - .../resources/application-local-staging.yaml | 2 ++ 10 files changed, 37 insertions(+), 61 deletions(-) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java diff --git a/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java index 583a780..663d2f8 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java +++ b/src/main/java/no/fintlabs/flyt/authorization/client/ClientAuthorization.java @@ -8,5 +8,5 @@ public class ClientAuthorization { private final boolean authorized; private final String clientId; - private final Long sourceApplicationId; // TODO eivindmorch 28/06/2024 : Endret til long. Fikse andre steder? + private final Long sourceApplicationId; } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java index 0db9991..26156fa 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java @@ -8,6 +8,8 @@ import no.fintlabs.flyt.authorization.user.model.User; import no.fintlabs.flyt.authorization.user.model.UserPermission; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -86,14 +88,12 @@ public Optional getUser(UUID objectIdentifier) { .map(this::mapToUserWithDisplayText); } - public List getUsers() { - return userPermissionService.getAll() - .stream() - .map(this::mapToUserWithDisplayText) - .toList(); + public Page getUsers(Pageable pageable) { + return userPermissionService.getAll(pageable) + .map(this::mapToUserWithDisplayText); } - public List putUsers(List userPermissions) { + public List batchPutUserPermissions(List userPermissions) { return userPermissionService.putAll(userPermissions) .stream() .map(this::mapToUserWithDisplayText) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java index 3adf4c7..78d861a 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java @@ -3,6 +3,8 @@ import lombok.extern.slf4j.Slf4j; import no.fintlabs.flyt.authorization.user.model.UserPermission; import no.fintlabs.flyt.authorization.user.model.UserPermissionEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.*; @@ -51,11 +53,9 @@ public Optional find(UUID objectIdentifier) { .map(this::mapFromEntity); } - public List getAll() { - return this.userPermissionRepository.findAll() - .stream() - .map(this::mapFromEntity) - .toList(); + public Page getAll(Pageable pageable) { + return this.userPermissionRepository.findAll(pageable) + .map(this::mapFromEntity); } public List putAll(List userPermissions) { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java deleted file mode 100644 index d6fb214..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/StringUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package no.fintlabs.flyt.authorization.user.azure; - -import java.util.Locale; - -public class StringUtils { - - // TODO eivindmorch 27/06/2024 : Trengs denne? - public static String capitalizeFirstLetterOfEachWord(String input) { - if (input == null || input.isEmpty()) { - return input; - } - - String[] words = input.split("\\s+"); - StringBuilder capitalizedString = new StringBuilder(); - - for (String word : words) { - if (!word.isEmpty()) { - capitalizedString.append(word.substring(0, 1).toUpperCase(Locale.ROOT)) - .append(word.substring(1).toLowerCase(Locale.ROOT)) - .append(" "); - } - } - - return capitalizedString.toString().trim(); - } -} \ No newline at end of file diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java index 07233bc..b5b7a05 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java @@ -34,17 +34,16 @@ public Set getGroupUserMemberIds(Collection groupIds) { return groupUserMemberIds; } - // TODO eivindmorch 27/06/2024 : Only include of principal type User public Set getGroupUserMemberIds(UUID groupId) { log.info("Retrieving group member ids for group with id: {}", groupId); Set groupMemberIds = graphPageWalkerService.getContentFromCurrentAndNextPages( graphServiceClient .groups(groupId.toString()) - .members() + .membersAsUser() .buildRequest() .select("id"), pageContent -> pageContent.stream() - .map(directoryObject -> directoryObject.id) + .map(user -> user.id) .filter(Objects::nonNull) .toList() ) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java index ec928e8..c5d7695 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphServicePrincipalService.java @@ -10,20 +10,20 @@ @Slf4j public class GraphServicePrincipalService { - // TODO eivindmorch 27/06/2024 : Rename service? + private final GraphServiceClient graphServiceClient; public GraphServicePrincipalService(GraphServiceClient graphServiceClient) { this.graphServiceClient = graphServiceClient; } - // TODO eivindmorch 27/06/2024 : Remove? public UUID getServicePrincipalId(UUID appId) { log.info("Retrieving service principal id for application with id: {}", appId); ServicePrincipalCollectionPage servicePrincipalSearchResult = graphServiceClient .servicePrincipals() .buildRequest(new QueryOption("$filter", "appId eq '" + appId.toString() + "'")) - .get();// TODO eivindmorch 27/06/2024 : Select only needed data + .select("id") + .get(); if (servicePrincipalSearchResult == null || servicePrincipalSearchResult.getCurrentPage().isEmpty()) { throw new IllegalStateException("No principal for application found"); diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 9c3e819..1acc929 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -75,7 +75,7 @@ public Mono> get( ? getUserFromUserAuthorizationComponent(authentication) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()) - : ResponseEntity.ok(createUserWithFullAccessFromToken(authentication)) + : ResponseEntity.ok(createUserWithAccessToAllApplications(authentication)) ); } @@ -85,7 +85,7 @@ private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationTo ); } - private User createUserWithFullAccessFromToken(JwtAuthenticationToken token) { + private User createUserWithAccessToAllApplications(JwtAuthenticationToken token) { UserDisplayText userDisplayTextFromToken = tokenParsingUtils.getUserDisplayTextFromToken(token); return User.builder() diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index d8b15be..c0d44b7 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -5,6 +5,8 @@ import no.fintlabs.flyt.authorization.user.model.User; import no.fintlabs.flyt.authorization.user.model.UserPermission; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -33,34 +35,34 @@ public UserController( } @GetMapping - public Mono>> get( - @AuthenticationPrincipal Mono authenticationMono + public Mono>> get( + @AuthenticationPrincipal Mono authenticationMono, + @RequestParam Pageable pageable ) { return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } - - // TODO: add pagable with sorting - return Mono.fromCallable(azureAdUserAuthorizationComponent::getUsers) - .map(ResponseEntity::ok); + return Mono.fromCallable( + () -> azureAdUserAuthorizationComponent.getUsers(pageable) + ).map(ResponseEntity::ok); }); } - @PutMapping - public Mono>> setUserPermissions( - @RequestBody List userPermissions, - @AuthenticationPrincipal Mono authenticationMono + @PostMapping("actions/userPermissionBatchPut") + public Mono> postUserPermissionBatchPutAction( + @AuthenticationPrincipal Mono authenticationMono, + @RequestBody List userPermissions ) { return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } - - // TODO: add pagable with sorting - return Mono.just(ResponseEntity.ok(azureAdUserAuthorizationComponent.putUsers(userPermissions))); + azureAdUserAuthorizationComponent.batchPutUserPermissions(userPermissions); + return Mono.just(ResponseEntity.ok().build()); }); } -} \ No newline at end of file + +} diff --git a/src/main/resources/application-flyt-azure.yaml b/src/main/resources/application-flyt-azure.yaml index d1ba513..869c93a 100644 --- a/src/main/resources/application-flyt-azure.yaml +++ b/src/main/resources/application-flyt-azure.yaml @@ -23,7 +23,6 @@ spring: fint: flyt: azure-ad-gateway: - enable: true permitted-approles: flyt-user: "https://role-catalog.vigoiks.no/vigo/flyt/user" sync-schedule: diff --git a/src/main/resources/application-local-staging.yaml b/src/main/resources/application-local-staging.yaml index 3e5753d..76554ee 100644 --- a/src/main/resources/application-local-staging.yaml +++ b/src/main/resources/application-local-staging.yaml @@ -1,6 +1,8 @@ fint: org-id: fintlabs.no flyt: + azure-ad-gateway: + enable: true resource-server: security: api: From 00731b3ba4364f04a4dcd55bd7070aa4122ce6dc Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Fri, 28 Jun 2024 15:31:53 +0200 Subject: [PATCH 34/50] wip commit --- .../AzureAdGatewayConfiguration.java | 2 +- .../azure/services/GraphAppRoleService.java | 1 + .../user/azure/services/GraphService.java | 6 +++--- src/main/resources/db/migration/V1__init.sql | 18 +++++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java index c251780..04d6cf1 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java @@ -24,7 +24,7 @@ public class AzureAdGatewayConfiguration { private Boolean enable; @Valid - private PermittedAppRolesProperties permittedAppRolesProperties; + private PermittedAppRolesProperties permittedAppRoles; @Getter @Setter diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java index f0339e7..4cd533d 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java @@ -46,6 +46,7 @@ public Set getAppRoleIdsFromAppRoleValues(UUID servicePrincipalId, Set appRoleIdPerValue = servicePrincipal.appRoles .stream() .filter(appRole -> Objects.nonNull(appRole.id)) + .filter(appRole -> Objects.nonNull(appRole.value)) .filter(appRole -> appRoleValues.contains(appRole.value)) .collect(toMap( appRole -> appRole.value, diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java index e5349bc..ff84080 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java @@ -54,7 +54,7 @@ public List getPermittedUsersInfo() { Set permittedAppRoleIds = graphAppRoleService.getAppRoleIdsFromAppRoleValues( servicePrincipalId, - Set.of(azureAdGatewayConfiguration.getPermittedAppRolesProperties().getFlytUser()) + Set.of(azureAdGatewayConfiguration.getPermittedAppRoles().getFlytUser()) ); Set permittedUserIds = getPermittedUserIds( @@ -83,9 +83,9 @@ private Set getPermittedUserIds(UUID servicePrincipalId, Set permitt return Stream.concat( graphGroupService.getGroupUserMemberIds( - idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("Group") + idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.getOrDefault("Group", Set.of()) ).stream(), - idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.get("User").stream() + idsOfPrincipalsWithPermittedAppRoleAssignmentsPerPrincipalType.getOrDefault("User", Set.of()).stream() ).collect(Collectors.toSet()); } diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index ed6d580..7f81a40 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -1,17 +1,17 @@ -create table user_permission_source_application_ids -( - user_permission_id int8 not null, - source_application_ids int4 -); create table user_permission ( - id bigserial not null, - object_identifier varchar(255) not null, + id bigserial not null, + object_identifier uuid not null, primary key (id) ); -alter table user_permission_source_application_ids - add constraint UK5w20jnmqeej62rhqsgyjahown unique (user_permission_id, source_application_ids); +create table user_permission_source_application_ids +( + user_permission_id int8 not null, + source_application_ids int8 +); alter table user_permission add constraint UK_3j9pvu47o30sbeqolxqgjsnj unique (object_identifier); +alter table user_permission_source_application_ids + add constraint UK5w20jnmqeej62rhqsgyjahown unique (user_permission_id, source_application_ids); alter table user_permission_source_application_ids add constraint FKjpup6xop1xa25bcs8333uhsm8 foreign key (user_permission_id) references user_permission; From a1a3ce8e4de659d9f1c9c811a6facd3fdfb42bf8 Mon Sep 17 00:00:00 2001 From: Eivind Morch Date: Fri, 28 Jun 2024 16:14:39 +0200 Subject: [PATCH 35/50] change to store display info in db --- build.gradle | 2 - .../AzureAdUserAuthorizationComponent.java | 90 ++++---------- .../user/UserPermissionRepository.java | 25 ---- .../user/UserPermissionService.java | 92 -------------- .../authorization/user/UserRepository.java | 23 ++++ .../flyt/authorization/user/UserService.java | 117 ++++++++++++++++++ .../UserDisplayTextCacheConfiguration.java | 20 --- .../user/azure/models/UserDisplayText.java | 14 --- .../user/controller/MeController.java | 9 +- .../user/controller/UserController.java | 5 +- .../controller/utils/TokenParsingUtils.java | 10 +- .../flyt/authorization/user/model/User.java | 7 +- ...rPermissionEntity.java => UserEntity.java} | 10 +- .../user/model/UserPermission.java | 21 ---- src/main/resources/application.yaml | 3 - 15 files changed, 184 insertions(+), 264 deletions(-) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java create mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/UserService.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java rename src/main/java/no/fintlabs/flyt/authorization/user/model/{UserPermissionEntity.java => UserEntity.java} (89%) delete mode 100644 src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java diff --git a/build.gradle b/build.gradle index 7370a20..7330a5c 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,6 @@ dependencies { implementation 'no.fintlabs:fint-kafka:4.0.1' implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0' - implementation 'no.fintlabs:fint-flyt-cache:1.2.3' - implementation 'javax.validation:validation-api' implementation 'org.hibernate.validator:hibernate-validator' diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java index 26156fa..5881c20 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java @@ -1,22 +1,19 @@ package no.fintlabs.flyt.authorization.user; import lombok.extern.slf4j.Slf4j; -import no.fintlabs.cache.FintCache; import no.fintlabs.flyt.authorization.user.azure.models.GraphUserInfo; -import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; import no.fintlabs.flyt.authorization.user.azure.services.GraphService; import no.fintlabs.flyt.authorization.user.model.User; -import no.fintlabs.flyt.authorization.user.model.UserPermission; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import java.util.*; -import java.util.stream.Collectors; - -import static java.util.stream.Collectors.toMap; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.UUID; @Component @ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") @@ -25,17 +22,12 @@ public class AzureAdUserAuthorizationComponent { private final GraphService graphService; - private final UserPermissionService userPermissionService; + private final UserService userService; - private final FintCache userDisplayTextCache; - public AzureAdUserAuthorizationComponent( - GraphService graphService, UserPermissionService userPermissionService, - FintCache userDisplayTextCache - ) { + public AzureAdUserAuthorizationComponent(GraphService graphService, UserService userService) { this.graphService = graphService; - this.userPermissionService = userPermissionService; - this.userDisplayTextCache = userDisplayTextCache; + this.userService = userService; } @Scheduled( @@ -51,67 +43,29 @@ public void syncUsers() { private void updateUsers(Collection permittedUsersGraphUserInfo) { log.info("Updating users based on {} permitted user info", permittedUsersGraphUserInfo.size()); - Set objectIdentifiers = permittedUsersGraphUserInfo.stream() - .map(GraphUserInfo::getId) - .collect(Collectors.toSet()); - - userPermissionService.updateUserPermissions(objectIdentifiers); - - Set objectIdentifiersAlreadyCachedButNotInNewUserInfo = userDisplayTextCache.getAll() - .stream() - .map(UserDisplayText::getObjectIdentifier) - .filter(objectIdentifier -> !objectIdentifiers.contains(objectIdentifier)) - .collect(Collectors.toSet()); - - log.info("Removing {} user display text with objectIdentifiers: {}", - objectIdentifiersAlreadyCachedButNotInNewUserInfo.size(), - objectIdentifiersAlreadyCachedButNotInNewUserInfo - ); - userDisplayTextCache.remove(objectIdentifiersAlreadyCachedButNotInNewUserInfo); - log.info("Putting {} user display text", permittedUsersGraphUserInfo); - userDisplayTextCache.put(permittedUsersGraphUserInfo - .stream() - .collect(toMap( - GraphUserInfo::getId, - graphUserInfo -> UserDisplayText - .builder() - .objectIdentifier(graphUserInfo.getId()) - .name(graphUserInfo.getDisplayName()) - .email(graphUserInfo.getMail()) - .build() - )) - ); + List usersToUpdate = permittedUsersGraphUserInfo.stream() + .map(graphUserInfo -> User + .builder() + .objectIdentifier(graphUserInfo.getId()) + .name(graphUserInfo.getDisplayName()) + .email(graphUserInfo.getMail()) + .build() + ).toList(); + + userService.updateUsers(usersToUpdate); + log.info("Successfully updated users"); } public Optional getUser(UUID objectIdentifier) { - return userPermissionService.find(objectIdentifier) - .map(this::mapToUserWithDisplayText); + return userService.find(objectIdentifier); } public Page getUsers(Pageable pageable) { - return userPermissionService.getAll(pageable) - .map(this::mapToUserWithDisplayText); + return userService.getAll(pageable); } - public List batchPutUserPermissions(List userPermissions) { - return userPermissionService.putAll(userPermissions) - .stream() - .map(this::mapToUserWithDisplayText) - .toList(); - } - - private User mapToUserWithDisplayText(UserPermission userPermission) { - User.UserBuilder userBuilder = User - .builder() - .objectIdentifier(userPermission.getObjectIdentifier()) - .sourceApplicationIds(userPermission.getSourceApplicationIds()); - - userDisplayTextCache.getOptional(userPermission.getObjectIdentifier()) - .ifPresent(userDisplayText -> userBuilder - .email(userDisplayText.getEmail()) - .name(userDisplayText.getName())); - - return userBuilder.build(); + public void batchPutUserPermissions(List users) { + userService.putAll(users); } } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java deleted file mode 100644 index 137802d..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -package no.fintlabs.flyt.authorization.user; - -import no.fintlabs.flyt.authorization.user.model.UserPermissionEntity; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.*; - - -@Repository -public interface UserPermissionRepository extends JpaRepository { - - Optional findByObjectIdentifier(UUID sub); - - List findAllByObjectIdentifierIn(Collection objectIdentifiers); - - interface ObjectIdentifierSelection { - UUID getObjectIdentifier(); - } - - List findObjectIdentifiersByObjectIdentifierIn(Collection objectIdentifiers); - - void deleteByObjectIdentifierNotIn(Set objectIdentifiers); - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java deleted file mode 100644 index 78d861a..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserPermissionService.java +++ /dev/null @@ -1,92 +0,0 @@ -package no.fintlabs.flyt.authorization.user; - -import lombok.extern.slf4j.Slf4j; -import no.fintlabs.flyt.authorization.user.model.UserPermission; -import no.fintlabs.flyt.authorization.user.model.UserPermissionEntity; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -import static java.util.stream.Collectors.toMap; - -@Service -@Slf4j -public class UserPermissionService { - - // TODO eivindmorch 27/06/2024 : Azure sync og tjeneste som leverer til frontend burde være separate - // ettersom horisontal skalering av synkingen vil skape problemer - - private final UserPermissionRepository userPermissionRepository; - - public UserPermissionService(UserPermissionRepository userPermissionRepository) { - this.userPermissionRepository = userPermissionRepository; - } - - public void updateUserPermissions(Set permittedUsersObjectIdentifiers) { - log.info("Updating user permissions"); - - userPermissionRepository.deleteByObjectIdentifierNotIn(permittedUsersObjectIdentifiers); // TODO eivindmorch 27/06/2024 : Soft delete? - - Set objectIdentifiersAlreadyPersisted = - userPermissionRepository.findObjectIdentifiersByObjectIdentifierIn(permittedUsersObjectIdentifiers) - .stream() - .map(UserPermissionRepository.ObjectIdentifierSelection::getObjectIdentifier) - .collect(Collectors.toSet()); - Set objectIdentifiersNotAlreadyPersisted = new HashSet<>(permittedUsersObjectIdentifiers); - objectIdentifiersNotAlreadyPersisted.removeAll(objectIdentifiersAlreadyPersisted); - userPermissionRepository.saveAll( - objectIdentifiersNotAlreadyPersisted.stream() - .map(objectIdentifier -> UserPermissionEntity - .builder() - .objectIdentifier(objectIdentifier) - .build()) - .toList() - ); - log.info("Successfully updated user permissions"); - } - - public Optional find(UUID objectIdentifier) { - return this.userPermissionRepository.findByObjectIdentifier(objectIdentifier) - .map(this::mapFromEntity); - } - - public Page getAll(Pageable pageable) { - return this.userPermissionRepository.findAll(pageable) - .map(this::mapFromEntity); - } - - public List putAll(List userPermissions) { - Map> sourceApplicationIdsPerObjectIdentifier = userPermissions.stream() - .collect(toMap( - UserPermission::getObjectIdentifier, - UserPermission::getSourceApplicationIds - )); - - List entities = userPermissionRepository.findAllByObjectIdentifierIn( - userPermissions - .stream() - .map(UserPermission::getObjectIdentifier) - .toList() - ); - - entities.forEach(entity -> entity.setSourceApplicationIds( - sourceApplicationIdsPerObjectIdentifier.get(entity.getObjectIdentifier()) - )); - - return userPermissionRepository.saveAll(entities).stream() - .map(this::mapFromEntity) - .collect(Collectors.toList()); - } - - private UserPermission mapFromEntity(UserPermissionEntity userPermissionEntity) { - return UserPermission - .builder() - .objectIdentifier(userPermissionEntity.getObjectIdentifier()) - .sourceApplicationIds(userPermissionEntity.getSourceApplicationIds()) - .build(); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java new file mode 100644 index 0000000..ed006c2 --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java @@ -0,0 +1,23 @@ +package no.fintlabs.flyt.authorization.user; + +import no.fintlabs.flyt.authorization.user.model.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.*; + + +@Repository +public interface UserRepository extends JpaRepository { + + Optional findByObjectIdentifier(UUID sub); + + List findAllByObjectIdentifierIn(Collection objectIdentifiers); + + interface ObjectIdentifierSelection { + UUID getObjectIdentifier(); + } + + void deleteByObjectIdentifierNotIn(Set objectIdentifiers); + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java new file mode 100644 index 0000000..dfec93b --- /dev/null +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java @@ -0,0 +1,117 @@ +package no.fintlabs.flyt.authorization.user; + +import lombok.extern.slf4j.Slf4j; +import no.fintlabs.flyt.authorization.user.model.User; +import no.fintlabs.flyt.authorization.user.model.UserEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toMap; + +@Service +@Slf4j +public class UserService { + + // TODO eivindmorch 27/06/2024 : Azure sync og tjeneste som leverer til frontend burde være separate + // ettersom horisontal skalering av synkingen vil skape problemer + + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public void updateUsers(Collection users) { + log.info("Updating {} user entities", users.size()); + + Map usersToUpdatePerObjectIdentifier = users.stream() + .collect(toMap( + User::getObjectIdentifier, + Function.identity() + )); + + userRepository.deleteByObjectIdentifierNotIn(usersToUpdatePerObjectIdentifier.keySet()); + + Map updatedUserEntitiesPerObjectIdentifier = + userRepository.findAllByObjectIdentifierIn(usersToUpdatePerObjectIdentifier.keySet()).stream() + .peek(userEntity -> { + User newUser = usersToUpdatePerObjectIdentifier.get(userEntity.getObjectIdentifier()); + userEntity.setName(newUser.getName()); + userEntity.setEmail(newUser.getEmail()); + }) + .collect(toMap( + UserEntity::getObjectIdentifier, + Function.identity() + )); + + Set objectIdentifiersForUsersNotExisting = usersToUpdatePerObjectIdentifier.keySet(); + objectIdentifiersForUsersNotExisting.removeAll(updatedUserEntitiesPerObjectIdentifier.keySet()); + + List newUserEntities = objectIdentifiersForUsersNotExisting.stream() + .map(usersToUpdatePerObjectIdentifier::get) + .map(user -> UserEntity + .builder() + .objectIdentifier(user.getObjectIdentifier()) + .name(user.getName()) + .email(user.getEmail()) + .build() + ) + .toList(); + + userRepository.saveAll( + Stream.concat( + updatedUserEntitiesPerObjectIdentifier.values().stream(), + newUserEntities.stream() + ).toList() + ); + + log.info("Successfully updated user entities"); + } + + public Optional find(UUID objectIdentifier) { + return this.userRepository.findByObjectIdentifier(objectIdentifier) + .map(this::mapFromEntity); + } + + public Page getAll(Pageable pageable) { + return this.userRepository.findAll(pageable) + .map(this::mapFromEntity); + } + + public void putAll(List users) { + Map> sourceApplicationIdsPerObjectIdentifier = users.stream() + .collect(toMap( + User::getObjectIdentifier, + User::getSourceApplicationIds + )); + + List entities = userRepository.findAllByObjectIdentifierIn( + users + .stream() + .map(User::getObjectIdentifier) + .toList() + ); + + entities.forEach(entity -> entity.setSourceApplicationIds( + sourceApplicationIdsPerObjectIdentifier.get(entity.getObjectIdentifier()) + )); + + userRepository.saveAll(entities); + } + + private User mapFromEntity(UserEntity userEntity) { + return User + .builder() + .objectIdentifier(userEntity.getObjectIdentifier()) + .name(userEntity.getName()) + .email(userEntity.getEmail()) + .sourceApplicationIds(userEntity.getSourceApplicationIds()) + .build(); + } + +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java deleted file mode 100644 index c0c67c8..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/UserDisplayTextCacheConfiguration.java +++ /dev/null @@ -1,20 +0,0 @@ -package no.fintlabs.flyt.authorization.user.azure.configuration; - -import no.fintlabs.cache.FintCache; -import no.fintlabs.cache.FintCacheManager; -import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.UUID; - - -@Configuration -public class UserDisplayTextCacheConfiguration { - - @Bean - public FintCache userDisplayTextCache(FintCacheManager fintCacheManager) { - return fintCacheManager.createCache("userDisplayText", UUID.class, UserDisplayText.class); - } - -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java deleted file mode 100644 index 836755e..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/models/UserDisplayText.java +++ /dev/null @@ -1,14 +0,0 @@ -package no.fintlabs.flyt.authorization.user.azure.models; - -import lombok.Builder; -import lombok.Getter; - -import java.util.UUID; - -@Getter -@Builder -public class UserDisplayText { - private UUID objectIdentifier; - private String email; - private String name; -} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 1acc929..44dd30e 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -5,7 +5,6 @@ import no.fintlabs.flyt.authorization.client.sourceapplications.EgrunnervervSourceApplication; import no.fintlabs.flyt.authorization.client.sourceapplications.VigoSourceApplication; import no.fintlabs.flyt.authorization.user.AzureAdUserAuthorizationComponent; -import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; import no.fintlabs.flyt.authorization.user.model.RestrictedPageAuthorization; import no.fintlabs.flyt.authorization.user.model.User; @@ -86,12 +85,8 @@ private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationTo } private User createUserWithAccessToAllApplications(JwtAuthenticationToken token) { - UserDisplayText userDisplayTextFromToken = - tokenParsingUtils.getUserDisplayTextFromToken(token); - return User.builder() - .objectIdentifier(userDisplayTextFromToken.getObjectIdentifier()) - .name(userDisplayTextFromToken.getName()) - .email(userDisplayTextFromToken.getEmail()) + return tokenParsingUtils.getUserFromToken(token) + .toBuilder() .sourceApplicationIds(sourceApplicationsWithoutUserPermissionSetup()) .build(); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index c0d44b7..208f462 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -3,7 +3,6 @@ import no.fintlabs.flyt.authorization.user.AzureAdUserAuthorizationComponent; import no.fintlabs.flyt.authorization.user.controller.utils.TokenParsingUtils; import no.fintlabs.flyt.authorization.user.model.User; -import no.fintlabs.flyt.authorization.user.model.UserPermission; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -53,14 +52,14 @@ public Mono>> get( @PostMapping("actions/userPermissionBatchPut") public Mono> postUserPermissionBatchPutAction( @AuthenticationPrincipal Mono authenticationMono, - @RequestBody List userPermissions + @RequestBody List users ) { return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); } - azureAdUserAuthorizationComponent.batchPutUserPermissions(userPermissions); + azureAdUserAuthorizationComponent.batchPutUserPermissions(users); return Mono.just(ResponseEntity.ok().build()); }); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java index 0bf2a9a..55d02af 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/utils/TokenParsingUtils.java @@ -1,6 +1,6 @@ package no.fintlabs.flyt.authorization.user.controller.utils; -import no.fintlabs.flyt.authorization.user.azure.models.UserDisplayText; +import no.fintlabs.flyt.authorization.user.model.User; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.stereotype.Service; @@ -15,13 +15,13 @@ public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticat return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString(); } - public UserDisplayText getUserDisplayTextFromToken(JwtAuthenticationToken jwtAuthenticationToken) { + public User getUserFromToken(JwtAuthenticationToken jwtAuthenticationToken) { Map tokenAttributes = jwtAuthenticationToken.getTokenAttributes(); - return UserDisplayText + return User .builder() .objectIdentifier(UUID.fromString(tokenAttributes.get("objectidentifier").toString())) - .name(tokenAttributes.get("").toString()) // TODO eivindmorch 28/06/2024 : - .email(tokenAttributes.get("").toString()) // TODO eivindmorch 28/06/2024 : + .name(tokenAttributes.get("displayname").toString()) + .email(tokenAttributes.get("email").toString()) .build(); } diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java index 3688934..6ec53b2 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java @@ -10,14 +10,17 @@ import java.util.UUID; @Getter -@Builder +@Builder(toBuilder = true) @EqualsAndHashCode @Jacksonized public class User { @NotNull private UUID objectIdentifier; + private String email; + private String name; + @NotNull private List sourceApplicationIds; -} \ No newline at end of file +} diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java similarity index 89% rename from src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java rename to src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java index d812780..86c4037 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermissionEntity.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java @@ -14,8 +14,8 @@ @Entity @NoArgsConstructor @AllArgsConstructor -@Table(name = "user_permission") -public class UserPermissionEntity { +@Table(name = "user") +public class UserEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @JsonIgnore @@ -27,6 +27,12 @@ public class UserPermissionEntity { @Column(nullable = false, unique = true) private UUID objectIdentifier; + @Setter + private String email; + + @Setter + private String name; + @ElementCollection @CollectionTable( name = "user_permission_source_application_ids", diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java deleted file mode 100644 index 655f315..0000000 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserPermission.java +++ /dev/null @@ -1,21 +0,0 @@ -package no.fintlabs.flyt.authorization.user.model; - -import com.sun.istack.NotNull; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.jackson.Jacksonized; - -import java.util.List; -import java.util.UUID; - -@Getter -@Builder -@EqualsAndHashCode -@Jacksonized -public class UserPermission { - @NotNull - private UUID objectIdentifier; - @NotNull - private List sourceApplicationIds; -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a8f7765..1d1f680 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,8 +1,5 @@ fint: application-id: fint-flyt-authorization-service - cache: - defaultCacheEntryTimeToLiveMillis: 600000 - defaultCacheHeapSize: 1000000 spring: profiles: include: From 5661833d3cef3055e097289c4d7adf1af5394e4e Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:41:03 +0200 Subject: [PATCH 36/50] rename sql table since 'user' is a reserved keyword --- .../authorization/user/model/UserEntity.java | 7 +++-- src/main/resources/db/migration/V1__init.sql | 26 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java index 86c4037..63a1986 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/UserEntity.java @@ -14,7 +14,6 @@ @Entity @NoArgsConstructor @AllArgsConstructor -@Table(name = "user") public class UserEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -35,9 +34,9 @@ public class UserEntity { @ElementCollection @CollectionTable( - name = "user_permission_source_application_ids", - joinColumns = @JoinColumn(name = "user_permission_id"), - uniqueConstraints = @UniqueConstraint(columnNames = {"user_permission_id", "source_application_ids"}) + name = "user_entity_source_application_ids", + joinColumns = @JoinColumn(name = "user_entity_id"), + uniqueConstraints = @UniqueConstraint(columnNames = {"user_entity_id", "source_application_ids"}) ) @Setter @Column(name = "source_application_ids") diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index 7f81a40..ec97888 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -1,17 +1,19 @@ -create table user_permission +create table user_entity_source_application_ids +( + user_entity_id int8 not null, + source_application_ids int8 +); +create table user_entity ( id bigserial not null, + email varchar(255), + name varchar(255), object_identifier uuid not null, primary key (id) ); -create table user_permission_source_application_ids -( - user_permission_id int8 not null, - source_application_ids int8 -); -alter table user_permission - add constraint UK_3j9pvu47o30sbeqolxqgjsnj unique (object_identifier); -alter table user_permission_source_application_ids - add constraint UK5w20jnmqeej62rhqsgyjahown unique (user_permission_id, source_application_ids); -alter table user_permission_source_application_ids - add constraint FKjpup6xop1xa25bcs8333uhsm8 foreign key (user_permission_id) references user_permission; +alter table user_entity_source_application_ids + add constraint UKo3tx39i7fw3q0sr3f0rjp679f unique (user_entity_id, source_application_ids); +alter table user_entity + add constraint UK_td2dvdf4t2le4cydfk7a1x17i unique (object_identifier); +alter table user_entity_source_application_ids + add constraint FKisf0n8x0vsfohl0wkenll537s foreign key (user_entity_id) references user_entity; From 424517ada87b0d27ac29fa7a7edaa5acb7e2b8b0 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:42:02 +0200 Subject: [PATCH 37/50] add check for credentials to prevent schedueler from running if they are missing --- .../user/AzureAdUserAuthorizationComponent.java | 12 +++++++++--- .../user/azure/services/GraphService.java | 9 +++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java index 5881c20..05a6415 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java @@ -21,11 +21,12 @@ public class AzureAdUserAuthorizationComponent { private final GraphService graphService; - private final UserService userService; - - public AzureAdUserAuthorizationComponent(GraphService graphService, UserService userService) { + public AzureAdUserAuthorizationComponent( + GraphService graphService, + UserService userService + ) { this.graphService = graphService; this.userService = userService; } @@ -35,6 +36,11 @@ public AzureAdUserAuthorizationComponent(GraphService graphService, UserService fixedDelayString = "${fint.flyt.azure-ad-gateway.sync-schedule.fixed-delay-ms}" ) public void syncUsers() { + if (!graphService.areCredentialsAvailable()) { + log.warn("Skipping syncUsers because credentials are not available."); + return; + } + log.info("Syncing users"); updateUsers(graphService.getPermittedUsersInfo()); log.info("Successfully synced users"); diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java index ff84080..116f9cc 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphService.java @@ -43,6 +43,15 @@ public GraphService( this.graphUserService = graphUserService; } + public boolean areCredentialsAvailable() { + String appId = azureCredentialsConfiguration.getAppId(); + if (appId == null || appId.isBlank() || "null".equals(appId)) { + log.error("AppId cannot be null, blank, or the string 'null'"); + return false; + } + return true; + } + public List getPermittedUsersInfo() { log.info("Retrieving permitted users info"); From e267070d31aeafd25b89311243001e2a33fba29b Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:42:42 +0200 Subject: [PATCH 38/50] remove logging of appRoleAssignments --- .../authorization/user/azure/services/GraphAppRoleService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java index 4cd533d..5d162f1 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphAppRoleService.java @@ -89,7 +89,7 @@ public List getAppRoleAssignmentsWithAppRoleIds( .stream() .map(this::fromAppRoleAssignment) .toList(); - log.info("Successfully retrieved {} app role assignments: {}", appRoleAssignments.size(), appRoleAssignments); + log.info("Successfully retrieved {} app role assignments", appRoleAssignments.size()); return appRoleAssignments; } From 16d12b14bc43f856465b0e2a1a44c3475621de53 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:44:12 +0200 Subject: [PATCH 39/50] diversify almost identical loglines --- .../user/azure/services/GraphGroupService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java index b5b7a05..d940743 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphGroupService.java @@ -25,17 +25,17 @@ public GraphGroupService( } public Set getGroupUserMemberIds(Collection groupIds) { - log.info("Retrieving group member ids for groups with ids: {}", groupIds); + log.info("Retrieving member IDs for groups with the following IDs: {}", groupIds); Set groupUserMemberIds = groupIds.stream() .map(this::getGroupUserMemberIds) .flatMap(Collection::stream) .collect(Collectors.toSet()); - log.info("Successfully retrieved {} group members' ids", groupUserMemberIds.size()); + log.info("Successfully retrieved member IDs for {} groups, totaling {} member IDs", groupIds.size(), groupUserMemberIds.size()); return groupUserMemberIds; } public Set getGroupUserMemberIds(UUID groupId) { - log.info("Retrieving group member ids for group with id: {}", groupId); + log.info("Retrieving member IDs for group with ID: {}", groupId); Set groupMemberIds = graphPageWalkerService.getContentFromCurrentAndNextPages( graphServiceClient .groups(groupId.toString()) @@ -50,7 +50,7 @@ public Set getGroupUserMemberIds(UUID groupId) { .stream() .map(UUID::fromString) .collect(Collectors.toSet()); - log.info("Successfully retrieved {} group member ids", groupMemberIds.size()); + log.info("Successfully retrieved {} member IDs for group with ID: {}", groupMemberIds.size(), groupId); return groupMemberIds; } From cc5bf8108daf96f7fa5f128b3881eb0bee7a26d8 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:44:35 +0200 Subject: [PATCH 40/50] remove unused repository method --- .../no/fintlabs/flyt/authorization/user/UserRepository.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java index ed006c2..8d83523 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserRepository.java @@ -14,10 +14,6 @@ public interface UserRepository extends JpaRepository { List findAllByObjectIdentifierIn(Collection objectIdentifiers); - interface ObjectIdentifierSelection { - UUID getObjectIdentifier(); - } - void deleteByObjectIdentifierNotIn(Set objectIdentifiers); } From e992392f3fbab0a0bb21565f9a7a3be1d6d47639 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:45:32 +0200 Subject: [PATCH 41/50] add default builder to list in model --- .../java/no/fintlabs/flyt/authorization/user/model/User.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java index 6ec53b2..a477dc4 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/model/User.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.extern.jackson.Jacksonized; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -22,5 +23,6 @@ public class User { private String name; @NotNull - private List sourceApplicationIds; + @Builder.Default + private List sourceApplicationIds = new ArrayList<>(); } From 3fd1d35ab8c6408fd59eb3204e1f456bb67aaa7b Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:46:08 +0200 Subject: [PATCH 42/50] wrap content in array to prevent error on modifying unmutable list --- .../user/azure/services/GraphPageWalkerService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java index 109e61d..287a6dd 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/services/GraphPageWalkerService.java @@ -78,16 +78,15 @@ List getContentFromCurrentAndNextPages( if (collectionPage == null) { throw new IllegalStateException("Page is null"); } - List content = contentProcessing.apply(new ArrayList<>(collectionPage.getCurrentPage())); + List content = new ArrayList<>(contentProcessing.apply(new ArrayList<>(collectionPage.getCurrentPage()))); COLLECTION_REQUEST_BUILDER nextBuilder = collectionPage.getNextPage(); if (nextBuilder != null) { content.addAll(getContentFromCurrentAndNextPages( - collectionPage.getNextPage().buildRequest(), + nextBuilder.buildRequest(), performRequest, contentProcessing )); } return content; } - } From 4b484a10d1b04cbd8f68acbfa5d753825c0941d7 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:46:32 +0200 Subject: [PATCH 43/50] add transactional to method to be able to delete users --- .../java/no/fintlabs/flyt/authorization/user/UserService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java index dfec93b..dddeff1 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/UserService.java @@ -6,6 +6,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.function.Function; @@ -26,6 +27,7 @@ public UserService(UserRepository userRepository) { this.userRepository = userRepository; } + @Transactional public void updateUsers(Collection users) { log.info("Updating {} user entities", users.size()); From c6cf1c242da76bebb1970a3962503bbccf860f36 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 10:55:42 +0200 Subject: [PATCH 44/50] update tests to use long sourceAppId's instead of String --- .../ClientAuthorizationProducerRecordBuilderTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java index 9f85300..9ca583e 100644 --- a/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java +++ b/src/test/java/no/fintlabs/ClientAuthorizationProducerRecordBuilderTest.java @@ -24,7 +24,7 @@ void setUp() { @Test void testApply_AcosSourceApplicationClientId() { String clientId = "acosClientId"; - String sourceAppId = "1"; + long sourceAppId = 1L; AcosSourceApplication.CLIENT_ID = clientId; ConsumerRecord record = new ConsumerRecord<>("topic", 0, 0, "", clientId); @@ -38,7 +38,7 @@ void testApply_AcosSourceApplicationClientId() { @Test void testApply_EgrunnervervSourceApplicationClientId() { String clientId = "egrunnervervClientId"; - String sourceAppId = "2"; + long sourceAppId = 2L; EgrunnervervSourceApplication.CLIENT_ID = clientId; ConsumerRecord record = new ConsumerRecord<>("topic", 0, 0, "", clientId); @@ -52,7 +52,7 @@ void testApply_EgrunnervervSourceApplicationClientId() { @Test void testApply_DigisakSourceApplicationClientId() { String clientId = "digisakClientId"; - String sourceAppId = "3"; + long sourceAppId = 3L; DigisakSourceApplication.CLIENT_ID = clientId; ConsumerRecord record = new ConsumerRecord<>("topic", 0, 0, "", clientId); From f69c2b7c297944553f4d786a10f4e8f27ee33c97 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 11:22:39 +0200 Subject: [PATCH 45/50] update kustomize deployments with fint.flyt.azure-ad-gateway.enabled --- kustomize/base/flais.yaml | 2 ++ kustomize/overlays/agderfk-no/api/kustomization.yaml | 3 +++ kustomize/overlays/ffk-no/api/kustomization.yaml | 3 +++ kustomize/overlays/mrfylke-no/api/kustomization.yaml | 3 +++ kustomize/overlays/rogfk-no/api/kustomization.yaml | 3 +++ kustomize/overlays/tromsfylke-no/api/kustomization.yaml | 3 +++ kustomize/overlays/tromsfylke-no/beta/kustomization.yaml | 3 +++ kustomize/overlays/trondelagfylke-no/api/kustomization.yaml | 3 +++ kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml | 3 +++ kustomize/overlays/vlfk-no/api/kustomization.yaml | 3 +++ kustomize/overlays/vlfk-no/beta/kustomization.yaml | 3 +++ 11 files changed, 32 insertions(+) diff --git a/kustomize/base/flais.yaml b/kustomize/base/flais.yaml index f16121d..5b1c022 100644 --- a/kustomize/base/flais.yaml +++ b/kustomize/base/flais.yaml @@ -41,6 +41,8 @@ spec: } - name: fint.flyt.resource-server.security.api.internal.enabled value: 'true' + - name: fint.flyt.azure-ad-gateway.enabled + value: 'true' onePassword: itemPath: path envFrom: [] diff --git a/kustomize/overlays/agderfk-no/api/kustomization.yaml b/kustomize/overlays/agderfk-no/api/kustomization.yaml index 72e2340..ee1545c 100644 --- a/kustomize/overlays/agderfk-no/api/kustomization.yaml +++ b/kustomize/overlays/agderfk-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/ffk-no/api/kustomization.yaml b/kustomize/overlays/ffk-no/api/kustomization.yaml index 6fb48d2..5cba56e 100644 --- a/kustomize/overlays/ffk-no/api/kustomization.yaml +++ b/kustomize/overlays/ffk-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/mrfylke-no/api/kustomization.yaml b/kustomize/overlays/mrfylke-no/api/kustomization.yaml index c96f083..513a317 100644 --- a/kustomize/overlays/mrfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/mrfylke-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/rogfk-no/api/kustomization.yaml b/kustomize/overlays/rogfk-no/api/kustomization.yaml index 7d6758c..6781c41 100644 --- a/kustomize/overlays/rogfk-no/api/kustomization.yaml +++ b/kustomize/overlays/rogfk-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/tromsfylke-no/api/kustomization.yaml b/kustomize/overlays/tromsfylke-no/api/kustomization.yaml index 869bad8..61dfdd0 100644 --- a/kustomize/overlays/tromsfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/tromsfylke-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml b/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml index dbed73c..819d7ae 100644 --- a/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml +++ b/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml @@ -35,6 +35,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false - op: add path: "/spec/envFrom/0" value: diff --git a/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml b/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml index f4bd862..7772e16 100644 --- a/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml b/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml index 196f363..ed7e964 100644 --- a/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml +++ b/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vlfk-no/api/kustomization.yaml b/kustomize/overlays/vlfk-no/api/kustomization.yaml index 3fe25e6..7ad6a85 100644 --- a/kustomize/overlays/vlfk-no/api/kustomization.yaml +++ b/kustomize/overlays/vlfk-no/api/kustomization.yaml @@ -34,6 +34,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vlfk-no/beta/kustomization.yaml b/kustomize/overlays/vlfk-no/beta/kustomization.yaml index fe927d2..7422673 100644 --- a/kustomize/overlays/vlfk-no/beta/kustomization.yaml +++ b/kustomize/overlays/vlfk-no/beta/kustomization.yaml @@ -35,6 +35,9 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } + - op: replace + path: "/spec/env/3/value" + value: false - op: add path: "/spec/env/-" value: From 61f8f5d97df537ab89021052ae4cdd36e3b1bd30 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 12:18:50 +0200 Subject: [PATCH 46/50] change property azure-ad-gateway.enable to azure-ad-gateway.enabled --- .../authorization/user/AzureAdUserAuthorizationComponent.java | 2 +- .../user/azure/configuration/AzureAdGatewayConfiguration.java | 2 +- .../user/azure/configuration/GraphServiceConfiguration.java | 2 +- .../flyt/authorization/user/controller/MeController.java | 2 +- .../flyt/authorization/user/controller/UserController.java | 2 +- src/main/resources/application-local-staging.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java index 05a6415..da13315 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/AzureAdUserAuthorizationComponent.java @@ -16,7 +16,7 @@ import java.util.UUID; @Component -@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enabled", havingValue = "true") @Slf4j public class AzureAdUserAuthorizationComponent { diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java index 04d6cf1..07e7446 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/AzureAdGatewayConfiguration.java @@ -21,7 +21,7 @@ public class AzureAdGatewayConfiguration { @NotNull - private Boolean enable; + private Boolean enabled; @Valid private PermittedAppRolesProperties permittedAppRoles; diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java index c97db05..a077d0c 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/azure/configuration/GraphServiceConfiguration.java @@ -22,7 +22,7 @@ @Validated @EnableAutoConfiguration @Configuration -@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enabled", havingValue = "true") public class GraphServiceConfiguration { @Bean diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index 44dd30e..e71d596 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -36,7 +36,7 @@ public class MeController { public MeController( TokenParsingUtils tokenParsingUtils, - @Value("${fint.flyt.azure-ad-gateway.enable}") Boolean azureAdUserAuthorizationEnabled, + @Value("${fint.flyt.azure-ad-gateway.enabled}") Boolean azureAdUserAuthorizationEnabled, @Autowired(required = false) AzureAdUserAuthorizationComponent azureAdUserAuthorizationComponent ) { this.tokenParsingUtils = tokenParsingUtils; diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index 208f462..ddc356e 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -17,7 +17,7 @@ import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API; -@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enable", havingValue = "true") +@ConditionalOnProperty(value = "fint.flyt.azure-ad-gateway.enabled", havingValue = "true") @RestController @RequestMapping(INTERNAL_API + "/authorization/users") public class UserController { diff --git a/src/main/resources/application-local-staging.yaml b/src/main/resources/application-local-staging.yaml index 76554ee..fae5dbe 100644 --- a/src/main/resources/application-local-staging.yaml +++ b/src/main/resources/application-local-staging.yaml @@ -2,7 +2,7 @@ fint: org-id: fintlabs.no flyt: azure-ad-gateway: - enable: true + enabled: true resource-server: security: api: From 7a0340ed864bd778e8a5e92b8572ea1db226895a Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 13:16:01 +0200 Subject: [PATCH 47/50] replace pageable with custom pageable that is supported by webflux --- .../flyt/authorization/user/controller/UserController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java index ddc356e..0a16a8d 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/UserController.java @@ -5,7 +5,9 @@ import no.fintlabs.flyt.authorization.user.model.User; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -36,8 +38,11 @@ public UserController( @GetMapping public Mono>> get( @AuthenticationPrincipal Mono authenticationMono, - @RequestParam Pageable pageable + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(defaultValue = "name") String sort ) { + Pageable pageable = PageRequest.of(page, size, Sort.by(sort)); return tokenParsingUtils.isAdmin(authenticationMono) .flatMap(isAdmin -> { if (!isAdmin) { From 165844f2e49297a72cc74336fb32294b56f89ee1 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Mon, 1 Jul 2024 13:42:49 +0200 Subject: [PATCH 48/50] surround boolean value with '' to prevent Invalid value: "boolean": spec.env[3].value in body must be of type string: "boolean" on build in github actions --- kustomize/overlays/agderfk-no/api/kustomization.yaml | 2 +- kustomize/overlays/ffk-no/api/kustomization.yaml | 2 +- kustomize/overlays/mrfylke-no/api/kustomization.yaml | 2 +- kustomize/overlays/rogfk-no/api/kustomization.yaml | 2 +- kustomize/overlays/tromsfylke-no/api/kustomization.yaml | 2 +- kustomize/overlays/tromsfylke-no/beta/kustomization.yaml | 2 +- kustomize/overlays/trondelagfylke-no/api/kustomization.yaml | 2 +- kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml | 2 +- kustomize/overlays/vlfk-no/api/kustomization.yaml | 2 +- kustomize/overlays/vlfk-no/beta/kustomization.yaml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kustomize/overlays/agderfk-no/api/kustomization.yaml b/kustomize/overlays/agderfk-no/api/kustomization.yaml index ee1545c..8453b04 100644 --- a/kustomize/overlays/agderfk-no/api/kustomization.yaml +++ b/kustomize/overlays/agderfk-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/ffk-no/api/kustomization.yaml b/kustomize/overlays/ffk-no/api/kustomization.yaml index 5cba56e..16882db 100644 --- a/kustomize/overlays/ffk-no/api/kustomization.yaml +++ b/kustomize/overlays/ffk-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/mrfylke-no/api/kustomization.yaml b/kustomize/overlays/mrfylke-no/api/kustomization.yaml index 513a317..9f98ce9 100644 --- a/kustomize/overlays/mrfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/mrfylke-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/rogfk-no/api/kustomization.yaml b/kustomize/overlays/rogfk-no/api/kustomization.yaml index 6781c41..3809728 100644 --- a/kustomize/overlays/rogfk-no/api/kustomization.yaml +++ b/kustomize/overlays/rogfk-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/tromsfylke-no/api/kustomization.yaml b/kustomize/overlays/tromsfylke-no/api/kustomization.yaml index 61dfdd0..74cea06 100644 --- a/kustomize/overlays/tromsfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/tromsfylke-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml b/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml index 819d7ae..9833d13 100644 --- a/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml +++ b/kustomize/overlays/tromsfylke-no/beta/kustomization.yaml @@ -37,7 +37,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' - op: add path: "/spec/envFrom/0" value: diff --git a/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml b/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml index 7772e16..c42cb99 100644 --- a/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml +++ b/kustomize/overlays/trondelagfylke-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml b/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml index ed7e964..355e48b 100644 --- a/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml +++ b/kustomize/overlays/trondelagfylke-no/beta/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vlfk-no/api/kustomization.yaml b/kustomize/overlays/vlfk-no/api/kustomization.yaml index 7ad6a85..418be41 100644 --- a/kustomize/overlays/vlfk-no/api/kustomization.yaml +++ b/kustomize/overlays/vlfk-no/api/kustomization.yaml @@ -36,7 +36,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' target: kind: Application name: fint-flyt-authorization-service \ No newline at end of file diff --git a/kustomize/overlays/vlfk-no/beta/kustomization.yaml b/kustomize/overlays/vlfk-no/beta/kustomization.yaml index 7422673..0a3746d 100644 --- a/kustomize/overlays/vlfk-no/beta/kustomization.yaml +++ b/kustomize/overlays/vlfk-no/beta/kustomization.yaml @@ -37,7 +37,7 @@ patches: } - op: replace path: "/spec/env/3/value" - value: false + value: 'false' - op: add path: "/spec/env/-" value: From 02d6b4b52d545c98c3e31d75f6dcb2f8ace24fa3 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Tue, 2 Jul 2024 09:50:54 +0200 Subject: [PATCH 49/50] always return access to all applications if users is admin --- .../user/controller/MeController.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java index e71d596..1ee777f 100644 --- a/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java +++ b/src/main/java/no/fintlabs/flyt/authorization/user/controller/MeController.java @@ -67,15 +67,25 @@ public Mono> getRestrictedPageAuthor public Mono> get( @AuthenticationPrincipal Mono authenticationMono ) { - return authenticationMono - .map(authentication -> (JwtAuthenticationToken) authentication) - .map(authentication -> - azureAdUserAuthorizationEnabled - ? getUserFromUserAuthorizationComponent(authentication) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()) - : ResponseEntity.ok(createUserWithAccessToAllApplications(authentication)) - ); + return tokenParsingUtils.isAdmin(authenticationMono) + .flatMap(isAdmin -> { + if (isAdmin) { + return authenticationMono + .map(authentication -> (JwtAuthenticationToken) authentication) + .flatMap(authentication -> + Mono.just(ResponseEntity.ok(createUserWithAccessToAllApplications(authentication)))); + } else { + return authenticationMono + .map(authentication -> (JwtAuthenticationToken) authentication) + .map(authentication -> + azureAdUserAuthorizationEnabled + ? getUserFromUserAuthorizationComponent(authentication) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()) + : ResponseEntity.ok(createUserWithAccessToAllApplications(authentication)) + ); + } + }); } private Optional getUserFromUserAuthorizationComponent(JwtAuthenticationToken token) { From 86fb5ddfc645f49cae2317d725257f670ce050a3 Mon Sep 17 00:00:00 2001 From: Egil Ballestad Date: Wed, 3 Jul 2024 10:29:19 +0200 Subject: [PATCH 50/50] add rogfk to fetch 1password credentials for Azure AD --- .../overlays/rogfk-no/api/kustomization.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/kustomize/overlays/rogfk-no/api/kustomization.yaml b/kustomize/overlays/rogfk-no/api/kustomization.yaml index 3809728..aa2fe68 100644 --- a/kustomize/overlays/rogfk-no/api/kustomization.yaml +++ b/kustomize/overlays/rogfk-no/api/kustomization.yaml @@ -34,9 +34,18 @@ patches: "vigo.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"], "novari.no":["https://role-catalog.vigoiks.no/vigo/flyt/developer"] } - - op: replace - path: "/spec/env/3/value" - value: 'false' + - op: add + path: "/spec/envFrom/0" + value: + secretRef: + name: fint-flyt-authorization-service target: kind: Application + name: fint-flyt-authorization-service + - patch: |- + - op: replace + path: "/spec/itemPath" + value: "vaults/aks-api-vault/items/fint-flyt-authorization-service-rogfk-no" + target: + kind: OnePasswordItem name: fint-flyt-authorization-service \ No newline at end of file