diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df4762b9..dbb1a95a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,4 +7,4 @@ on: jobs: build: - uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v2.0.11 + uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v3 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dfef0b70..9f366b27 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,7 +7,7 @@ on: jobs: build-and-deploy: - uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v2 + uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v3 secrets: github-token: ${{ secrets.GITHUB_TOKEN }} mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }} diff --git a/pom.xml b/pom.xml index d189c453..7dc98193 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.vality service-parent-pom - 2.1.8 + 3.1.7 org-manager @@ -16,19 +16,19 @@ org-manager - 17 - 17 + 21 + 21 UTF-8 UTF-8 8022 8080 8023 ${server.port} ${server.rest.port} ${management.port} - 5.6.12 17.0.0 4.34.0 - 1.19-8707f87-server + 1.38-8c9b968 2.32.0 + 1.685-5c25c2e @@ -36,7 +36,7 @@ dev.vality.woody woody-thrift - ${woody.version} + 2.1.0 dev.vality @@ -45,17 +45,18 @@ dev.vality - swag-organizations - 1.25-de0cd06-server + swag-organizations-server + ${swag.organizations.version} dev.vality damsel + ${damsel.version} dev.vality org-management-proto - 1.11-03a269d + 1.18-6b1eb95 dev.vality @@ -66,7 +67,7 @@ dev.vality bouncer-proto - 1.41-5628c61 + 1.57-31866c3 @@ -94,26 +95,10 @@ org.springframework.boot spring-boot-starter-security - - - org.springframework - spring-security-config - - - org.springframework - spring-security-web - - - - - org.springframework.security - spring-security-web - ${spring-security.version} - org.springframework.security - spring-security-config - ${spring-security.version} + org.springframework.boot + spring-boot-starter-oauth2-resource-server net.javacrumbs.shedlock @@ -128,37 +113,8 @@ org.keycloak - keycloak-admin-client + keycloak-core ${keycloak.version} - - - org.jboss.resteasy - resteasy-client - - - org.jboss.resteasy - resteasy-multipart-provider - - - org.jboss.resteasy - resteasy-jackson2-provider - - - org.jboss.resteasy - resteasy-jaxb-provider - - - - - org.keycloak - keycloak-spring-security-adapter - ${keycloak.version} - - - org.bouncycastle - bcprov-jdk15on - - org.bouncycastle @@ -177,14 +133,9 @@ micrometer-registry-prometheus - javax.servlet - javax.servlet-api - 4.0.1 - - - javax.validation - validation-api - 2.0.1.Final + jakarta.validation + jakarta.validation-api + 3.1.1 org.projectlombok @@ -193,12 +144,15 @@ org.postgresql postgresql - 42.4.3 org.flywaydb flyway-core + + org.flywaydb + flyway-database-postgresql + com.google.guava guava @@ -231,7 +185,7 @@ org.springframework.cloud spring-cloud-contract-wiremock - 3.1.1 + 4.1.5 test @@ -319,6 +273,22 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 21 + 21 + + + org.projectlombok + lombok + 1.18.36 + + + + diff --git a/src/main/java/dev/vality/orgmanager/config/ConfigResolverConfig.java b/src/main/java/dev/vality/orgmanager/config/ConfigResolverConfig.java index bf7af82b..7152cfd0 100644 --- a/src/main/java/dev/vality/orgmanager/config/ConfigResolverConfig.java +++ b/src/main/java/dev/vality/orgmanager/config/ConfigResolverConfig.java @@ -2,51 +2,65 @@ import com.google.common.base.Strings; import dev.vality.orgmanager.config.properties.KeyCloakProperties; -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.KeycloakDeployment; -import org.keycloak.adapters.KeycloakDeploymentBuilder; -import org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper; -import org.keycloak.representations.adapters.config.AdapterConfig; -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.context.annotation.DependsOn; +import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtValidators; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import java.io.IOException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @Configuration +@ConditionalOnProperty(value = "auth.enabled", havingValue = "true") public class ConfigResolverConfig { @Bean - public KeycloakConfigResolver keycloakConfigResolver(KeyCloakProperties keyCloakProperties) { - return facade -> { - KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(adapterConfig(keyCloakProperties)); - deployment.setNotBefore(keyCloakProperties.getNotBefore()); - return deployment; - }; + public JwtDecoder jwtDecoder(KeyCloakProperties keyCloakProperties) { + RSAPublicKey publicKey = readPublicKey(keycloakRealmPublicKey(keyCloakProperties)); + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(publicKey).build(); + + String issuer = String.format( + "%s/realms/%s", + keyCloakProperties.getAuthServerUrl(), + keyCloakProperties.getRealm() + ); + OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuer); + OAuth2TokenValidator validator = new DelegatingOAuth2TokenValidator<>(withIssuer); + jwtDecoder.setJwtValidator(validator); + return jwtDecoder; } - private AdapterConfig adapterConfig(KeyCloakProperties keyCloakProperties) { - String keycloakRealmPublicKey; + private String keycloakRealmPublicKey(KeyCloakProperties keyCloakProperties) { if (!Strings.isNullOrEmpty(keyCloakProperties.getRealmPublicKeyFilePath())) { - keycloakRealmPublicKey = readKeyFromFile(keyCloakProperties.getRealmPublicKeyFilePath()); - } else { - keycloakRealmPublicKey = keyCloakProperties.getRealmPublicKey(); + return readKeyFromFile(keyCloakProperties.getRealmPublicKeyFilePath()); } + return keyCloakProperties.getRealmPublicKey(); + } - AdapterConfig adapterConfig = new AdapterConfig(); - adapterConfig.setRealm(keyCloakProperties.getRealm()); - adapterConfig.setRealmKey(keycloakRealmPublicKey); - adapterConfig.setResource(keyCloakProperties.getResource()); - adapterConfig.setAuthServerUrl(keyCloakProperties.getAuthServerUrl()); - adapterConfig.setUseResourceRoleMappings(true); - adapterConfig.setBearerOnly(true); - adapterConfig.setSslRequired(keyCloakProperties.getSslRequired()); - return adapterConfig; + private RSAPublicKey readPublicKey(String publicKey) { + String normalizedPublicKey = publicKey.replaceAll("\\s", ""); + byte[] decoded = Base64.getDecoder().decode(normalizedPublicKey); + X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded); + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return (RSAPublicKey) keyFactory.generatePublic(spec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { + throw new IllegalStateException("Unable to parse keycloak realm public key", ex); + } } private String readKeyFromFile(String filePath) { diff --git a/src/main/java/dev/vality/orgmanager/config/PartyManagementConfig.java b/src/main/java/dev/vality/orgmanager/config/PartyManagementConfig.java deleted file mode 100644 index c810f800..00000000 --- a/src/main/java/dev/vality/orgmanager/config/PartyManagementConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package dev.vality.orgmanager.config; - -import dev.vality.damsel.payment_processing.PartyManagementSrv; -import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.Resource; - -import java.io.IOException; - -@Configuration -public class PartyManagementConfig { - - @Bean - public PartyManagementSrv.Iface partyManagementClient( - @Value("${party-management.url}") Resource resource, - @Value("${party-management.networkTimeout}") int networkTimeout - ) throws IOException { - return new THSpawnClientBuilder() - .withNetworkTimeout(networkTimeout) - .withAddress(resource.getURI()) - .build(PartyManagementSrv.Iface.class); - } - -} diff --git a/src/main/java/dev/vality/orgmanager/config/SecurityConfig.java b/src/main/java/dev/vality/orgmanager/config/SecurityConfig.java index a596a0b8..71c11b5a 100644 --- a/src/main/java/dev/vality/orgmanager/config/SecurityConfig.java +++ b/src/main/java/dev/vality/orgmanager/config/SecurityConfig.java @@ -1,66 +1,47 @@ package dev.vality.orgmanager.config; -import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.keycloak.adapters.springsecurity.management.HttpSessionManager; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @Configuration -@EnableWebSecurity -@ComponentScan( - basePackageClasses = KeycloakSecurityComponents.class, - excludeFilters = @ComponentScan.Filter( - type = FilterType.REGEX, - pattern = "org.keycloak.adapters.springsecurity.management.HttpSessionManager" - ) -) -@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) +@EnableMethodSecurity(prePostEnabled = true) @ConditionalOnProperty(value = "auth.enabled", havingValue = "true") -@DependsOn("configResolverConfig") -public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { - - @Override - protected HttpSessionManager httpSessionManager() { - return super.httpSessionManager(); - } +public class SecurityConfig { @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new NullAuthenticatedSessionStrategy(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .cors().and() - .csrf().disable() - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .antMatchers(HttpMethod.GET, "/**/health").permitAll() - .antMatchers(HttpMethod.GET, "/**/health/readiness").permitAll() - .antMatchers(HttpMethod.GET, "/**/health/liveness").permitAll() - .antMatchers("/**/auth-context").permitAll() - .antMatchers(HttpMethod.GET, "/**/prometheus").permitAll() - .anyRequest().authenticated(); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) { - auth.authenticationProvider(keycloakAuthenticationProvider()); + .cors(Customizer.withDefaults()) + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(config -> config + .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .requestMatchers( + HttpMethod.GET, + "/health", + "/health/readiness", + "/health/liveness", + "/actuator/health", + "/actuator/health/readiness", + "/actuator/health/liveness", + "/prometheus", + "/actuator/prometheus" + ).permitAll() + .requestMatchers("/auth-context").permitAll() + .anyRequest().authenticated()) + .oauth2ResourceServer(config -> config.jwt(Customizer.withDefaults())); + return http.build(); } @Bean diff --git a/src/main/java/dev/vality/orgmanager/config/SecurityDisabledConfig.java b/src/main/java/dev/vality/orgmanager/config/SecurityDisabledConfig.java new file mode 100644 index 00000000..0637eaf4 --- /dev/null +++ b/src/main/java/dev/vality/orgmanager/config/SecurityDisabledConfig.java @@ -0,0 +1,21 @@ +package dev.vality.orgmanager.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@ConditionalOnProperty(value = "auth.enabled", havingValue = "false") +public class SecurityDisabledConfig { + + @Bean + public SecurityFilterChain disabledSecurityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(config -> config.anyRequest().permitAll()); + return http.build(); + } +} diff --git a/src/main/java/dev/vality/orgmanager/controller/OrgsController.java b/src/main/java/dev/vality/orgmanager/controller/OrgsController.java index 9aaf8aeb..9d102764 100644 --- a/src/main/java/dev/vality/orgmanager/controller/OrgsController.java +++ b/src/main/java/dev/vality/orgmanager/controller/OrgsController.java @@ -11,8 +11,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; -import javax.validation.constraints.Size; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; import java.util.Objects; @Slf4j @@ -128,15 +128,15 @@ public ResponseEntity listInvitations(String requestId, public ResponseEntity revokeInvitation(String requestId, String orgId, String invitationId, - InlineObject1 inlineObject1) { + RevokeInvitationRequest request) { log.info("Revoke invitation: requestId={}, orgId={}, invitationId={}, payload={}", - requestId, orgId, invitationId, inlineObject1); + requestId, orgId, invitationId, request); ResourceDto resource = ResourceDto.builder() .orgId(orgId) .invitationId(invitationId) .build(); resourceAccessService.checkRights(resource); - invitationService.revoke(orgId, invitationId, inlineObject1); + invitationService.revoke(orgId, invitationId, request); return ResponseEntity .status(HttpStatus.NO_CONTENT) .build(); @@ -167,12 +167,12 @@ public ResponseEntity listOrgRoles(String requestId, St } @Override - public ResponseEntity patchOrg(String requestId, String orgId, InlineObject inlineObject) { + public ResponseEntity patchOrg(String requestId, String orgId, PatchOrgRequest request) { ResourceDto resource = ResourceDto.builder() .orgId(orgId) .build(); resourceAccessService.checkRights(resource); - Organization modifiedOrganization = organizationService.modify(orgId, inlineObject.getName()); + Organization modifiedOrganization = organizationService.modify(orgId, request.getName()); return ResponseEntity.ok(modifiedOrganization); } diff --git a/src/main/java/dev/vality/orgmanager/controller/UserController.java b/src/main/java/dev/vality/orgmanager/controller/UserController.java index c4c2e240..668de83e 100644 --- a/src/main/java/dev/vality/orgmanager/controller/UserController.java +++ b/src/main/java/dev/vality/orgmanager/controller/UserController.java @@ -12,7 +12,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @Slf4j @RestController diff --git a/src/main/java/dev/vality/orgmanager/entity/InvitationEntity.java b/src/main/java/dev/vality/orgmanager/entity/InvitationEntity.java index 8d3f4266..520f2a66 100644 --- a/src/main/java/dev/vality/orgmanager/entity/InvitationEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/InvitationEntity.java @@ -2,7 +2,7 @@ import lombok.*; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; import java.time.LocalDateTime; diff --git a/src/main/java/dev/vality/orgmanager/entity/MemberContextEntity.java b/src/main/java/dev/vality/orgmanager/entity/MemberContextEntity.java index 41b07c47..2a558ade 100644 --- a/src/main/java/dev/vality/orgmanager/entity/MemberContextEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/MemberContextEntity.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; diff --git a/src/main/java/dev/vality/orgmanager/entity/MemberEntity.java b/src/main/java/dev/vality/orgmanager/entity/MemberEntity.java index ccc7951c..6130f590 100644 --- a/src/main/java/dev/vality/orgmanager/entity/MemberEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/MemberEntity.java @@ -2,7 +2,7 @@ import lombok.*; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; import java.util.HashSet; diff --git a/src/main/java/dev/vality/orgmanager/entity/MemberRoleEntity.java b/src/main/java/dev/vality/orgmanager/entity/MemberRoleEntity.java index 3ba6c34a..6ed55000 100644 --- a/src/main/java/dev/vality/orgmanager/entity/MemberRoleEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/MemberRoleEntity.java @@ -2,9 +2,9 @@ import lombok.*; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import java.io.Serializable; diff --git a/src/main/java/dev/vality/orgmanager/entity/OrganizationEntity.java b/src/main/java/dev/vality/orgmanager/entity/OrganizationEntity.java index b279eb9b..c5ccf292 100644 --- a/src/main/java/dev/vality/orgmanager/entity/OrganizationEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/OrganizationEntity.java @@ -4,7 +4,7 @@ import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; import java.time.LocalDateTime; import java.util.HashSet; diff --git a/src/main/java/dev/vality/orgmanager/entity/OrganizationRoleEntity.java b/src/main/java/dev/vality/orgmanager/entity/OrganizationRoleEntity.java index a43e5803..eb563913 100644 --- a/src/main/java/dev/vality/orgmanager/entity/OrganizationRoleEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/OrganizationRoleEntity.java @@ -2,7 +2,7 @@ import lombok.*; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; import java.util.Set; diff --git a/src/main/java/dev/vality/orgmanager/entity/ScopeEntity.java b/src/main/java/dev/vality/orgmanager/entity/ScopeEntity.java index a2b8c58a..b507851a 100644 --- a/src/main/java/dev/vality/orgmanager/entity/ScopeEntity.java +++ b/src/main/java/dev/vality/orgmanager/entity/ScopeEntity.java @@ -5,9 +5,9 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; import java.io.Serializable; @Entity diff --git a/src/main/java/dev/vality/orgmanager/exception/RestExceptionHandler.java b/src/main/java/dev/vality/orgmanager/exception/RestExceptionHandler.java index 4dffa5f3..8fc6c146 100644 --- a/src/main/java/dev/vality/orgmanager/exception/RestExceptionHandler.java +++ b/src/main/java/dev/vality/orgmanager/exception/RestExceptionHandler.java @@ -1,7 +1,8 @@ package dev.vality.orgmanager.exception; -import dev.vality.swag.organizations.model.InlineResponse422; -import dev.vality.swag.organizations.model.InlineResponse4222; +import dev.vality.swag.organizations.model.JoinOrg422Response; +import dev.vality.swag.organizations.model.RemoveMemberRole422Response; +import dev.vality.swag.organizations.model.RevokeInvitation422Response; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -37,33 +38,33 @@ protected ResponseEntity handleBouncerException(BouncerException ex, Web } @ExceptionHandler(InviteExpiredException.class) - public ResponseEntity handleInviteExpiredException(InviteExpiredException ex) { - InlineResponse422 badResponse = new InlineResponse422() - .code(InlineResponse422.CodeEnum.INVITATIONEXPIRED) + public ResponseEntity handleInviteExpiredException(InviteExpiredException ex) { + JoinOrg422Response badResponse = new JoinOrg422Response() + .code(JoinOrg422Response.CodeEnum.INVITATION_EXPIRED) .message(String.format("Invite expired at: %s", ex.getExpiredAt())); return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(badResponse); } @ExceptionHandler(InviteRevokedException.class) - public ResponseEntity handleInviteRevokedException(InviteRevokedException ex) { - InlineResponse422 badResponse = new InlineResponse422() - .code(InlineResponse422.CodeEnum.INVITATIONEXPIRED) + public ResponseEntity handleInviteRevokedException(InviteRevokedException ex) { + RevokeInvitation422Response badResponse = new RevokeInvitation422Response() + .code(RevokeInvitation422Response.CodeEnum.INVALID_STATUS) .message(String.format("Invite revoked: %s", ex.getReason())); return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(badResponse); } @ExceptionHandler(InviteAlreadyAcceptedException.class) - public ResponseEntity handleInviteAlreadyAcceptedException(InviteAlreadyAcceptedException ex) { - InlineResponse422 badResponse = new InlineResponse422() - .code(InlineResponse422.CodeEnum.INVITATIONEXPIRED) + public ResponseEntity handleInviteAlreadyAcceptedException(InviteAlreadyAcceptedException ex) { + JoinOrg422Response badResponse = new JoinOrg422Response() + .code(JoinOrg422Response.CodeEnum.INVITATION_EXPIRED) .message(String.format("Invite accepted at: %s", ex.getAcceptedAt())); return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(badResponse); } @ExceptionHandler(LastRoleException.class) - public ResponseEntity handleLastRoleException(LastRoleException ex) { - InlineResponse4222 badResponse = new InlineResponse4222() - .code(InlineResponse4222.CodeEnum.ONLYROLELEFT) + public ResponseEntity handleLastRoleException(LastRoleException ex) { + RemoveMemberRole422Response badResponse = new RemoveMemberRole422Response() + .code(RemoveMemberRole422Response.CodeEnum.ONLY_ROLE_LEFT) .message("Member have only one role"); return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(badResponse); } diff --git a/src/main/java/dev/vality/orgmanager/repository/InvitationRepository.java b/src/main/java/dev/vality/orgmanager/repository/InvitationRepository.java index 69e95552..d58fc071 100644 --- a/src/main/java/dev/vality/orgmanager/repository/InvitationRepository.java +++ b/src/main/java/dev/vality/orgmanager/repository/InvitationRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.jpa.repository.QueryHints; import org.springframework.stereotype.Repository; -import javax.persistence.QueryHint; +import jakarta.persistence.QueryHint; import java.util.List; import java.util.Optional; diff --git a/src/main/java/dev/vality/orgmanager/service/InvitationService.java b/src/main/java/dev/vality/orgmanager/service/InvitationService.java index e4532552..9848fa3a 100644 --- a/src/main/java/dev/vality/orgmanager/service/InvitationService.java +++ b/src/main/java/dev/vality/orgmanager/service/InvitationService.java @@ -87,11 +87,11 @@ public ResponseEntity list(String orgId, InvitationStatusN } @Transactional - public void revoke(String orgId, String invitationId, InlineObject1 inlineObject) { + public void revoke(String orgId, String invitationId, RevokeInvitationRequest request) { InvitationEntity invitation = invitationRepository.findByIdAndOrganizationId(invitationId, orgId) .orElseThrow(ResourceNotFoundException::new); - invitation.setStatus(inlineObject.getStatus().getValue()); - invitation.setRevocationReason(inlineObject.getReason()); + invitation.setStatus(request.getStatus().getValue()); + invitation.setRevocationReason(request.getReason()); invitation.setRevokedAt(LocalDateTime.now()); invitationRepository.save(invitation); diff --git a/src/main/java/dev/vality/orgmanager/service/KeycloakService.java b/src/main/java/dev/vality/orgmanager/service/KeycloakService.java index 4dd8969f..23fdc5b3 100644 --- a/src/main/java/dev/vality/orgmanager/service/KeycloakService.java +++ b/src/main/java/dev/vality/orgmanager/service/KeycloakService.java @@ -1,19 +1,43 @@ package dev.vality.orgmanager.service; -import org.keycloak.KeycloakPrincipal; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; import org.keycloak.representations.AccessToken; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Service; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + @Service +@RequiredArgsConstructor public class KeycloakService { + private final ObjectMapper objectMapper; + public AccessToken getAccessToken() { - KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext() - .getAuthentication() - .getPrincipal(); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !(authentication.getPrincipal() instanceof Jwt jwt)) { + throw new IllegalStateException("Jwt principal is required"); + } + return objectMapper.convertValue(normalizedClaims(jwt.getClaims()), AccessToken.class); + } - return keycloakPrincipal.getKeycloakSecurityContext().getToken(); + private Map normalizedClaims(Map claims) { + Map normalized = new HashMap<>(claims); + normalizeEpochClaim(normalized, "exp"); + normalizeEpochClaim(normalized, "iat"); + normalizeEpochClaim(normalized, "nbf"); + return normalized; } + private void normalizeEpochClaim(Map claims, String claimName) { + Object claim = claims.get(claimName); + if (claim instanceof Instant instant) { + claims.put(claimName, instant.getEpochSecond()); + } + } } diff --git a/src/main/java/dev/vality/orgmanager/service/OrganizationService.java b/src/main/java/dev/vality/orgmanager/service/OrganizationService.java index 788c80d1..f757b9a5 100644 --- a/src/main/java/dev/vality/orgmanager/service/OrganizationService.java +++ b/src/main/java/dev/vality/orgmanager/service/OrganizationService.java @@ -42,7 +42,6 @@ public class OrganizationService { private final MemberContextRepository memberContextRepository; private final InvitationService invitationService; private final MemberRoleService memberRoleService; - private final PartyManagementService partyManagementService; // TODO [a.romanov]: idempotency @Transactional @@ -53,7 +52,6 @@ public Organization create( String keycloakUserId = token.getSubject(); OrganizationEntity entity = organizationConverter.toEntity(organization, keycloakUserId); OrganizationEntity savedEntity = organizationRepository.save(entity); - partyManagementService.createParty(savedEntity.getParty(), keycloakUserId, token.getEmail()); return organizationConverter.toDomain(savedEntity); } diff --git a/src/main/java/dev/vality/orgmanager/service/PartyManagementService.java b/src/main/java/dev/vality/orgmanager/service/PartyManagementService.java deleted file mode 100644 index febc7a6e..00000000 --- a/src/main/java/dev/vality/orgmanager/service/PartyManagementService.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.vality.orgmanager.service; - -public interface PartyManagementService { - - void createParty(String partyId, String userId, String email); - -} diff --git a/src/main/java/dev/vality/orgmanager/service/PartyManagementServiceImpl.java b/src/main/java/dev/vality/orgmanager/service/PartyManagementServiceImpl.java deleted file mode 100644 index ab7cd867..00000000 --- a/src/main/java/dev/vality/orgmanager/service/PartyManagementServiceImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package dev.vality.orgmanager.service; - -import dev.vality.damsel.domain.PartyContactInfo; -import dev.vality.damsel.payment_processing.PartyExists; -import dev.vality.damsel.payment_processing.PartyManagementSrv; -import dev.vality.damsel.payment_processing.PartyParams; -import dev.vality.orgmanager.exception.PartyManagementException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.thrift.TException; -import org.springframework.stereotype.Service; - -@Slf4j -@RequiredArgsConstructor -@Service -public class PartyManagementServiceImpl implements PartyManagementService { - - private final PartyManagementSrv.Iface partyManagementClient; - - @Override - public void createParty(String partyId, String userId, String email) { - PartyParams partyParams = new PartyParams(new PartyContactInfo(email)); - try { - partyManagementClient.create(partyId, partyParams); - } catch (PartyExists ex) { - log.warn("Party already exists. (partyId: {}, userId: {}, email: {})", partyId, userId, email); - } catch (TException ex) { - throw new PartyManagementException( - String.format("Exception during party creation. (partyId: %s, userId: %s, email: %s)", - partyId, userId, email), - ex); - } - - log.info("Created party. (partyId: {}, userId: {}, email: {})", partyId, userId, email); - } -} diff --git a/src/main/java/dev/vality/orgmanager/service/model/UserInfo.java b/src/main/java/dev/vality/orgmanager/service/model/UserInfo.java index c173f258..3800cf9e 100644 --- a/src/main/java/dev/vality/orgmanager/service/model/UserInfo.java +++ b/src/main/java/dev/vality/orgmanager/service/model/UserInfo.java @@ -5,7 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.util.Set; diff --git a/src/main/java/dev/vality/orgmanager/servlet/AuthContextProviderServlet.java b/src/main/java/dev/vality/orgmanager/servlet/AuthContextProviderServlet.java index b190efbe..12415208 100644 --- a/src/main/java/dev/vality/orgmanager/servlet/AuthContextProviderServlet.java +++ b/src/main/java/dev/vality/orgmanager/servlet/AuthContextProviderServlet.java @@ -6,13 +6,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import javax.servlet.GenericServlet; -import javax.servlet.Servlet; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.annotation.WebServlet; +import jakarta.servlet.GenericServlet; +import jakarta.servlet.Servlet; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.annotation.WebServlet; import java.io.IOException; import java.util.List; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 483245fc..c7941c52 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,25 +4,23 @@ server: port: '@server.rest.port@' management: - security: - flag: false server: port: ${management.port} - metrics: - export: - prometheus: - enabled: false endpoint: health: show-details: always metrics: - enabled: true + access: unrestricted prometheus: - enabled: true + access: unrestricted endpoints: web: exposure: include: health,info,prometheus + prometheus: + metrics: + export: + enabled: false spring: application: @@ -80,12 +78,8 @@ dudoser: networkTimeout: 10000 mail-from: no-reply@vality.dev -party-management: - url: http://party-management:8022/processing/partymgmt - networkTimeout: 10000 - dashboard: - url: https://dashboard.rbk.money/organization-section/accept-invitation/ + url: https://dashboard:8080 bouncer: url: http://localhost:8022/change_it diff --git a/src/test/java/dev/vality/orgmanager/TestObjectFactory.java b/src/test/java/dev/vality/orgmanager/TestObjectFactory.java index 045fea50..5cb23c22 100644 --- a/src/test/java/dev/vality/orgmanager/TestObjectFactory.java +++ b/src/test/java/dev/vality/orgmanager/TestObjectFactory.java @@ -103,7 +103,7 @@ public static InvitationRequest testInvitationRequest() { Invitee invitee = new Invitee(); InviteeContact inviteeContact = new InviteeContact(); inviteeContact.setEmail(randomString()); - inviteeContact.setType(InviteeContact.TypeEnum.EMAIL); + inviteeContact.setType(InviteeContact.TypeEnum.E_MAIL); invitee.setContact(inviteeContact); InvitationRequest invitationRequest = new InvitationRequest(); invitationRequest.setInvitee(invitee); diff --git a/src/test/java/dev/vality/orgmanager/controller/AbstractControllerTest.java b/src/test/java/dev/vality/orgmanager/controller/AbstractControllerTest.java index c5b2d809..8a96c7d2 100644 --- a/src/test/java/dev/vality/orgmanager/controller/AbstractControllerTest.java +++ b/src/test/java/dev/vality/orgmanager/controller/AbstractControllerTest.java @@ -6,19 +6,22 @@ import org.junit.jupiter.api.BeforeEach; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.context.annotation.Import; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.servlet.MockMvc; @Import(KeycloakTestConfig.class) @AutoConfigureMockMvc @AutoConfigureWireMock(port = 0) -@TestPropertySource(locations = "classpath:wiremock.properties") +@TestPropertySource( + locations = "classpath:wiremock.properties", + properties = "auth.enabled=true" +) public abstract class AbstractControllerTest extends AbstractRepositoryTest { - @SpyBean + @MockitoSpyBean protected ResourceAccessService resourceAccessService; @BeforeEach @@ -40,8 +43,8 @@ protected String generateJwt(long iat, long exp, String... roles) { return keycloakOpenIdStub.generateJwt(iat, exp, roles); } - protected String generateRbkAdminJwt() { - return keycloakOpenIdStub.generateJwt("RBKadmin"); + protected String generateAdminJwt() { + return keycloakOpenIdStub.generateJwt("admin"); } protected String getUserFromToken() { diff --git a/src/test/java/dev/vality/orgmanager/controller/JwtTokenBuilder.java b/src/test/java/dev/vality/orgmanager/controller/JwtTokenBuilder.java index df419e21..a08ae3b5 100644 --- a/src/test/java/dev/vality/orgmanager/controller/JwtTokenBuilder.java +++ b/src/test/java/dev/vality/orgmanager/controller/JwtTokenBuilder.java @@ -1,13 +1,17 @@ package dev.vality.orgmanager.controller; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; import java.security.PrivateKey; import java.time.Instant; +import java.util.List; +import java.util.Map; import java.util.UUID; public class JwtTokenBuilder { @@ -54,32 +58,31 @@ public String generateJwtWithRoles(String issuer, String... roles) { } public String generateJwtWithRoles(long iat, long exp, String issuer, String... roles) { - String payload; try { - payload = new JSONObject() - .put("jti", UUID.randomUUID().toString()) - .put("exp", exp) - .put("nbf", "0") - .put("iat", iat) - .put("iss", issuer) - .put("aud", "private-api") - .put("sub", userId) - .put("typ", "Bearer") - .put("azp", "private-api") - .put("resource_access", new JSONObject() - .put("common-api", new JSONObject() - .put("roles", new JSONArray(roles)))) - .put("preferred_username", username) - .put("email", email).toString(); - } catch (JSONException e) { - throw new RuntimeException(e); + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() + .jwtID(UUID.randomUUID().toString()) + .expirationTime(java.util.Date.from(Instant.ofEpochSecond(exp))) + .notBeforeTime(java.util.Date.from(Instant.EPOCH)) + .issueTime(java.util.Date.from(Instant.ofEpochSecond(iat))) + .issuer(issuer) + .audience("private-api") + .subject(userId) + .claim("typ", "Bearer") + .claim("azp", "private-api") + .claim("resource_access", Map.of("common-api", Map.of("roles", List.of(roles)))) + .claim("preferred_username", username) + .claim("email", email) + .build(); + + SignedJWT signedJwt = new SignedJWT( + new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build(), + claimsSet + ); + signedJwt.sign(new RSASSASigner(privateKey)); + return signedJwt.serialize(); + } catch (JOSEException e) { + throw new RuntimeException("Unable to generate JWT for test", e); } - - String jwt = Jwts.builder() - .setPayload(payload) - .signWith(SignatureAlgorithm.RS256, privateKey) - .compact(); - return jwt; } } diff --git a/src/test/java/dev/vality/orgmanager/controller/OrgsControllerTest.java b/src/test/java/dev/vality/orgmanager/controller/OrgsControllerTest.java index 64cc640a..503f0cc0 100644 --- a/src/test/java/dev/vality/orgmanager/controller/OrgsControllerTest.java +++ b/src/test/java/dev/vality/orgmanager/controller/OrgsControllerTest.java @@ -42,7 +42,7 @@ void expelOrgMemberWithErrorCallBouncer() throws Exception { mockMvc.perform(delete(String.format("/orgs/%s/members/%s", ORGANIZATION_ID, MEMBER_ID)) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isFailedDependency()); } @@ -54,7 +54,7 @@ void expelOrgMemberWithoutAccess() throws Exception { mockMvc.perform(delete(String.format("/orgs/%s/members/%s", ORGANIZATION_ID, MEMBER_ID)) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isForbidden()); } @@ -68,7 +68,7 @@ void assignMemberRoleWithoutAccess() throws Exception { mockMvc.perform(post(String.format("/orgs/%s/members/%s/roles", ORGANIZATION_ID, MEMBER_ID)) .contentType("application/json") .content(objectMapper.writeValueAsString(memberRole)) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isForbidden()); } @@ -92,7 +92,7 @@ void assignMemberRoleTest() throws Exception { mockMvc.perform(post(String.format("/orgs/%s/members/%s/roles", savedOrganization.getId(), savedMember.getId())) .contentType("application/json") .content(objectMapper.writeValueAsString(memberRole)) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").isNotEmpty()) @@ -117,7 +117,7 @@ void expelOrgMemberTest() throws Exception { mockMvc.perform(delete(String.format("/orgs/%s/members/%s", savedOrganization.getId(), savedMember.getId())) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isNoContent()); @@ -144,7 +144,7 @@ void removeMemberRoleWithOnlyOneRole() throws Exception { savedMemberRole.getId()) ) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isUnprocessableEntity()); } @@ -167,7 +167,7 @@ void removeMemberRole() throws Exception { roleToRemove.getId()) ) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isNoContent()); @@ -188,7 +188,7 @@ void createInvitationWithoutAccess() throws Exception { mockMvc.perform(post(String.format("/orgs/%s/invitations", ORGANIZATION_ID)) .contentType("application/json") .content(body) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isForbidden()); } @@ -203,7 +203,7 @@ void createInvitationTest() throws Exception { mockMvc.perform(post(String.format("/orgs/%s/invitations", ORGANIZATION_ID)) .contentType("application/json") .content(body) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(jsonPath("$.status", is("Pending"))) .andExpect(jsonPath("$.acceptToken").doesNotExist()); @@ -220,7 +220,7 @@ void listOrgMembersTest() throws Exception { mockMvc.perform(get(String.format("/orgs/%s/members", savedOrganization.getId())) .contentType("application/json") - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isOk()) .andExpect(jsonPath("$.result", anything())); diff --git a/src/test/java/dev/vality/orgmanager/controller/UserControllerTest.java b/src/test/java/dev/vality/orgmanager/controller/UserControllerTest.java index 52d053c2..18e7d906 100644 --- a/src/test/java/dev/vality/orgmanager/controller/UserControllerTest.java +++ b/src/test/java/dev/vality/orgmanager/controller/UserControllerTest.java @@ -38,7 +38,7 @@ void joinOrgTestWithResourceNotFound() throws Exception { mockMvc.perform(post("/user/membership") .contentType("application/json") .content(objectMapper.writeValueAsString(organizationJoinRequest)) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isNotFound()); } @@ -53,14 +53,14 @@ void joinOrgTestWithoutAccess() throws Exception { mockMvc.perform(post("/user/membership") .contentType("application/json") .content(objectMapper.writeValueAsString(organizationJoinRequest)) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().isForbidden()); } @Test void joinOrgTestWrongUserEmail() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); OrganizationEntity savedOrg = organizationRepository.save(buildOrganization()); InvitationEntity savedInvitation = invitationRepository.save(buildInvitation(savedOrg.getId())); OrganizationJoinRequest organizationJoinRequest = new OrganizationJoinRequest(); @@ -88,14 +88,14 @@ void joinOrgTestInviteAlreadyAccepted() throws Exception { mockMvc.perform(post("/user/membership") .contentType("application/json") .content(objectMapper.writeValueAsString(organizationJoinRequest)) - .header("Authorization", "Bearer " + generateRbkAdminJwt()) + .header("Authorization", "Bearer " + generateAdminJwt()) .header("X-Request-ID", "testRequestId")) .andExpect(status().is(422)); } @Test void joinOrgNewMemberTest() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); OrganizationEntity savedOrg = organizationRepository.save(buildOrganization()); InvitationEntity savedInvitation = invitationRepository.save(buildInvitation(savedOrg.getId(), DEFAULT_EMAIL)); @@ -126,7 +126,7 @@ void joinOrgNewMemberTest() throws Exception { @Test void joinOrgExistMemberTest() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); memberRepository.save(testMemberEntity(userId)); OrganizationEntity savedOrg = organizationRepository.save(buildOrganization()); @@ -160,7 +160,7 @@ void joinOrgExistMemberTest() throws Exception { @Test @Transactional void cancelOrgMembershipTest() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); MemberEntity member = memberRepository.save(testMemberEntity(userId)); OrganizationEntity orgWithMember = organizationRepository.save(buildOrganization(member)); @@ -177,7 +177,7 @@ void cancelOrgMembershipTest() throws Exception { @Test void inquireOrgMembershipTest() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); MemberEntity member = memberRepository.save(testMemberEntity(userId)); OrganizationEntity orgWithMember = organizationRepository.save(buildOrganization(member)); @@ -200,7 +200,7 @@ void inquireOrgMembershipTest() throws Exception { @Test @Transactional void listOrgMembershipAfterCancel() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); MemberEntity member = memberRepository.save(testMemberEntity(userId)); OrganizationEntity orgWithMember = organizationRepository.save(buildOrganization(member)); @@ -238,7 +238,7 @@ void listOrgMembershipAfterCancel() throws Exception { @Test void listOrgMembershipWithoutLimitTest() throws Exception { - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); String userId = getUserFromToken(); MemberEntity targetMember = memberRepository.save(testMemberEntity(userId)); Set targetEntities = buildOrganization(targetMember, 7); @@ -267,7 +267,7 @@ void listOrgMembershipTest() throws Exception { targetEntities.addAll(List.of(anotherOrganization, organizationWithOwner)); organizationRepository.saveAll(targetEntities); String limit = "4"; - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); MvcResult mvcResultFirst = mockMvc.perform(get("/user/membership") .queryParam("limit", limit) @@ -315,7 +315,7 @@ void switchOrganizationWithNotExistMember() throws Exception { OrganizationEntity organizationEntity = organizationRepository.save(buildOrganization()); OrganizationSwitchRequest organizationSwitchRequest = new OrganizationSwitchRequest(); organizationSwitchRequest.setOrganizationId(organizationEntity.getId()); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); mockMvc.perform(put("/user/context") .accept(MediaType.APPLICATION_JSON) @@ -332,7 +332,7 @@ void switchOrganizationWithNewContextCreation() throws Exception { String userId = getUserFromToken(); MemberEntity memberEntity = memberRepository.save(testMemberEntity(userId)); OrganizationEntity organizationEntity = organizationRepository.save(buildOrganization()); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); OrganizationSwitchRequest organizationSwitchRequest = new OrganizationSwitchRequest(); organizationSwitchRequest.setOrganizationId(organizationEntity.getId()); @@ -358,7 +358,7 @@ void switchOrganizationWithOwnerUser() throws Exception { OrganizationEntity organization = buildOrganization(); organization.setOwner(userId); OrganizationEntity organizationEntity = organizationRepository.save(organization); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); OrganizationSwitchRequest organizationSwitchRequest = new OrganizationSwitchRequest(); organizationSwitchRequest.setOrganizationId(organizationEntity.getId()); @@ -382,7 +382,7 @@ void switchOrganizationWithOwnerUser() throws Exception { void switchOrganizationOnUnknown() throws Exception { String userId = getUserFromToken(); MemberEntity memberEntity = memberRepository.save(testMemberEntity(userId)); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); OrganizationSwitchRequest organizationSwitchRequest = new OrganizationSwitchRequest(); organizationSwitchRequest.setOrganizationId("testOrgId"); @@ -408,7 +408,7 @@ void switchOrganizationWithExistsContext() throws Exception { .build() ); OrganizationEntity newOrganizationEntity = organizationRepository.save(buildOrganization()); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); OrganizationSwitchRequest organizationSwitchRequest = new OrganizationSwitchRequest(); organizationSwitchRequest.setOrganizationId(newOrganizationEntity.getId()); @@ -438,7 +438,7 @@ void getMemberContext() throws Exception { .organizationEntity(organizationEntity) .build() ); - String jwtToken = generateRbkAdminJwt(); + String jwtToken = generateAdminJwt(); mockMvc.perform(get("/user/context") .accept(MediaType.APPLICATION_JSON) diff --git a/src/test/java/dev/vality/orgmanager/converter/InvitationConverterTest.java b/src/test/java/dev/vality/orgmanager/converter/InvitationConverterTest.java index ac4f2a8d..6f45c357 100644 --- a/src/test/java/dev/vality/orgmanager/converter/InvitationConverterTest.java +++ b/src/test/java/dev/vality/orgmanager/converter/InvitationConverterTest.java @@ -47,7 +47,7 @@ void shouldConvertToEntity() throws Exception { InvitationRequest invitation = new InvitationRequest() .invitee(new Invitee() .contact(new InviteeContact() - .type(InviteeContact.TypeEnum.EMAIL) + .type(InviteeContact.TypeEnum.E_MAIL) .email("email")) .roles(List.of(new MemberRole()))) .metadata(Map.of("a", "b")); @@ -89,7 +89,7 @@ void shouldConvertToDomain() { .createdAt(OffsetDateTime.parse("2019-08-24T14:15:22Z")) .invitee(new Invitee() .contact(new InviteeContact() - .type(InviteeContact.TypeEnum.EMAIL) + .type(InviteeContact.TypeEnum.E_MAIL) .email("email")) .roles(List.of(new MemberRole()))) .metadata(Map.of("a", "b")); diff --git a/src/test/java/dev/vality/orgmanager/repository/AbstractRepositoryTest.java b/src/test/java/dev/vality/orgmanager/repository/AbstractRepositoryTest.java index 2b05c7ab..8e8ea355 100644 --- a/src/test/java/dev/vality/orgmanager/repository/AbstractRepositoryTest.java +++ b/src/test/java/dev/vality/orgmanager/repository/AbstractRepositoryTest.java @@ -9,11 +9,13 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.TestPropertySource; import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.utility.DockerImageName; @ActiveProfiles("test") +@TestPropertySource(properties = "auth.enabled=false") @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {OrgManagerApplication.class}) diff --git a/src/test/java/dev/vality/orgmanager/service/InvitationServiceTest.java b/src/test/java/dev/vality/orgmanager/service/InvitationServiceTest.java index d240f434..75c9ef69 100644 --- a/src/test/java/dev/vality/orgmanager/service/InvitationServiceTest.java +++ b/src/test/java/dev/vality/orgmanager/service/InvitationServiceTest.java @@ -189,9 +189,9 @@ void shouldRevoke() { .thenReturn(Optional.of(entity)); // When - service.revoke(orgId, invitationId, new InlineObject1() + service.revoke(orgId, invitationId, new RevokeInvitationRequest() .reason("reason") - .status(InlineObject1.StatusEnum.REVOKED)); + .status(RevokeInvitationRequest.StatusEnum.REVOKED)); // Then assertThat(entity.getStatus()) @@ -210,7 +210,8 @@ void shouldThrowNotFoundIfInvitationDoesNotExist() { when(invitationRepository.findByIdAndOrganizationId(invitationId, orgId)) .thenReturn(Optional.empty()); - assertThrows(ResourceNotFoundException.class, () -> service.revoke(orgId, invitationId, new InlineObject1())); + assertThrows(ResourceNotFoundException.class, + () -> service.revoke(orgId, invitationId, new RevokeInvitationRequest())); } @Test diff --git a/src/test/java/dev/vality/orgmanager/service/OrganizationServiceTest.java b/src/test/java/dev/vality/orgmanager/service/OrganizationServiceTest.java index fdfaaebb..37466805 100644 --- a/src/test/java/dev/vality/orgmanager/service/OrganizationServiceTest.java +++ b/src/test/java/dev/vality/orgmanager/service/OrganizationServiceTest.java @@ -40,41 +40,12 @@ class OrganizationServiceTest { private MemberConverter memberConverter; @Mock private MemberRepository memberRepository; - @Mock - private PartyManagementService partyManagementService; - @InjectMocks private OrganizationService service; private static final String OWNER_ID = "testOwnerId"; private static final String EMAIL = "email@email.org"; - @Test - void shouldThrowPartyManagementExceptionOnCreate() { - Organization organization = new Organization(); - OrganizationEntity entity = new OrganizationEntity(); - OrganizationEntity savedEntity = TestObjectFactory.buildOrganization(); - - when(organizationConverter.toEntity(organization, OWNER_ID)) - .thenReturn(entity); - when(organizationRepository.save(entity)) - .thenReturn(savedEntity); - doThrow(new PartyManagementException()) - .when(partyManagementService).createParty(anyString(), anyString(), anyString()); - - assertThrows(PartyManagementException.class, - () -> service.create(testToken(OWNER_ID, EMAIL), organization, "")); - - verify(organizationConverter, times(1)) - .toEntity(organization, OWNER_ID); - verify(organizationRepository, times(1)) - .save(entity); - verify(partyManagementService, times(1)) - .createParty(savedEntity.getParty(), OWNER_ID, EMAIL); - verify(organizationConverter, times(0)) - .toDomain(any(OrganizationEntity.class)); - } - @Test void shouldCreate() { Organization organization = new Organization(); @@ -96,8 +67,6 @@ void shouldCreate() { .toEntity(organization, OWNER_ID); verify(organizationRepository, times(1)) .save(entity); - verify(partyManagementService, times(1)) - .createParty(savedEntity.getParty(), OWNER_ID, EMAIL); verify(organizationConverter, times(1)) .toDomain(savedEntity); assertThat(response) diff --git a/src/test/java/dev/vality/orgmanager/service/PartyManagementServiceImplTest.java b/src/test/java/dev/vality/orgmanager/service/PartyManagementServiceImplTest.java deleted file mode 100644 index 1a02b8bd..00000000 --- a/src/test/java/dev/vality/orgmanager/service/PartyManagementServiceImplTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package dev.vality.orgmanager.service; - -import dev.vality.damsel.payment_processing.PartyExists; -import dev.vality.damsel.payment_processing.PartyManagementSrv; -import dev.vality.damsel.payment_processing.PartyParams; -import dev.vality.orgmanager.exception.PartyManagementException; -import org.apache.thrift.TException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static dev.vality.orgmanager.TestObjectFactory.randomString; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class PartyManagementServiceImplTest { - - private PartyManagementService partyManagementService; - - @Mock - private PartyManagementSrv.Iface partyManagementClient; - - @BeforeEach - void setUp() { - partyManagementService = new PartyManagementServiceImpl(partyManagementClient); - } - - @Test - void shouldThrowPartyManagementExceptionOnCreateParty() throws TException { - doThrow(new TException()) - .when(partyManagementClient).create(anyString(), any(PartyParams.class)); - String partyId = randomString(); - String userId = randomString(); - String email = randomString(); - - var exception = - assertThrows( - PartyManagementException.class, - () -> partyManagementService.createParty(partyId, userId, email) - ); - - assertTrue(exception.getMessage().contains(String.format( - "Exception during party creation. (partyId: %s, userId: %s, email: %s)", partyId, userId, email))); - } - - @Test - void shouldCreatePartyIfPartyExistThrown() throws TException { - doThrow(new PartyExists()) - .when(partyManagementClient).create(anyString(), any(PartyParams.class)); - String partyId = randomString(); - String userId = randomString(); - String email = randomString(); - - partyManagementService.createParty(partyId, userId, email); - - verify(partyManagementClient, times(1)) - .create(anyString(), any(PartyParams.class)); - } - - @Test - void shouldCreateParty() throws TException { - String partyId = randomString(); - String userId = randomString(); - String email = randomString(); - - partyManagementService.createParty(partyId, userId, email); - - ArgumentCaptor partyIdCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor partyParamsCaptor = ArgumentCaptor.forClass(PartyParams.class); - - verify(partyManagementClient, times(1)) - .create(partyIdCaptor.capture(), partyParamsCaptor.capture()); - - assertEquals(1, partyIdCaptor.getAllValues().size()); - assertEquals(partyId, partyIdCaptor.getValue()); - - assertEquals(1, partyParamsCaptor.getAllValues().size()); - assertEquals(email, partyParamsCaptor.getValue().getContactInfo().getEmail()); - } -} diff --git a/src/test/java/dev/vality/orgmanager/util/TestData.java b/src/test/java/dev/vality/orgmanager/util/TestData.java index 4f8ad71c..45ad6d33 100644 --- a/src/test/java/dev/vality/orgmanager/util/TestData.java +++ b/src/test/java/dev/vality/orgmanager/util/TestData.java @@ -61,7 +61,7 @@ public static InvitationEntity buildInvitation( .createdAt(LocalDateTime.now()) .expiresAt(expiresAt) .inviteeContactEmail("contactEmail") - .inviteeContactType(InviteeContact.TypeEnum.EMAIL.getValue()) + .inviteeContactType(InviteeContact.TypeEnum.E_MAIL.getValue()) .organizationId(organizationId) .status(statusName.getValue()) .inviteeRoles(Set.of( @@ -85,7 +85,7 @@ public static InvitationEntity buildInvitation( public static InvitationRequest buildInvitationRequest() { InviteeContact inviteeContact = new InviteeContact(); inviteeContact.setEmail("testEmail@mail.ru"); - inviteeContact.setType(InviteeContact.TypeEnum.EMAIL); + inviteeContact.setType(InviteeContact.TypeEnum.E_MAIL); Invitee invitee = new Invitee(); invitee.setContact(inviteeContact); invitee.setRoles(List.of(buildMemberRole()));