Skip to content

Commit

Permalink
Merge pull request #15 from FINTLabs/FFS-1060-utvide-fint-flyt-author…
Browse files Browse the repository at this point in the history
…ization-service-til-a-snakke-frontend

Ffs 1060 utvide fint flyt authorization service til a snakke frontend
  • Loading branch information
Battlestad authored May 30, 2024
2 parents 24c061c + 14a0c4f commit a68a072
Show file tree
Hide file tree
Showing 16 changed files with 304 additions and 47 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.72.Final:osx-aarch_64'

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
implementation 'org.flywaydb:flyway-core'

implementation 'no.fintlabs:fint-kafka:4.0.1'

implementation 'no.fintlabs:fint-flyt-resource-server:2.1.0-rc-3'
Expand Down
2 changes: 2 additions & 0 deletions kustomize/base/flais.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ spec:
acls:
- permission: admin
topic: 'no-permission'
database:
database: fint-flyt
url:
hostname: flyt.vigoiks.no
basePath: path
Expand Down
42 changes: 0 additions & 42 deletions src/main/java/no/fintlabs/AuthorizationController.java

This file was deleted.

11 changes: 11 additions & 0 deletions src/main/java/no/fintlabs/authorization/AuthorizationUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package no.fintlabs.authorization;

import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Service;

@Service
public class AuthorizationUtil {
public String getObjectIdentifierFromToken(JwtAuthenticationToken jwtAuthenticationToken) {
return jwtAuthenticationToken.getTokenAttributes().get("objectidentifier").toString();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package no.fintlabs.models.user;
package no.fintlabs.authorization.adminuser;

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class AuthorizedUser {
public class AdminUser {
private boolean admin;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package no.fintlabs.authorization.adminuser;

import no.fintlabs.authorization.user.UserPermission;
import no.fintlabs.authorization.user.UserPermissionDto;
import no.fintlabs.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.List;
import java.util.Optional;

import static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API;

@RestController
@RequestMapping(INTERNAL_API + "/authorization/adminuser")
public class AdminUserController {

private final UserPermissionRepository userPermissionRepository;

public AdminUserController(
UserPermissionRepository userPermissionRepository) {
this.userPermissionRepository = userPermissionRepository;
}


@GetMapping("check-is-admin")
public Mono<ResponseEntity<AdminUser>> checkAdminUser(
@AuthenticationPrincipal Mono<Authentication> authenticationMono
) {
return isAdmin(authenticationMono)
.map(isAdmin -> ResponseEntity.ok(AdminUser.builder().admin(isAdmin).build()));
}

@GetMapping("userpermissions")
public Mono<ResponseEntity<List<UserPermissionDto>>> getUserPermissions(
@AuthenticationPrincipal Mono<Authentication> authenticationMono
) {
return isAdmin(authenticationMono)
.flatMap(isAdmin -> {
if (isAdmin) {
return Mono.fromCallable(userPermissionRepository::findAll)
.subscribeOn(Schedulers.boundedElastic())
.map(userPermissions -> {
List<UserPermissionDto> userPermissionDtos = new java.util.ArrayList<>(List.of());
userPermissions.forEach(userPermission -> userPermissionDtos.add(buildUserPermissionDto(userPermission)));
return ResponseEntity.ok().body(userPermissionDtos);
});
} else {
return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build());
}
});
}

@PostMapping("userpermissions")
public Mono<ResponseEntity<List<UserPermissionDto>>> setUserPermissions(
@RequestBody List<UserPermissionDto> userPermissionDtos,
@AuthenticationPrincipal Mono<Authentication> authenticationMono
) {
return isAdmin(authenticationMono)
.flatMap(isAdmin -> {
if (isAdmin) {
return Flux.fromIterable(userPermissionDtos)
.flatMap(userPermissionDto -> Mono.fromCallable(() -> {
Optional<UserPermission> userPermissionOptional = userPermissionRepository
.findByObjectIdentifier(userPermissionDto.getObjectIdentifier());
if (userPermissionOptional.isPresent()) {
UserPermission userPermission = userPermissionOptional.get();
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);
}
}).subscribeOn(Schedulers.boundedElastic()))
.collectList()
.map(ResponseEntity::ok);
} else {
return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build());
}
});
}

private UserPermissionDto buildUserPermissionDto(UserPermission userPermission) {
return UserPermissionDto
.builder()
.objectIdentifier(userPermission.getObjectIdentifier())
.sourceApplicationIds(userPermission.getSourceApplicationIds())
.build();
}

private Mono<Boolean> isAdmin(Mono<Authentication> authenticationMono) {
return authenticationMono
.map(authentication -> authentication != null && authentication.getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_ADMIN")));
}

}
32 changes: 32 additions & 0 deletions src/main/java/no/fintlabs/authorization/user/UserPermission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package no.fintlabs.authorization.user;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;

import javax.persistence.*;
import java.util.List;

@Getter
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class UserPermission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonIgnore
@Setter(AccessLevel.NONE)
private long id;
@Setter
@Column(unique = true, nullable = false)
private String objectIdentifier;
@ElementCollection
@CollectionTable(
name = "user_permission_source_application_ids",
joinColumns = @JoinColumn(name = "user_permission_id"),
uniqueConstraints = @UniqueConstraint(columnNames = {"user_permission_id", "source_application_ids"})
)
@Setter
@Column(name = "source_application_ids")
private List<Integer> sourceApplicationIds;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package no.fintlabs.authorization.user;

import lombok.extern.slf4j.Slf4j;
import no.fintlabs.authorization.AuthorizationUtil;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
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 static no.fintlabs.resourceserver.UrlPaths.INTERNAL_API;

@Slf4j
@RequestMapping(INTERNAL_API + "/authorization/user")
@RestController
public class UserPermissionController {

private final UserPermissionRepository userPermissionRepository;
private final AuthorizationUtil authorizationUtil;

public UserPermissionController(
UserPermissionRepository userPermissionRepository,
AuthorizationUtil authorizationUtil
) {
this.userPermissionRepository = userPermissionRepository;
this.authorizationUtil = authorizationUtil;
}

@GetMapping("check-authorized")
public ResponseEntity<?> checkAuthorization() {
return ResponseEntity.ok("User authorized");
}

@GetMapping("permission")
public Mono<ResponseEntity<UserPermissionDto>> getSourceApplications(
@AuthenticationPrincipal Mono<Authentication> authenticationMono
) {
return authenticationMono
.map(authentication -> authorizationUtil
.getObjectIdentifierFromToken((JwtAuthenticationToken) authentication))
.publishOn(Schedulers.boundedElastic())
.map(userPermissionRepository::findByObjectIdentifier)
.map(optionalUserPermission -> optionalUserPermission.map(userPermission -> ResponseEntity.ok(
UserPermissionDto
.builder()
.objectIdentifier(userPermission.getObjectIdentifier())
.sourceApplicationIds(userPermission.getSourceApplicationIds())
.build()
)).orElseGet(() -> ResponseEntity.notFound().build())
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package no.fintlabs.authorization.user;

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 UserPermissionDto {
@NotNull
private String objectIdentifier;
private String email;
@NotNull
private List<Integer> sourceApplicationIds;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package no.fintlabs.authorization.user;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserPermissionRepository extends JpaRepository<UserPermission, Long> {

Optional<UserPermission> findByObjectIdentifier(String sub);

}
23 changes: 23 additions & 0 deletions src/main/resources/application-flyt-postgres.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
spring:
jpa:
properties:
hibernate:
jdbc:
time_zone: UTC
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
enable_lazy_load_no_trans: true
hibernate:
ddl-auto: none
datasource:
driver-class-name: org.postgresql.Driver
username: ${fint.database.username}
hikari:
schema: ${fint.database.username}
url: ${fint.database.url}
password: ${fint.database.password}

flyway:
locations: classpath:db/migration/
lock-retry-count: 300
6 changes: 6 additions & 0 deletions src/main/resources/application-local-staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,11 @@ fint:
spring:
kafka:
bootstrap-servers: localhost:9092
datasource:
hikari:
schema: fintlabs_no
url: jdbc:postgresql://localhost:5435/fint-flyt-authorization-service
username: postgres
password: password
server:
port: 8086
4 changes: 1 addition & 3 deletions src/main/resources/application-schema-generation.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
spring:
profiles:
include: local-staging
jpa:
properties:
javax:
Expand All @@ -9,4 +7,4 @@ spring:
scripts:
action: create
create-target: __init.sql
create-source: metadata
create-source: metadata
1 change: 1 addition & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ spring:
include:
- flyt-kafka
- flyt-logging
- flyt-postgres
- flyt-resource-server
Loading

0 comments on commit a68a072

Please sign in to comment.