From 04758ab49eac44e54a2ed62526fd4ff85251b7d0 Mon Sep 17 00:00:00 2001 From: Thomas Schaffter Date: Mon, 7 Oct 2024 16:15:32 -0700 Subject: [PATCH] chore(openchallenges): remove projects related to auth and Keycloak (#2885) --- .devcontainer/devcontainer.json | 19 +- apps/openchallenges/api-gateway/.env.example | 1 - apps/openchallenges/api-gateway/build.gradle | 2 - apps/openchallenges/api-gateway/requests.http | 48 +- .../api/gateway/config/SecurityConfig.java | 6 +- .../src/app/initialize-keycloak.factory.ts | 35 - apps/openchallenges/auth-service/.env.example | 2 - apps/openchallenges/auth-service/.gitignore | 32 - apps/openchallenges/auth-service/build.gradle | 49 - .../auth-service/docker-compose.yml | 18 - .../auth-service/gradle.properties | 6 - .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - apps/openchallenges/auth-service/gradlew | 240 -- apps/openchallenges/auth-service/project.json | 71 - .../openchallenges/auth-service/requests.http | 11 - .../auth-service/settings.gradle | 1 - .../ChallengeAuthServiceApplication.java | 22 - .../configuration/KeycloakConfiguration.java | 15 - .../configuration/SecurityConfiguration.java | 81 - .../challenge/controller/LoginController.java | 36 - .../CustomKeycloakAuthenticationHandler.java | 37 - .../exception/GlobalControllerAdvice.java | 15 - .../exception/RestAccessDeniedHandler.java | 22 - .../challenge/model/dto/LoginRequest.java | 10 - .../challenge/model/dto/LoginResponse.java | 13 - .../challenge/service/LoginService.java | 57 - .../src/main/resources/application.yml | 41 - .../ChallengeAuthServiceApplicationTests.java | 11 - .../challenge-service/.env.example | 1 - .../challenge-service/build.gradle | 2 - .../challenge-service/gradle.properties | 1 - .../configuration/KeycloakConfiguration.java | 15 - .../configuration/KeycloakManager.java | 36 - .../KeycloakManagerProperties.java | 16 - .../configuration/SecurityConfiguration.java | 64 +- .../openchallenges/config-server/.env.example | 2 +- apps/openchallenges/core-service/.env.example | 2 - apps/openchallenges/core-service/.gitignore | 32 - apps/openchallenges/core-service/build.gradle | 56 - .../core-service/docker-compose.yml | 18 - .../core-service/gradle.properties | 6 - .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - apps/openchallenges/core-service/gradlew | 240 -- apps/openchallenges/core-service/project.json | 63 - .../openchallenges/core-service/requests.http | 65 - .../core-service/settings.gradle | 1 - .../ChallengeCoreServiceApplication.java | 14 - .../controller/AccountController.java | 28 - .../challenge/controller/UserController.java | 30 - .../exception/EntityNotFoundException.java | 14 - .../challenge/exception/GlobalErrorCode.java | 7 - .../exception/GlobalExceptionHandler.java | 32 - .../challenge/model/AccountStatus.java | 8 - .../challenge/model/AccountType.java | 6 - .../challenge/model/dto/ChallengeAccount.java | 18 - .../challenge/model/dto/User.java | 15 - .../model/entity/ChallengeAccountEntity.java | 35 - .../challenge/model/entity/UserEntity.java | 25 - .../model/mapper/ChallengeAccountMapper.java | 27 - .../challenge/model/mapper/UserMapper.java | 31 - .../ChallengeAccountRepository.java | 9 - .../challenge/repository/UserRepository.java | 9 - .../challenge/service/AccountService.java | 22 - .../challenge/service/UserService.java | 31 - .../src/main/resources/application.yml | 33 - ...427174638__create_base_table_structure.sql | 27 - .../V1.0.20210427174721__temp_data.sql | 25 - .../ChallengeCoreServiceApplicationTests.java | 11 - .../image-service/gradle.properties | 1 - apps/openchallenges/keycloak/.env.example | 11 - apps/openchallenges/keycloak/.gitignore | 3 - apps/openchallenges/keycloak/Dockerfile | 1 - apps/openchallenges/keycloak/README.md | 54 - apps/openchallenges/keycloak/data/h2/.gitkeep | 0 .../keycloak/data/import/master-realm.json | 2245 --------------- .../keycloak/data/import/master-users-0.json | 27 - .../keycloak/data/import/test-realm.json | 2408 ----------------- .../keycloak/data/import/test-users-0.json | 184 -- .../keycloak/docker-compose.yml | 25 - apps/openchallenges/keycloak/project.json | 61 - apps/openchallenges/keycloak/requests.http | 22 - .../themes/challenge-registry/README.md | 1 - .../themes/challenge-registry/login/terms.ftl | 15 - .../challenge-registry/login/theme.properties | 2 - .../organization-service/.env.example | 1 - .../organization-service/build.gradle | 4 - .../organization-service/gradle.properties | 1 - .../configuration/KeycloakConfiguration.java | 15 - .../configuration/KeycloakManager.java | 36 - .../KeycloakManagerProperties.java | 16 - .../configuration/SecurityConfiguration.java | 64 +- apps/openchallenges/postgres/.env.example | 3 - apps/openchallenges/postgres/Dockerfile | 1 - .../postgres/docker-compose.yml | 27 - .../docker-entrypoint-initdb.d/init-db.sql | 9 - apps/openchallenges/postgres/project.json | 41 - apps/openchallenges/user-service/.env.example | 3 - apps/openchallenges/user-service/.gitignore | 38 - .../user-service/.openapi-generator-ignore | 28 - .../user-service/.openapi-generator/FILES | 18 - .../user-service/.openapi-generator/VERSION | 1 - apps/openchallenges/user-service/AUTHORS.md | 11 - apps/openchallenges/user-service/build.gradle | 80 - .../user-service/build.gradle.old | 108 - .../user-service/docker-compose.yml | 18 - .../user-service/docs/openapi.yaml | 150 - .../user-service/gradle.properties | 9 - .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - apps/openchallenges/user-service/gradlew | 240 -- .../user-service/openapitools.json | 27 - apps/openchallenges/user-service/project.json | 73 - .../openchallenges/user-service/requests.http | 65 - apps/openchallenges/user-service/session.sql | 17 - .../user-service/settings.gradle | 1 - .../ChallengeUserServiceApplicationTests.java | 11 - .../UserRepositoryIntegrationTest.java | 69 - .../integrationTest/resources/application.yml | 55 - .../ChallengeUserServiceApplication.java | 38 - .../CustomFeignClientConfiguration.java | 15 - .../CustomFeignErrorDecoder.java | 66 - .../configuration/KeycloakConfiguration.java | 15 - .../configuration/KeycloakManager.java | 36 - .../KeycloakManagerProperties.java | 16 - .../configuration/OpenApiConfiguration.java | 30 - .../configuration/SecurityConfiguration.java | 69 - .../challenge/controller/UserController.java | 53 - .../challenge/exception/GlobalErrorCode.java | 9 - .../exception/InvalidEmailException.java | 10 - .../exception/InvalidUserException.java | 10 - .../UserAlreadyRegisteredException.java | 10 - .../challenge/model/dto/User.java | 25 - .../challenge/model/dto/UserStatus.java | 8 - .../model/dto/UserUpdateRequest.java | 9 - .../challenge/model/entity/UserEntity.java | 37 - .../challenge/model/mapper/UserMapper.java | 27 - .../model/repository/UserRepository.java | 6 - .../model/rest/response/AccountResponse.java | 14 - .../model/rest/response/UserResponse.java | 16 - .../service/KeycloakUserService.java | 54 - .../challenge/service/UserService.java | 104 - .../service/rest/ChallengeCoreRestClient.java | 17 - .../src.old/main/resources/application.yml | 72 - ...V1.0.20210427174638__create_user_table.sql | 13 - .../V1.0.20210427174721__temp_data.sql | 7 - .../controller/UserControllerTest.java | 58 - .../challenge/service/UserServiceTest.java | 94 - .../src.old/test/resources/application.yml | 23 - .../ChallengeUserServiceApplication.java | 29 - .../challenge/RFC3339DateFormat.java | 38 - .../challenge/api/ApiUtil.java | 19 - .../challenge/api/UserApi.java | 339 --- .../challenge/api/UserApiController.java | 23 - .../challenge/api/UserApiDelegate.java | 124 - .../challenge/api/UserApiDelegateImpl.java | 35 - .../configuration/HomeController.java | 14 - .../configuration/KeycloakConfiguration.java | 15 - .../configuration/KeycloakManager.java | 36 - .../KeycloakManagerProperties.java | 16 - .../configuration/SecurityConfiguration.java | 71 - .../configuration/SpringDocConfiguration.java | 33 - .../challenge/exception/ErrorConstants.java | 22 - .../exception/GlobalExceptionHandler.java | 40 - .../exception/InvalidEmailException.java | 13 - .../exception/InvalidUserException.java | 13 - .../SimpleChallengeGlobalException.java | 23 - .../exception/UserNotFoundException.java | 13 - .../UsernameAlreadyExistsException.java | 13 - .../challenge/model/dto/BasicErrorDto.java | 163 -- .../challenge/model/dto/PageMetadataDto.java | 109 - .../model/dto/PageMetadataPagingDto.java | 78 - .../model/dto/UserCreateRequestDto.java | 229 -- .../model/dto/UserCreateResponseDto.java | 84 - .../challenge/model/dto/UserDto.java | 337 --- .../challenge/model/dto/UserStatusDto.java | 45 - .../challenge/model/dto/UsersPageDto.java | 143 - .../challenge/model/entity/UserEntity.java | 44 - .../challenge/model/mapper/UserMapper.java | 27 - .../model/repository/UserRepository.java | 6 - .../model/rest/response/AccountResponse.java | 14 - .../model/rest/response/UserResponse.java | 16 - .../service/KeycloakUserService.java | 54 - .../challenge/service/UserService.java | 120 - .../src/main/resources/application.yml | 69 - ...V1.0.20210427174638__create_user_table.sql | 13 - .../V1.0.20210427174721__temp_data.sql | 7 - .../src/main/resources/openapi.yaml | 442 --- .../OpenApiGeneratorApplicationTests.java | 11 - .../user-service/templates/AUTHORS.md | 11 - .../user-service/templates/config.yaml | 2 - .../templates/openapi2SpringBoot.mustache | 27 - .../user-service/templates/pojo.mustache | 254 -- .../v6.1.x/openapi2SpringBoot.mustache | 27 - .../templates/v6.1.x/pojo.mustache | 248 -- .../user-service/tools/pull-templates.sh | 11 - apps/openchallenges/user-service/users.http | 30 - .../config/src/lib/app.config.ts | 1 - package.json | 2 - pnpm-lock.yaml | 43 +- tools/configure-hostnames.sh | 4 - 202 files changed, 33 insertions(+), 12821 deletions(-) delete mode 100644 apps/openchallenges/app/src/app/initialize-keycloak.factory.ts delete mode 100644 apps/openchallenges/auth-service/.env.example delete mode 100644 apps/openchallenges/auth-service/.gitignore delete mode 100644 apps/openchallenges/auth-service/build.gradle delete mode 100644 apps/openchallenges/auth-service/docker-compose.yml delete mode 100644 apps/openchallenges/auth-service/gradle.properties delete mode 100644 apps/openchallenges/auth-service/gradle/wrapper/gradle-wrapper.jar delete mode 100644 apps/openchallenges/auth-service/gradle/wrapper/gradle-wrapper.properties delete mode 100755 apps/openchallenges/auth-service/gradlew delete mode 100644 apps/openchallenges/auth-service/project.json delete mode 100644 apps/openchallenges/auth-service/requests.http delete mode 100644 apps/openchallenges/auth-service/settings.gradle delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplication.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/controller/LoginController.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/CustomKeycloakAuthenticationHandler.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalControllerAdvice.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/RestAccessDeniedHandler.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginRequest.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginResponse.java delete mode 100644 apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/service/LoginService.java delete mode 100644 apps/openchallenges/auth-service/src/main/resources/application.yml delete mode 100644 apps/openchallenges/auth-service/src/test/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplicationTests.java delete mode 100644 apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakConfiguration.java delete mode 100644 apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManager.java delete mode 100644 apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManagerProperties.java delete mode 100644 apps/openchallenges/core-service/.env.example delete mode 100644 apps/openchallenges/core-service/.gitignore delete mode 100644 apps/openchallenges/core-service/build.gradle delete mode 100644 apps/openchallenges/core-service/docker-compose.yml delete mode 100644 apps/openchallenges/core-service/gradle.properties delete mode 100644 apps/openchallenges/core-service/gradle/wrapper/gradle-wrapper.jar delete mode 100644 apps/openchallenges/core-service/gradle/wrapper/gradle-wrapper.properties delete mode 100755 apps/openchallenges/core-service/gradlew delete mode 100644 apps/openchallenges/core-service/project.json delete mode 100644 apps/openchallenges/core-service/requests.http delete mode 100644 apps/openchallenges/core-service/settings.gradle delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplication.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/AccountController.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/UserController.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/EntityNotFoundException.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountStatus.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountType.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/ChallengeAccount.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/User.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/ChallengeAccountEntity.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/ChallengeAccountMapper.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/ChallengeAccountRepository.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/UserRepository.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/AccountService.java delete mode 100644 apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java delete mode 100644 apps/openchallenges/core-service/src/main/resources/application.yml delete mode 100644 apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174638__create_base_table_structure.sql delete mode 100644 apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql delete mode 100644 apps/openchallenges/core-service/src/test/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplicationTests.java delete mode 100644 apps/openchallenges/keycloak/.env.example delete mode 100644 apps/openchallenges/keycloak/.gitignore delete mode 100644 apps/openchallenges/keycloak/Dockerfile delete mode 100644 apps/openchallenges/keycloak/README.md delete mode 100644 apps/openchallenges/keycloak/data/h2/.gitkeep delete mode 100644 apps/openchallenges/keycloak/data/import/master-realm.json delete mode 100644 apps/openchallenges/keycloak/data/import/master-users-0.json delete mode 100644 apps/openchallenges/keycloak/data/import/test-realm.json delete mode 100644 apps/openchallenges/keycloak/data/import/test-users-0.json delete mode 100644 apps/openchallenges/keycloak/docker-compose.yml delete mode 100644 apps/openchallenges/keycloak/project.json delete mode 100644 apps/openchallenges/keycloak/requests.http delete mode 100644 apps/openchallenges/keycloak/themes/challenge-registry/README.md delete mode 100644 apps/openchallenges/keycloak/themes/challenge-registry/login/terms.ftl delete mode 100644 apps/openchallenges/keycloak/themes/challenge-registry/login/theme.properties delete mode 100644 apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakConfiguration.java delete mode 100644 apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManager.java delete mode 100644 apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManagerProperties.java delete mode 100644 apps/openchallenges/postgres/.env.example delete mode 100644 apps/openchallenges/postgres/Dockerfile delete mode 100644 apps/openchallenges/postgres/docker-compose.yml delete mode 100644 apps/openchallenges/postgres/docker-entrypoint-initdb.d/init-db.sql delete mode 100644 apps/openchallenges/postgres/project.json delete mode 100644 apps/openchallenges/user-service/.env.example delete mode 100644 apps/openchallenges/user-service/.gitignore delete mode 100644 apps/openchallenges/user-service/.openapi-generator-ignore delete mode 100644 apps/openchallenges/user-service/.openapi-generator/FILES delete mode 100644 apps/openchallenges/user-service/.openapi-generator/VERSION delete mode 100644 apps/openchallenges/user-service/AUTHORS.md delete mode 100644 apps/openchallenges/user-service/build.gradle delete mode 100644 apps/openchallenges/user-service/build.gradle.old delete mode 100644 apps/openchallenges/user-service/docker-compose.yml delete mode 100644 apps/openchallenges/user-service/docs/openapi.yaml delete mode 100644 apps/openchallenges/user-service/gradle.properties delete mode 100644 apps/openchallenges/user-service/gradle/wrapper/gradle-wrapper.jar delete mode 100644 apps/openchallenges/user-service/gradle/wrapper/gradle-wrapper.properties delete mode 100755 apps/openchallenges/user-service/gradlew delete mode 100644 apps/openchallenges/user-service/openapitools.json delete mode 100644 apps/openchallenges/user-service/project.json delete mode 100644 apps/openchallenges/user-service/requests.http delete mode 100644 apps/openchallenges/user-service/session.sql delete mode 100644 apps/openchallenges/user-service/settings.gradle delete mode 100644 apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/ChallengeUserServiceApplicationTests.java delete mode 100644 apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/model/repository/UserRepositoryIntegrationTest.java delete mode 100644 apps/openchallenges/user-service/src.old/integrationTest/resources/application.yml delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignClientConfiguration.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignErrorDecoder.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/OpenApiConfiguration.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/controller/UserController.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/UserAlreadyRegisteredException.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/User.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserStatus.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserUpdateRequest.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/UserService.java delete mode 100644 apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/rest/ChallengeCoreRestClient.java delete mode 100644 apps/openchallenges/user-service/src.old/main/resources/application.yml delete mode 100644 apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql delete mode 100644 apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174721__temp_data.sql delete mode 100644 apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/controller/UserControllerTest.java delete mode 100644 apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/service/UserServiceTest.java delete mode 100644 apps/openchallenges/user-service/src.old/test/resources/application.yml delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/RFC3339DateFormat.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/ApiUtil.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApi.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiController.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegate.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegateImpl.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/HomeController.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SpringDocConfiguration.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/ErrorConstants.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/SimpleChallengeGlobalException.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UserNotFoundException.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UsernameAlreadyExistsException.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/BasicErrorDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataPagingDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateRequestDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateResponseDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserStatusDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UsersPageDto.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java delete mode 100644 apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java delete mode 100644 apps/openchallenges/user-service/src/main/resources/application.yml delete mode 100644 apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql delete mode 100644 apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql delete mode 100644 apps/openchallenges/user-service/src/main/resources/openapi.yaml delete mode 100644 apps/openchallenges/user-service/src/test/java/org/sagebionetworks/challenge/OpenApiGeneratorApplicationTests.java delete mode 100644 apps/openchallenges/user-service/templates/AUTHORS.md delete mode 100644 apps/openchallenges/user-service/templates/config.yaml delete mode 100644 apps/openchallenges/user-service/templates/openapi2SpringBoot.mustache delete mode 100644 apps/openchallenges/user-service/templates/pojo.mustache delete mode 100644 apps/openchallenges/user-service/templates/v6.1.x/openapi2SpringBoot.mustache delete mode 100644 apps/openchallenges/user-service/templates/v6.1.x/pojo.mustache delete mode 100755 apps/openchallenges/user-service/tools/pull-templates.sh delete mode 100644 apps/openchallenges/user-service/users.http diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cc4c1c146f..8a8aaf0b11 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -68,8 +68,7 @@ }, "forwardPorts": [ 2432, 3000, 3306, 3333, 4200, 4211, 5200, 5432, 5601, 7010, 7443, 7200, 7888, 8010, 8071, 8000, - 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8090, 8091, 8092, 8200, 8787, 8888, 8889, 9090, 9104, - 9200, 9411, 27017 + 8080, 8081, 8082, 8084, 8085, 8086, 8090, 8200, 8787, 8888, 8889, 9090, 9104, 9200, 9411, 27017 ], "portsAttributes": { "2432": { @@ -140,10 +139,6 @@ "label": "openchallenges-schema-registry", "onAutoForward": "silent" }, - "8080": { - "label": "openchallenges-keycloak", - "onAutoForward": "silent" - }, "8081": { "label": "openchallenges-service-registry", "onAutoForward": "silent" @@ -152,10 +147,6 @@ "label": "openchallenges-api-gateway", "onAutoForward": "silent" }, - "8083": { - "label": "openchallenges-user-service", - "onAutoForward": "silent" - }, "8084": { "label": "openchallenges-organization-service", "onAutoForward": "silent" @@ -172,14 +163,6 @@ "label": "openchallenges-config-server", "onAutoForward": "silent" }, - "8091": { - "label": "openchallenges-auth-service", - "onAutoForward": "silent" - }, - "8092": { - "label": "openchallenges-core-service", - "onAutoForward": "silent" - }, "8787": { "label": "openchallenges-rstudio", "onAutoForward": "silent" diff --git a/apps/openchallenges/api-gateway/.env.example b/apps/openchallenges/api-gateway/.env.example index 979135756f..8f9221bd72 100644 --- a/apps/openchallenges/api-gateway/.env.example +++ b/apps/openchallenges/api-gateway/.env.example @@ -1,3 +1,2 @@ SERVER_PORT=8082 -KEYCLOAK_URL=http://openchallenges-keycloak:8080 SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka \ No newline at end of file diff --git a/apps/openchallenges/api-gateway/build.gradle b/apps/openchallenges/api-gateway/build.gradle index 95f99973df..4adb4c8ae0 100644 --- a/apps/openchallenges/api-gateway/build.gradle +++ b/apps/openchallenges/api-gateway/build.gradle @@ -19,8 +19,6 @@ dependencies { implementation "org.sagebionetworks.openchallenges:openchallenges-app-config-data:${openchallengesVersion}" implementation "org.springframework.boot:spring-boot-devtools:${springBootVersion}" implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-oauth2-client:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server:${springBootVersion}" implementation "org.springframework.boot:spring-boot-starter-security:${springBootVersion}" implementation "org.springframework.cloud:spring-cloud-sleuth-zipkin:${springCloudVersion}" implementation "org.springframework.cloud:spring-cloud-starter-config:${springCloudVersion}" diff --git a/apps/openchallenges/api-gateway/requests.http b/apps/openchallenges/api-gateway/requests.http index 8d88bd4e50..c0ec8df5ff 100644 --- a/apps/openchallenges/api-gateway/requests.http +++ b/apps/openchallenges/api-gateway/requests.http @@ -1,55 +1,11 @@ @serviceRegistryHost = http://openchallenges-service-registry:8081 @apiGatewayHost = http://openchallenges-api-gateway:8082 @userServiceHost = http://openchallenges-user-service:8083 -@keycloakHost = http://localhost:8080 -### Check API gateway actuator (expected to redirect to Keycloak login page) +### Check API gateway actuator GET {{apiGatewayHost}}/actuator ### Check service registry info -GET {{serviceRegistryHost}}/actuator/info - -### Check user service info - -GET {{userServiceHost}}/actuator/info - -### Check user service info via the API gateway (expected to redirect to -### Keycloak login page) - -GET {{apiGatewayHost}}/user/actuator/info - -### Get access token from Keycloak - -@clientId = challenge-core-client -@clientSecret = O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX -@username = luke -@password = changeme - -# @name login - -POST {{keycloakHost}}/realms/test/protocol/openid-connect/token -Content-Type: application/x-www-form-urlencoded - -grant_type=password -&scope=email -&client_id={{clientId}} -&client_secret={{clientSecret}} -&username={{username}} -&password={{password}} - -### Check user service info - -GET {{apiGatewayHost}}/user/actuator/info -Authorization: Bearer {{login.response.body.$.access_token}} - -### Check API gateway info - -GET {{apiGatewayHost}}/actuator/info -Authorization: Bearer {{login.response.body.$.access_token}} - -### Check API gateway routes - -GET {{apiGatewayHost}}/actuator/gateway/routes -Authorization: Bearer {{login.response.body.$.access_token}} \ No newline at end of file +GET {{serviceRegistryHost}}/actuator/info \ No newline at end of file diff --git a/apps/openchallenges/api-gateway/src/main/java/org/sagebionetworks/openchallenges/api/gateway/config/SecurityConfig.java b/apps/openchallenges/api-gateway/src/main/java/org/sagebionetworks/openchallenges/api/gateway/config/SecurityConfig.java index d1a775b540..1b64982647 100644 --- a/apps/openchallenges/api-gateway/src/main/java/org/sagebionetworks/openchallenges/api/gateway/config/SecurityConfig.java +++ b/apps/openchallenges/api-gateway/src/main/java/org/sagebionetworks/openchallenges/api/gateway/config/SecurityConfig.java @@ -40,11 +40,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { .authenticated() .and() .csrf() - .disable() - .oauth2Login() - .and() - .oauth2ResourceServer() - .jwt(); + .disable(); return http.build(); } } diff --git a/apps/openchallenges/app/src/app/initialize-keycloak.factory.ts b/apps/openchallenges/app/src/app/initialize-keycloak.factory.ts deleted file mode 100644 index fd342b6b69..0000000000 --- a/apps/openchallenges/app/src/app/initialize-keycloak.factory.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { KeycloakService } from 'keycloak-angular'; -// import { of } from 'rxjs'; -// import { switchMap } from 'rxjs/operators'; -// import { fromPromise } from 'rxjs/internal-compatibility'; -import { ConfigService } from '@sagebionetworks/openchallenges/config'; -// import { ConfigInitService } from './config-init.service'; -import { isPlatformBrowser } from '@angular/common'; - -export function initializeKeycloakFactory( - configService: ConfigService, - keycloak: KeycloakService, - platformId: Record, -) { - return () => { - if (isPlatformBrowser(platformId)) { - keycloak.init({ - config: { - url: 'http://localhost:8080', - realm: 'test', // configService.config.keycloakRealm, - clientId: 'test-client', - // url: config['KEYCLOAK_URL'] + '/auth', - // realm: config['KEYCLOAK_REALM'], - // clientId: config['KEYCLOAK_CLIENT_ID'], - }, - initOptions: { - onLoad: 'check-sso', - silentCheckSsoRedirectUri: 'http://localhost:4200' + '/assets/silent-check-sso.html', - // window.location.origin + '/assets/silent-check-sso.html', - flow: 'standard', - }, - bearerExcludedUrls: [], - }); - } - }; -} diff --git a/apps/openchallenges/auth-service/.env.example b/apps/openchallenges/auth-service/.env.example deleted file mode 100644 index b9ffab557f..0000000000 --- a/apps/openchallenges/auth-service/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -KEYCLOAK_URL=http://openchallenges-keycloak:8080 -SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka \ No newline at end of file diff --git a/apps/openchallenges/auth-service/.gitignore b/apps/openchallenges/auth-service/.gitignore deleted file mode 100644 index e3406cf0fc..0000000000 --- a/apps/openchallenges/auth-service/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -HELP.md -target/ -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/apps/openchallenges/auth-service/build.gradle b/apps/openchallenges/auth-service/build.gradle deleted file mode 100644 index 09f39f0b68..0000000000 --- a/apps/openchallenges/auth-service/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } -} - -plugins { - id 'java' - id 'org.springframework.boot' version "${springBootVersion}" - id "io.spring.dependency-management" version "${springDependencyManagementVersion}" -} - -repositories { - mavenCentral() - mavenLocal() -} - -dependencies { - annotationProcessor "org.projectlombok:lombok:${lombokVersion}" - compileOnly "org.projectlombok:lombok:${lombokVersion}" - implementation "org.keycloak:keycloak-spring-boot-starter:${keycloakVersion}" - implementation "org.springframework.boot:spring-boot-devtools:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-security:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:${springCloudVersion}" - testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}" -} - -group = 'org.sagebionetworks.challenge' -version = '0.0.1-SNAPSHOT' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -springBoot { - mainClass = 'org.sagebionetworks.challenge.ChallengeAuthServiceApplication' -} - -bootBuildImage { - imageName = 'ghcr.io/sage-bionetworks/openchallenges-auth-service:local' -} \ No newline at end of file diff --git a/apps/openchallenges/auth-service/docker-compose.yml b/apps/openchallenges/auth-service/docker-compose.yml deleted file mode 100644 index 0d4e4361ce..0000000000 --- a/apps/openchallenges/auth-service/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '3.8' - -services: - openchallenges-auth-service: - image: ghcr.io/sage-bionetworks/openchallenges-auth-service:local - container_name: openchallenges-auth-service - restart: always - env_file: - - .env - networks: - - openchallenges - ports: - - '8091:8091' - command: start-dev - -networks: - openchallenges: - name: openchallenges diff --git a/apps/openchallenges/auth-service/gradle.properties b/apps/openchallenges/auth-service/gradle.properties deleted file mode 100644 index 7aee56552b..0000000000 --- a/apps/openchallenges/auth-service/gradle.properties +++ /dev/null @@ -1,6 +0,0 @@ -openchallengesVersion=0.0.1-SNAPSHOT -keycloakVersion=19.0.3 -lombokVersion=1.18.24 -springBootVersion=2.7.8 -springCloudVersion=3.1.4 -springDependencyManagementVersion=1.0.14.RELEASE \ No newline at end of file diff --git a/apps/openchallenges/auth-service/gradle/wrapper/gradle-wrapper.jar b/apps/openchallenges/auth-service/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/apps/openchallenges/auth-service/project.json b/apps/openchallenges/auth-service/project.json deleted file mode 100644 index 617344a385..0000000000 --- a/apps/openchallenges/auth-service/project.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "openchallenges-auth-service", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/openchallenges/auth-service/src", - "projectType": "application", - "targets": { - "create-config": { - "executor": "nx:run-commands", - "options": { - "command": "cp -n .env.example .env", - "cwd": "{projectRoot}" - } - }, - "prepare": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew --version 1> /dev/null"], - "cwd": "{projectRoot}" - } - }, - "build": { - "executor": "nx:run-commands", - "outputs": ["{projectRoot}/build"], - "options": { - "command": "./gradlew build", - "cwd": "{projectRoot}" - }, - "dependsOn": ["^install"] - }, - "test": { - "executor": "nx:run-commands", - "outputs": ["{projectRoot}/build"], - "options": { - "command": "./gradlew test", - "cwd": "{projectRoot}" - }, - "dependsOn": ["^install"] - }, - "clean": { - "executor": "nx:run-commands", - "options": { - "command": "./gradlew clean", - "cwd": "{projectRoot}" - } - }, - "serve": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew build --continuous", "./gradlew bootRun"], - "cwd": "apps/openchallenges/auth-service", - "parallel": true - }, - "dependsOn": [] - }, - "serve-detach": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up -d", - "cwd": "apps/openchallenges/auth-service" - }, - "dependsOn": [] - } - }, - "tags": ["type:service", "scope:backend", "language:java", "package-manager:gradle"], - "implicitDependencies": [ - "openchallenges-api-gateway", - "openchallenges-keycloak", - "openchallenges-service-registry", - "shared-java-util" - ] -} diff --git a/apps/openchallenges/auth-service/requests.http b/apps/openchallenges/auth-service/requests.http deleted file mode 100644 index d5eb999164..0000000000 --- a/apps/openchallenges/auth-service/requests.http +++ /dev/null @@ -1,11 +0,0 @@ -@apiGatewayHost = http://openchallenges-api-gateway:8082 - -### Login - -POST {{apiGatewayHost}}/api/v1/auth/login -Content-Type: application/json - -{ - "username": "luke", - "password": "changeme" -} diff --git a/apps/openchallenges/auth-service/settings.gradle b/apps/openchallenges/auth-service/settings.gradle deleted file mode 100644 index bad89f7f6d..0000000000 --- a/apps/openchallenges/auth-service/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'openchallenges-auth-service' diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplication.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplication.java deleted file mode 100644 index 86a08c137b..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplication.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; - -@EnableEurekaClient -@SpringBootApplication -public class ChallengeAuthServiceApplication { - - public static void main(String[] args) { - SpringApplication.run(ChallengeAuthServiceApplication.class, args); - } - - @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder) { - return builder.build(); - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java deleted file mode 100644 index 49a9c28fcb..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class KeycloakConfiguration { - - @Bean - public KeycloakConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java deleted file mode 100644 index 7923e2aa31..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter; -import org.sagebionetworks.challenge.exception.CustomKeycloakAuthenticationHandler; -import org.sagebionetworks.challenge.exception.RestAccessDeniedHandler; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; - -// Defines all annotations that are needed to integrate Keycloak in Spring Security -@KeycloakConfiguration -class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - - @Autowired - RestAccessDeniedHandler restAccessDeniedHandler; - - @Autowired - CustomKeycloakAuthenticationHandler customKeycloakAuthenticationHandler; - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - http - .csrf() - .disable() - .cors() - .disable() - .authorizeRequests() - .antMatchers("/api/v1/auth/login") - .permitAll() - .anyRequest() - .authenticated(); - - // Custom error handler - http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler); - } - - // Disable default role prefix ROLE_ - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = - keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); - } - - // Use Spring Boot property files instead of default keycloak.json - @Bean - public KeycloakSpringBootConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } - - // Register authentication strategy for public or confidential applications - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); - } - - // Keycloak auth exception handler - @Bean - @Override - protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() - throws Exception { - KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter( - this.authenticationManagerBean() - ); - filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy()); - filter.setAuthenticationFailureHandler(customKeycloakAuthenticationHandler); - return filter; - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/controller/LoginController.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/controller/LoginController.java deleted file mode 100644 index 002a102d20..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/controller/LoginController.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.sagebionetworks.challenge.controller; - -import javax.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.sagebionetworks.challenge.model.dto.LoginRequest; -import org.sagebionetworks.challenge.model.dto.LoginResponse; -import org.sagebionetworks.challenge.service.LoginService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Slf4j -@RestController -@RequestMapping(value = "/api/v1/auth") -public class LoginController { - - @Autowired - LoginService loginService; - - @PostMapping(value = "/login") - public ResponseEntity login( - HttpServletRequest request, - @RequestBody LoginRequest loginRequest - ) throws Exception { - log.info("Executing login"); - log.info("Username: {}", loginRequest.getUsername()); - - ResponseEntity response = null; - response = loginService.login(loginRequest); - - return response; - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/CustomKeycloakAuthenticationHandler.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/CustomKeycloakAuthenticationHandler.java deleted file mode 100644 index 5673158178..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/CustomKeycloakAuthenticationHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import java.io.IOException; -import java.time.LocalDateTime; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.stereotype.Component; - -@Component -public class CustomKeycloakAuthenticationHandler implements AuthenticationFailureHandler { - - @Override - public void onAuthenticationFailure( - HttpServletRequest request, - HttpServletResponse response, - AuthenticationException exception - ) throws ServletException, IOException { - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response - .getOutputStream() - .println( - "{ \"timestamp\": \"" + - LocalDateTime.now() + - "\", \"error\": \"" + - "Unauthorized" + - "\", \"status\": 401 , \"message\": \"" + - "invalid token" + - "\", \"path\": \"" + - request.getServletPath() + - "\" }" - ); - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalControllerAdvice.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalControllerAdvice.java deleted file mode 100644 index 9dd1e2a1bd..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalControllerAdvice.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.client.HttpClientErrorException; - -@RestControllerAdvice -public class GlobalControllerAdvice { - - @ExceptionHandler(HttpClientErrorException.class) - public ResponseEntity handleUnauthorized(HttpClientErrorException e) { - return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsString()); - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/RestAccessDeniedHandler.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/RestAccessDeniedHandler.java deleted file mode 100644 index 63988e3663..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/exception/RestAccessDeniedHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.stereotype.Component; - -@Component -public class RestAccessDeniedHandler implements AccessDeniedHandler { - - @Override - public void handle( - HttpServletRequest httpServletRequest, - HttpServletResponse httpServletResponse, - AccessDeniedException e - ) throws IOException, ServletException { - httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); - } -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginRequest.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginRequest.java deleted file mode 100644 index 9d67f4b8cf..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import lombok.Data; - -@Data -public class LoginRequest { - - private String username; - private String password; -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginResponse.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginResponse.java deleted file mode 100644 index 7477833d80..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/model/dto/LoginResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import lombok.Data; - -@Data -public class LoginResponse { - - private String access_token; - private String refresh_token; - private String expires_in; - private String refresh_expires_in; - private String token_type; -} diff --git a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/service/LoginService.java b/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/service/LoginService.java deleted file mode 100644 index d6fe4e72b4..0000000000 --- a/apps/openchallenges/auth-service/src/main/java/org/sagebionetworks/challenge/service/LoginService.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import lombok.extern.slf4j.Slf4j; -import org.sagebionetworks.challenge.model.dto.LoginRequest; -import org.sagebionetworks.challenge.model.dto.LoginResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -@Slf4j -@Service -public class LoginService { - - @Value("${app.keycloak.login.url}") - private String loginUrl; - - @Value("${app.keycloak.client-id}") - private String clientId; - - @Value("${app.keycloak.client-secret}") - private String clientSecret; - - @Value("${app.keycloak.grant-type}") - private String grantType; - - @Autowired - RestTemplate restTemplate; - - public ResponseEntity login(LoginRequest request) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - MultiValueMap map = new LinkedMultiValueMap<>(); - map.add("username", request.getUsername()); - map.add("password", request.getPassword()); - map.add("client_id", clientId); - map.add("client_secret", clientSecret); - map.add("grant_type", grantType); - - log.info("client_id: {}", clientId); - - HttpEntity> httpEntity = new HttpEntity<>(map, headers); - ResponseEntity loginResponse = restTemplate.postForEntity( - loginUrl, - httpEntity, - LoginResponse.class - ); - - log.info(loginResponse.getBody().toString()); - - return ResponseEntity.status(200).body(loginResponse.getBody()); - } -} diff --git a/apps/openchallenges/auth-service/src/main/resources/application.yml b/apps/openchallenges/auth-service/src/main/resources/application.yml deleted file mode 100644 index da88bfaa33..0000000000 --- a/apps/openchallenges/auth-service/src/main/resources/application.yml +++ /dev/null @@ -1,41 +0,0 @@ -spring: - application: - name: openchallenges-auth-service - -server: - port: 8091 - -eureka: - client: - service-url: - defaultZone: ${service.registry.url} - instance: - preferIpAddress: true - -management: - info: - env: - enabled: true - endpoints: - web: - exposure: - include: info - -info: - application: - name: ${spring.application.name} - -keycloak: - realm: test - resource: challenge-core-client # TODO update - auth-server-url: ${keycloak.url} - ssl-required: external - use-resource-role-mappings: false # true: use client roles - -app: - keycloak: - login: - url: http://openchallenges-keycloak:8080/realms/test/protocol/openid-connect/token # TODO update - client-id: ${keycloak.resource} - client-secret: O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX - grant-type: password diff --git a/apps/openchallenges/auth-service/src/test/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplicationTests.java b/apps/openchallenges/auth-service/src/test/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplicationTests.java deleted file mode 100644 index fa94d5c0e8..0000000000 --- a/apps/openchallenges/auth-service/src/test/java/org/sagebionetworks/challenge/ChallengeAuthServiceApplicationTests.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ChallengeAuthServiceApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/apps/openchallenges/challenge-service/.env.example b/apps/openchallenges/challenge-service/.env.example index eb7938b539..55c0282f67 100644 --- a/apps/openchallenges/challenge-service/.env.example +++ b/apps/openchallenges/challenge-service/.env.example @@ -1,4 +1,3 @@ SERVER_PORT=8085 -KEYCLOAK_URL=http://openchallenges-keycloak:8080 SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka DB_URL=jdbc:mysql://openchallenges-mariadb:3306/challenge_service?allowLoadLocalInfile=true \ No newline at end of file diff --git a/apps/openchallenges/challenge-service/build.gradle b/apps/openchallenges/challenge-service/build.gradle index 5a10fc1e4d..1dc1e88d3e 100644 --- a/apps/openchallenges/challenge-service/build.gradle +++ b/apps/openchallenges/challenge-service/build.gradle @@ -36,8 +36,6 @@ dependencies { implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${fasterxmlVersion}" implementation "org.flywaydb:flyway-core:${flywaydbVersion}" implementation "org.flywaydb:flyway-mysql:${flywaydbVersion}" - implementation "org.keycloak:keycloak-admin-client:${keycloakVersion}" - implementation "org.keycloak:keycloak-spring-boot-starter:${keycloakVersion}" implementation "org.sagebionetworks:util:${openchallengesVersion}" implementation 'org.springframework.boot:spring-boot-starter-amqp:2.7.7' diff --git a/apps/openchallenges/challenge-service/gradle.properties b/apps/openchallenges/challenge-service/gradle.properties index edec945fa6..e0ee139751 100644 --- a/apps/openchallenges/challenge-service/gradle.properties +++ b/apps/openchallenges/challenge-service/gradle.properties @@ -1,7 +1,6 @@ fasterxmlVersion=2.13.4 flywaydbVersion=9.4.0 hibernateSearchVersion=6.1.7.Final -keycloakVersion=19.0.3 lombokVersion=1.18.24 openchallengesVersion=0.0.1-SNAPSHOT sonarqubeVersion=4.3.0.3225 diff --git a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakConfiguration.java b/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakConfiguration.java deleted file mode 100644 index 5ec40769f6..0000000000 --- a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.openchallenges.challenge.service.configuration; - -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class KeycloakConfiguration { - - @Bean - public KeycloakConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } -} diff --git a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManager.java b/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManager.java deleted file mode 100644 index f76882bd0e..0000000000 --- a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManager.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.sagebionetworks.openchallenges.challenge.service.configuration; - -import lombok.extern.slf4j.Slf4j; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.admin.client.resource.RealmResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class KeycloakManager { - - @Autowired - private KeycloakManagerProperties keycloakProperties; - - private static Keycloak keycloakInstance = null; - - public RealmResource getKeycloakInstanceWithRealm() { - return getInstance().realm(keycloakProperties.getRealm()); - } - - public Keycloak getInstance() { - if (keycloakInstance == null) { - log.info("KC SERVER URL: {}", keycloakProperties.getServerUrl()); - keycloakInstance = KeycloakBuilder.builder() - .serverUrl(keycloakProperties.getServerUrl()) - .realm(keycloakProperties.getRealm()) - .grantType("client_credentials") - .clientId(keycloakProperties.getClientId()) - .clientSecret(keycloakProperties.getClientSecret()) - .build(); - } - return keycloakInstance; - } -} diff --git a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManagerProperties.java b/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManagerProperties.java deleted file mode 100644 index 1556d3786e..0000000000 --- a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/KeycloakManagerProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.openchallenges.challenge.service.configuration; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "app.config.keycloak") -public class KeycloakManagerProperties { - - private String serverUrl; - private String realm; - private String clientId; - private String clientSecret; -} diff --git a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/SecurityConfiguration.java b/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/SecurityConfiguration.java index 13530feed9..38a6eb7d06 100644 --- a/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/SecurityConfiguration.java +++ b/apps/openchallenges/challenge-service/src/main/java/org/sagebionetworks/openchallenges/challenge/service/configuration/SecurityConfiguration.java @@ -1,71 +1,31 @@ package org.sagebionetworks.openchallenges.challenge.service.configuration; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; -@KeycloakConfiguration +@Configuration +@EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true) -class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { +public class SecurityConfiguration { - // @Autowired - // RestAccessDeniedHandler restAccessDeniedHandler; - - // @Autowired - // CustomKeycloakAuthenticationHandler customKeycloakAuthenticationHandler; - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf() .disable() .cors() .disable() .authorizeRequests() - // .antMatchers("/api/v1/users/register", "/api/v1/**", "/swagger-ui/**", - // "/swagger-ui.html") .antMatchers("**") .permitAll() .anyRequest() - .authenticated(); - // Custom error handler - // http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = - keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); + .authenticated() + .and() + .httpBasic(); // Use Basic Auth for simplicity; replace as needed + return http.build(); } - - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); - } - // // Keycloak auth exception handler - // @Bean - // @Override - // protected KeycloakAuthenticationProcessingFilter - // keycloakAuthenticationProcessingFilter() - // throws Exception { - // KeycloakAuthenticationProcessingFilter filter = - // new KeycloakAuthenticationProcessingFilter(this.authenticationManagerBean()); - // filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy()); - // filter.setAuthenticationFailureHandler(customKeycloakAuthenticationHandler); - // return filter; - // } - } diff --git a/apps/openchallenges/config-server/.env.example b/apps/openchallenges/config-server/.env.example index 2b818f0cf7..8523c679dc 100644 --- a/apps/openchallenges/config-server/.env.example +++ b/apps/openchallenges/config-server/.env.example @@ -1,4 +1,4 @@ -GIT_DEFAULT_LABEL=test-2 +GIT_DEFAULT_LABEL=test-2-wo-keycloak GIT_HOST_KEY_ALGORITHM=ssh-ed25519 GIT_HOST_KEY=AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl GIT_PRIVATE_KEY="-----BEGIN OPENSSH PRIVATE KEY-----\nchangeme\n-----END OPENSSH PRIVATE KEY-----" diff --git a/apps/openchallenges/core-service/.env.example b/apps/openchallenges/core-service/.env.example deleted file mode 100644 index 8037ac72cf..0000000000 --- a/apps/openchallenges/core-service/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka -DB_URL=jdbc:mysql://openchallenges-mariadb:3306/challenge \ No newline at end of file diff --git a/apps/openchallenges/core-service/.gitignore b/apps/openchallenges/core-service/.gitignore deleted file mode 100644 index e3406cf0fc..0000000000 --- a/apps/openchallenges/core-service/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -HELP.md -target/ -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/apps/openchallenges/core-service/build.gradle b/apps/openchallenges/core-service/build.gradle deleted file mode 100644 index 7f5c822e16..0000000000 --- a/apps/openchallenges/core-service/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "org.flywaydb:flyway-mysql:${flywaydbVersion}" - } -} - -plugins { - id 'java' - id 'org.springframework.boot' version "${springBootVersion}" - id "io.spring.dependency-management" version "${springDependencyManagementVersion}" - id "org.flywaydb.flyway" version "${flywaydbVersion}" -} - -repositories { - mavenCentral() - mavenLocal() -} - -dependencies { - annotationProcessor "org.projectlombok:lombok:${lombokVersion}" - compileOnly "org.projectlombok:lombok:${lombokVersion}" - implementation "org.flywaydb:flyway-core:${flywaydbVersion}" - implementation "org.flywaydb:flyway-mysql:${flywaydbVersion}" - implementation "org.sagebionetworks:util:${openchallengesVersion}" - implementation "org.springframework.boot:spring-boot-devtools:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:${springCloudVersion}" - runtimeOnly 'mysql:mysql-connector-java:8.0.30' - testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}" -} - -group = 'org.sagebionetworks.challenge' -version = '0.0.1-SNAPSHOT' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -springBoot { - mainClass = 'org.sagebionetworks.challenge.ChallengeCoreServiceApplication' -} - -bootBuildImage { - imageName = 'ghcr.io/sage-bionetworks/openchallenges-core-service:local' -} diff --git a/apps/openchallenges/core-service/docker-compose.yml b/apps/openchallenges/core-service/docker-compose.yml deleted file mode 100644 index 817fee095e..0000000000 --- a/apps/openchallenges/core-service/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '3.8' - -services: - openchallenges-core-service: - image: ghcr.io/sage-bionetworks/openchallenges-core-service:local - container_name: openchallenges-core-service - restart: always - env_file: - - .env - networks: - - openchallenges - ports: - - '8092:8092' - command: start-dev - -networks: - openchallenges: - name: openchallenges diff --git a/apps/openchallenges/core-service/gradle.properties b/apps/openchallenges/core-service/gradle.properties deleted file mode 100644 index 70b823e2b6..0000000000 --- a/apps/openchallenges/core-service/gradle.properties +++ /dev/null @@ -1,6 +0,0 @@ -openchallengesVersion=0.0.1-SNAPSHOT -flywaydbVersion=9.4.0 -lombokVersion=1.18.24 -springBootVersion=2.7.8 -springCloudVersion=3.1.4 -springDependencyManagementVersion=1.0.14.RELEASE \ No newline at end of file diff --git a/apps/openchallenges/core-service/gradle/wrapper/gradle-wrapper.jar b/apps/openchallenges/core-service/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/apps/openchallenges/core-service/project.json b/apps/openchallenges/core-service/project.json deleted file mode 100644 index 6dcfd80296..0000000000 --- a/apps/openchallenges/core-service/project.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "openchallenges-core-service", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/openchallenges/core-service/src", - "projectType": "application", - "targets": { - "create-config": { - "executor": "nx:run-commands", - "options": { - "command": "cp -n .env.example .env", - "cwd": "{projectRoot}" - } - }, - "prepare": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew --version 1> /dev/null"], - "cwd": "{projectRoot}" - } - }, - "build": { - "executor": "nx:run-commands", - "outputs": ["{projectRoot}/build"], - "options": { - "command": "./gradlew build", - "cwd": "{projectRoot}" - }, - "dependsOn": ["^install"] - }, - "clean": { - "executor": "nx:run-commands", - "options": { - "command": "./gradlew clean", - "cwd": "{projectRoot}" - } - }, - "serve": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew build --continuous", "./gradlew bootRun"], - "cwd": "apps/openchallenges/core-service", - "parallel": true - }, - "dependsOn": [] - }, - "serve-detach": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up -d", - "cwd": "apps/openchallenges/core-service" - }, - "dependsOn": [] - } - }, - "tags": ["type:service", "scope:backend", "language:java", "package-manager:gradle"], - "implicitDependencies": [ - "openchallenges-api-gateway", - "openchallenges-keycloak", - "openchallenges-mariadb", - "openchallenges-service-registry", - "shared-java-util" - ] -} diff --git a/apps/openchallenges/core-service/requests.http b/apps/openchallenges/core-service/requests.http deleted file mode 100644 index f731e4510e..0000000000 --- a/apps/openchallenges/core-service/requests.http +++ /dev/null @@ -1,65 +0,0 @@ -# @serviceRegistryHost = http://localhost:8081 -@apiGatewayHost = http://openchallenges-api-gateway:8082 -# @userServiceHost = http://localhost:8083 -@keycloakHost = http://openchallenges-keycloak:8080 -@coreServiceHost = http://openchallenges-core-service:8092 - -### Check core service info - -GET {{coreServiceHost}}/actuator/info - -### Check core service info via the API gateway (expected to redirect to -### Keycloak login page) - -GET {{apiGatewayHost}}/core/actuator/info - -### Get access token from Keycloak - -# @clientId = challenge-core-client -# @clientSecret = O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX -@clientId = challenge-api-client -@clientSecret = mg2DrRcxHx19PIITibdOnbNEbJUKjGKb -@username = luke -@password = changeme - -# @name login - -POST {{keycloakHost}}/realms/test/protocol/openid-connect/token -Content-Type: application/x-www-form-urlencoded - -grant_type=password -&scope=email -&client_id={{clientId}} -&client_secret={{clientSecret}} -&username={{username}} -&password={{password}} - -### Check core service info - -GET {{apiGatewayHost}}/core/actuator/info -Authorization: Bearer {{login.response.body.$.access_token}} - -### Read user account by identification number - -GET {{apiGatewayHost}}/core/api/v1/account/challenge-account/100015003000 -Authorization: Bearer {{login.response.body.$.access_token}} - -# ### Create user - -# # POST {{apiGatewayHost}}/user/api/v1/users/register -# POST {{userServiceHost}}/api/v1/users/register -# Content-Type: application/json - -# { -# "email": "test4@gmail.com", -# "password": "changeme", -# "identification": "12345" -# } - -# ### List all the users - -# # from the API gateway => requires authentication / unauthorized -# # from the user service => works - -# # GET {{apiGatewayHost}}/user/api/v1/users -# GET {{userServiceHost}}/api/v1/users diff --git a/apps/openchallenges/core-service/settings.gradle b/apps/openchallenges/core-service/settings.gradle deleted file mode 100644 index 9096898cd4..0000000000 --- a/apps/openchallenges/core-service/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'openchallenges-core-service' diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplication.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplication.java deleted file mode 100644 index 0cb359905a..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplication.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; - -@EnableEurekaClient -@SpringBootApplication -public class ChallengeCoreServiceApplication { - - public static void main(String[] args) { - SpringApplication.run(ChallengeCoreServiceApplication.class, args); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/AccountController.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/AccountController.java deleted file mode 100644 index 94603b4055..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/AccountController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.sagebionetworks.challenge.controller; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.sagebionetworks.challenge.model.dto.ChallengeAccount; -import org.sagebionetworks.challenge.service.AccountService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Slf4j -@RestController -@RequestMapping(value = "/api/v1/account") -@RequiredArgsConstructor -public class AccountController { - - private final AccountService accountService; - - @GetMapping("/challenge-account/{account_number}") - public ResponseEntity getChallengeAccount( - @PathVariable("account_number") String accountNumber - ) { - log.info("Reading account by ID {}", accountNumber); - return ResponseEntity.ok(accountService.readChallengeAccount(accountNumber)); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/UserController.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/UserController.java deleted file mode 100644 index 450bcec675..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/controller/UserController.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.sagebionetworks.challenge.controller; - -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.service.UserService; -import org.springframework.data.domain.Pageable; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(value = "/api/v1/user") -@RequiredArgsConstructor -public class UserController { - - private final UserService userService; - - @GetMapping(value = "/{identification}") - public ResponseEntity readUser(@PathVariable("identification") String identification) { - return ResponseEntity.ok(userService.readUser(identification)); - } - - @GetMapping - public ResponseEntity> readUser(Pageable pageable) { - return ResponseEntity.ok(userService.readUsers(pageable)); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/EntityNotFoundException.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/EntityNotFoundException.java deleted file mode 100644 index 8d6adc328a..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/EntityNotFoundException.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; - -public class EntityNotFoundException extends SimpleChallengeGlobalException { - - public EntityNotFoundException() { - super("Requested entity not present in the DB.", GlobalErrorCode.ERROR_ENTITY_NOT_FOUND); - } - - public EntityNotFoundException(String message) { - super(message, GlobalErrorCode.ERROR_ENTITY_NOT_FOUND); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java deleted file mode 100644 index e57e1abab5..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class GlobalErrorCode { - - public static final String ERROR_ENTITY_NOT_FOUND = "CHALLENGE-CORE-SERVICE-1000"; - public static final String INSUFFICIENT_FUNDS = "CHALLENGE-CORE-SERVICE-1001"; -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java deleted file mode 100644 index 9f99c80496..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import java.util.Locale; -import org.sagebionetworks.util.exception.ErrorResponse; -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - -@ControllerAdvice -public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { - - @ExceptionHandler(SimpleChallengeGlobalException.class) - protected ResponseEntity handleGlobalException( - SimpleChallengeGlobalException simpleChallengeGlobalException, - Locale locale - ) { - return ResponseEntity.badRequest() - .body( - ErrorResponse.builder() - .code(simpleChallengeGlobalException.getCode()) - .message(simpleChallengeGlobalException.getMessage()) - .build() - ); - } - - @ExceptionHandler({ Exception.class }) - protected ResponseEntity handleException(Exception e, Locale locale) { - return ResponseEntity.badRequest().body("Exception occur inside API " + e); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountStatus.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountStatus.java deleted file mode 100644 index 640922c965..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.sagebionetworks.challenge.model; - -public enum AccountStatus { - PENDING, - ACTIVE, - DORMANT, - BLOCKED, -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountType.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountType.java deleted file mode 100644 index 22a1892d47..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/AccountType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.sagebionetworks.challenge.model; - -public enum AccountType { - USER_ACCOUNT, - ORGANIZATION_ACCOUNT, -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/ChallengeAccount.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/ChallengeAccount.java deleted file mode 100644 index 0011789897..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/ChallengeAccount.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import java.math.BigDecimal; -import lombok.Data; -import org.sagebionetworks.challenge.model.AccountStatus; -import org.sagebionetworks.challenge.model.AccountType; - -@Data -public class ChallengeAccount { - - private Long id; - private String number; - private AccountType type; - private AccountStatus status; - private BigDecimal availableBalance; - private BigDecimal actualBalance; - private User user; -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/User.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/User.java deleted file mode 100644 index 5ede019050..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/dto/User.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class User { - - private Long id; - private String firstName; - private String lastName; - private String email; - private String identificationNumber; - private List challengeAccounts; -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/ChallengeAccountEntity.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/ChallengeAccountEntity.java deleted file mode 100644 index 79e1886e08..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/ChallengeAccountEntity.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.sagebionetworks.challenge.model.entity; - -import java.math.BigDecimal; -import javax.persistence.*; -import lombok.Getter; -import lombok.Setter; -import org.sagebionetworks.challenge.model.AccountStatus; -import org.sagebionetworks.challenge.model.AccountType; - -@Getter -@Setter -@Entity -@Table(name = "challenge_core_account") -public class ChallengeAccountEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String number; - - @Enumerated(EnumType.STRING) - private AccountType type; - - @Enumerated(EnumType.STRING) - private AccountStatus status; - - private BigDecimal availableBalance; - - private BigDecimal actualBalance; - - @ManyToOne - @JoinColumn(name = "user_id") - private UserEntity user; -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java deleted file mode 100644 index b896251e9a..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.sagebionetworks.challenge.model.entity; - -import java.util.List; -import javax.persistence.*; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -@Table(name = "challenge_core_user") -public class UserEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String firstName; - private String lastName; - private String email; - private String identificationNumber; - - @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL) - private List accounts; -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/ChallengeAccountMapper.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/ChallengeAccountMapper.java deleted file mode 100644 index b082533523..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/ChallengeAccountMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.sagebionetworks.challenge.model.mapper; - -import org.sagebionetworks.challenge.model.dto.ChallengeAccount; -import org.sagebionetworks.challenge.model.entity.ChallengeAccountEntity; -import org.sagebionetworks.util.model.mapper.BaseMapper; -import org.springframework.beans.BeanUtils; - -public class ChallengeAccountMapper extends BaseMapper { - - @Override - public ChallengeAccountEntity convertToEntity(ChallengeAccount dto, Object... args) { - ChallengeAccountEntity entity = new ChallengeAccountEntity(); - if (dto != null) { - BeanUtils.copyProperties(dto, entity, "user"); - } - return entity; - } - - @Override - public ChallengeAccount convertToDto(ChallengeAccountEntity entity, Object... args) { - ChallengeAccount dto = new ChallengeAccount(); - if (entity != null) { - BeanUtils.copyProperties(entity, dto, "user"); - } - return dto; - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java deleted file mode 100644 index d34aaaf0ed..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.sagebionetworks.challenge.model.mapper; - -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.util.model.mapper.BaseMapper; -import org.springframework.beans.BeanUtils; - -public class UserMapper extends BaseMapper { - - private ChallengeAccountMapper challengeAccountMapper = new ChallengeAccountMapper(); - - @Override - public UserEntity convertToEntity(User dto, Object... args) { - UserEntity entity = new UserEntity(); - if (dto != null) { - BeanUtils.copyProperties(dto, entity, "accounts"); - entity.setAccounts(challengeAccountMapper.convertToEntityList(dto.getChallengeAccounts())); - } - return entity; - } - - @Override - public User convertToDto(UserEntity entity, Object... args) { - User dto = new User(); - if (entity != null) { - BeanUtils.copyProperties(entity, dto, "accounts"); - dto.setChallengeAccounts(challengeAccountMapper.convertToDtoList(entity.getAccounts())); - } - return dto; - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/ChallengeAccountRepository.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/ChallengeAccountRepository.java deleted file mode 100644 index 0e3d644f52..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/ChallengeAccountRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sagebionetworks.challenge.repository; - -import java.util.Optional; -import org.sagebionetworks.challenge.model.entity.ChallengeAccountEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ChallengeAccountRepository extends JpaRepository { - Optional findByNumber(String accountNumber); -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/UserRepository.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/UserRepository.java deleted file mode 100644 index a0b3832205..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/repository/UserRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sagebionetworks.challenge.repository; - -import java.util.Optional; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserRepository extends JpaRepository { - Optional findByIdentificationNumber(String identificationNumber); -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/AccountService.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/AccountService.java deleted file mode 100644 index 3579d71735..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/AccountService.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import lombok.RequiredArgsConstructor; -import org.sagebionetworks.challenge.model.dto.ChallengeAccount; -import org.sagebionetworks.challenge.model.entity.ChallengeAccountEntity; -import org.sagebionetworks.challenge.model.mapper.ChallengeAccountMapper; -import org.sagebionetworks.challenge.repository.ChallengeAccountRepository; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class AccountService { - - private ChallengeAccountMapper bankAccountMapper = new ChallengeAccountMapper(); - - private final ChallengeAccountRepository bankAccountRepository; - - public ChallengeAccount readChallengeAccount(String accountNumber) { - ChallengeAccountEntity entity = bankAccountRepository.findByNumber(accountNumber).get(); - return bankAccountMapper.convertToDto(entity); - } -} diff --git a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java b/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java deleted file mode 100644 index 6e427dc9a6..0000000000 --- a/apps/openchallenges/core-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.sagebionetworks.challenge.exception.EntityNotFoundException; -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.challenge.model.mapper.UserMapper; -import org.sagebionetworks.challenge.repository.UserRepository; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class UserService { - - private UserMapper userMapper = new UserMapper(); - - private final UserRepository userRepository; - - public User readUser(String identification) { - UserEntity userEntity = userRepository - .findByIdentificationNumber(identification) - .orElseThrow(EntityNotFoundException::new); - return userMapper.convertToDto(userEntity); - } - - public List readUsers(Pageable pageable) { - return userMapper.convertToDtoList(userRepository.findAll(pageable).getContent()); - } -} diff --git a/apps/openchallenges/core-service/src/main/resources/application.yml b/apps/openchallenges/core-service/src/main/resources/application.yml deleted file mode 100644 index cd721e27a4..0000000000 --- a/apps/openchallenges/core-service/src/main/resources/application.yml +++ /dev/null @@ -1,33 +0,0 @@ -spring: - application: - name: openchallenges-core-service - datasource: - url: ${db.url} - username: challenge_core_service - password: changeme - jpa: - hibernate: - ddl-auto: none - -server: - port: 8092 - -eureka: - client: - service-url: - defaultZone: ${service.registry.url} - instance: - preferIpAddress: true - -management: - info: - env: - enabled: true - endpoints: - web: - exposure: - include: info - -info: - app: - name: ${spring.application.name} diff --git a/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174638__create_base_table_structure.sql b/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174638__create_base_table_structure.sql deleted file mode 100644 index 3a5f740f21..0000000000 --- a/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174638__create_base_table_structure.sql +++ /dev/null @@ -1,27 +0,0 @@ --- challenge.challenge_core_user definition - --- CREATE TABLE `challenge_core_user` --- ( --- `id` bigint(20) NOT NULL AUTO_INCREMENT, --- `email` varchar(255) DEFAULT NULL, --- `first_name` varchar(255) DEFAULT NULL, --- `identification_number` varchar(255) DEFAULT NULL, --- `last_name` varchar(255) DEFAULT NULL, --- PRIMARY KEY (`id`) --- ); - --- challenge.challenge_core_account definition - --- CREATE TABLE `challenge_core_account` --- ( --- `id` bigint(20) NOT NULL AUTO_INCREMENT, --- `actual_balance` decimal(19, 2) DEFAULT NULL, --- `available_balance` decimal(19, 2) DEFAULT NULL, --- `number` varchar(255) DEFAULT NULL, --- `status` varchar(255) DEFAULT NULL, --- `type` varchar(255) DEFAULT NULL, --- `user_id` bigint(20) DEFAULT NULL, --- PRIMARY KEY (`id`), --- KEY `FKt5uqy9p0v3rp3yhlgvm7ep0ij` (`user_id`), --- CONSTRAINT `FKt5uqy9p0v3rp3yhlgvm7ep0ij` FOREIGN KEY (`user_id`) REFERENCES `challenge_core_user` (`id`) --- ); diff --git a/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql b/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql deleted file mode 100644 index 70a2311471..0000000000 --- a/apps/openchallenges/core-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql +++ /dev/null @@ -1,25 +0,0 @@ --- INSERT INTO challenge_core_user (id, email, first_name, identification_number, last_name) --- VALUES ('1', 'sam@gmail.com', 'Sam', '808829932V', 'Silva'); --- INSERT INTO challenge.challenge_core_user (id, email, first_name, identification_number, last_name) --- VALUES ('2', 'guru@gmail.com', 'Guru', '901830556V', 'Darmaraj'); --- INSERT INTO challenge.challenge_core_user (id, email, first_name, identification_number, last_name) --- VALUES ('3', 'ragu@gmail.com', 'Ragu', '348829932V', 'Sivaraj'); --- INSERT INTO challenge.challenge_core_user (id, email, first_name, identification_number, last_name) --- VALUES ('4', 'randor@gmail.com', 'Randor', '842829932V', 'Manoon'); - --- INSERT INTO challenge_core_account --- (actual_balance, available_balance, `number`, status, `type`, user_id) --- VALUES (100000.00, 100000.00, 100015003000, 'ACTIVE', 'USER_ACCOUNT', '1'), --- (100000.00, 100000.00, 100015003001, 'ACTIVE', 'USER_ACCOUNT', '1'), --- (100000.00, 100000.00, 100015003002, 'ACTIVE', 'USER_ACCOUNT', '2'), --- (12000.00, 12000.00, 100015003003, 'ACTIVE', 'USER_ACCOUNT', '2'), --- (12000.00, 12000.00, 100015003004, 'ACTIVE', 'USER_ACCOUNT', '2'), --- (12000.00, 12000.00, 100015003005, 'ACTIVE', 'USER_ACCOUNT', '3'), --- (290000.00, 290000.00, 100015003006, 'ACTIVE', 'USER_ACCOUNT', '3'), --- (290000.00, 290000.00, 100015003007, 'ACTIVE', 'USER_ACCOUNT', '3'), --- (290000.00, 290000.00, 100015003008, 'ACTIVE', 'USER_ACCOUNT', '3'), --- (365023.00, 365023.00, 100015003009, 'ACTIVE', 'USER_ACCOUNT', '3'), --- (365023.00, 365023.00, 100015003010, 'ACTIVE', 'USER_ACCOUNT', '4'), --- (365023.00, 89456.00, 100015003011, 'ACTIVE', 'USER_ACCOUNT', '4'), --- (89456.00, 89456.00, 100015003012, 'ACTIVE', 'USER_ACCOUNT', '4'), --- (889000.33, 889000.33, 100015003013, 'ACTIVE', 'USER_ACCOUNT', '4'); diff --git a/apps/openchallenges/core-service/src/test/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplicationTests.java b/apps/openchallenges/core-service/src/test/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplicationTests.java deleted file mode 100644 index e49a030e38..0000000000 --- a/apps/openchallenges/core-service/src/test/java/org/sagebionetworks/challenge/ChallengeCoreServiceApplicationTests.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ChallengeCoreServiceApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/apps/openchallenges/image-service/gradle.properties b/apps/openchallenges/image-service/gradle.properties index a3b9aefba3..4967a7ccb6 100644 --- a/apps/openchallenges/image-service/gradle.properties +++ b/apps/openchallenges/image-service/gradle.properties @@ -2,7 +2,6 @@ fasterxmlVersion=2.13.4 flywaydbVersion=9.4.0 hibernateSearchVersion=6.1.7.Final -keycloakVersion=19.0.3 lombokVersion=1.18.30 openchallengesVersion=0.0.1-SNAPSHOT sonarqubeVersion=4.3.0.3225 diff --git a/apps/openchallenges/keycloak/.env.example b/apps/openchallenges/keycloak/.env.example deleted file mode 100644 index 26ae1f1473..0000000000 --- a/apps/openchallenges/keycloak/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# https://www.keycloak.org/server/all-config -KEYCLOAK_ADMIN=admin -KEYCLOAK_ADMIN_PASSWORD=changeme -KC_DB=dev-file -#KC_DB=postgres -#KC_DB_URL_HOST=keycloak-db -#KC_DB_URL_DATABASE=keycloak -#KC_DB_USERNAME=keycloak -#KC_DB_SCHEMA=public -#KC_DB_PASSWORD=changeme -KC_LOG_LEVEL=info diff --git a/apps/openchallenges/keycloak/.gitignore b/apps/openchallenges/keycloak/.gitignore deleted file mode 100644 index b368867960..0000000000 --- a/apps/openchallenges/keycloak/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -data/h2/* - -!data/h2/.gitkeep \ No newline at end of file diff --git a/apps/openchallenges/keycloak/Dockerfile b/apps/openchallenges/keycloak/Dockerfile deleted file mode 100644 index bb6923863e..0000000000 --- a/apps/openchallenges/keycloak/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM quay.io/keycloak/keycloak:19.0.3 \ No newline at end of file diff --git a/apps/openchallenges/keycloak/README.md b/apps/openchallenges/keycloak/README.md deleted file mode 100644 index 53129e5381..0000000000 --- a/apps/openchallenges/keycloak/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Keycloak - -## Introduction - -TODO - -## Preparing - -```console -nx create-config openchallenges-keycloak -nx docker openchallenges-keycloak -``` - -## Deploying - -```console -nx serve openchallenges-keycloak -``` - -## Accessing Kecloak Admin Console - -- Navigate to http://localhost:8080 -- Click on `Administrative Console` -- Enter the credentials defined in `.env` - - `KEYCLOAK_ADMIN` - - `KEYCLOAK_ADMIN_PASSWORD` - -## Development - -### Importing realms and users from files - -Import development data from JSON file and generate data in `./data/h2/`. This -command must be run when the Keycloak server is not running. - -```console -nx import-dev-data openchallenges-keycloak -``` - -### Editing KC data - -Start Keycloak in development mode and make edits. - -```console -nx serve openchallenges-keycloak -``` - -### Exporting realms and users - -Export development data from `./data/h2/` to JSON files. This command must be -run when the Keycloak server is not running. - -```console -nx export-dev-data openchallenges-keycloak -``` diff --git a/apps/openchallenges/keycloak/data/h2/.gitkeep b/apps/openchallenges/keycloak/data/h2/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/apps/openchallenges/keycloak/data/import/master-realm.json b/apps/openchallenges/keycloak/data/import/master-realm.json deleted file mode 100644 index 928bf262ed..0000000000 --- a/apps/openchallenges/keycloak/data/import/master-realm.json +++ /dev/null @@ -1,2245 +0,0 @@ -{ - "id": "20388d40-3efd-4a62-9b29-bc5264fee031", - "realm": "master", - "displayName": "Keycloak", - "displayNameHtml": "

Keycloak
", - "notBefore": 0, - "defaultSignatureAlgorithm": "RS256", - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 60, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "ssoSessionIdleTimeoutRememberMe": 0, - "ssoSessionMaxLifespanRememberMe": 0, - "offlineSessionIdleTimeout": 2592000, - "offlineSessionMaxLifespanEnabled": false, - "offlineSessionMaxLifespan": 5184000, - "clientSessionIdleTimeout": 0, - "clientSessionMaxLifespan": 0, - "clientOfflineSessionIdleTimeout": 0, - "clientOfflineSessionMaxLifespan": 0, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 300, - "oauth2DeviceCodeLifespan": 600, - "oauth2DevicePollingInterval": 600, - "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, - "roles": { - "realm": [ - { - "id": "33562da8-c9ff-43ed-8c5e-ea282801b735", - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "composite": false, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031", - "attributes": {} - }, - { - "id": "f2b8f756-cdfd-4019-9d4a-2f79cf8a2c5f", - "name": "admin", - "description": "${role_admin}", - "composite": true, - "composites": { - "realm": ["create-realm"], - "client": { - "test-realm": [ - "manage-clients", - "view-realm", - "query-clients", - "create-client", - "manage-authorization", - "manage-realm", - "manage-identity-providers", - "query-realms", - "impersonation", - "manage-users", - "view-clients", - "view-identity-providers", - "view-users", - "view-events", - "view-authorization", - "query-groups", - "query-users", - "manage-events" - ], - "master-realm": [ - "manage-events", - "view-authorization", - "query-realms", - "query-users", - "view-users", - "view-realm", - "manage-realm", - "manage-users", - "view-clients", - "manage-authorization", - "query-clients", - "manage-clients", - "view-events", - "impersonation", - "view-identity-providers", - "query-groups", - "create-client", - "manage-identity-providers" - ] - } - }, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031", - "attributes": {} - }, - { - "id": "e315590b-9048-4042-845f-c0d4273dc05c", - "name": "default-roles-master", - "description": "${role_default-roles}", - "composite": true, - "composites": { - "realm": ["offline_access", "uma_authorization"], - "client": { - "account": ["manage-account", "view-profile"] - } - }, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031", - "attributes": {} - }, - { - "id": "c9c6f86e-2b38-4813-b0cc-75ae3312af10", - "name": "offline_access", - "description": "${role_offline-access}", - "composite": false, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031", - "attributes": {} - }, - { - "id": "bbc2fae9-4de3-4e7a-b3fe-f8e4aac8bb7c", - "name": "create-realm", - "description": "${role_create-realm}", - "composite": false, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031", - "attributes": {} - } - ], - "client": { - "test-realm": [ - { - "id": "271e991b-a534-4860-9730-25cd34618def", - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "b16fca49-5a06-4457-ab95-036bc5d35057", - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "test-realm": ["query-clients"] - } - }, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "46192614-c796-4034-bfeb-4ef4f941bea0", - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "b5e9e760-bf9a-4662-9425-b2eb5e00ebc1", - "name": "view-realm", - "description": "${role_view-realm}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "6e3af7de-ef39-4f1f-b530-96753541c266", - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "test-realm": ["query-users", "query-groups"] - } - }, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "2fad0262-6845-4b83-87cd-93468ca6fb65", - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "cbf02f29-ec61-4e4c-ac38-b7be80953e52", - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "7265ba8c-b682-4957-ae3a-d8dde430ae2e", - "name": "view-authorization", - "description": "${role_view-authorization}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "79609445-bdec-4c12-9ffc-923064f1b4e8", - "name": "create-client", - "description": "${role_create-client}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "7c6fc091-6a3f-492c-a79b-4167194f4217", - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "ed953899-da2e-4b0d-8927-ccc0ba57c122", - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "55e0571f-255d-4a7c-9ae1-c20fd5bf91d2", - "name": "manage-realm", - "description": "${role_manage-realm}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "8aad91ac-12c1-4b82-bcf0-fb6b50e9c285", - "name": "query-users", - "description": "${role_query-users}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "c4beea87-f82b-43af-988b-f90497b30345", - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "2f3dc0d7-853c-4cb9-beff-7bba43b986a5", - "name": "manage-events", - "description": "${role_manage-events}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "4b5d8ff5-4855-4877-86eb-6b8a906c33da", - "name": "query-realms", - "description": "${role_query-realms}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "5660949c-3c6f-49e1-b174-0e87c49a81f4", - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - }, - { - "id": "cbb5c25c-8862-4f91-acfc-27d0d1e45414", - "name": "manage-users", - "description": "${role_manage-users}", - "composite": false, - "clientRole": true, - "containerId": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "attributes": {} - } - ], - "security-admin-console": [], - "admin-cli": [], - "account-console": [], - "broker": [ - { - "id": "c6b5fc4b-72ae-447f-98a5-ef83531f91a2", - "name": "read-token", - "description": "${role_read-token}", - "composite": false, - "clientRole": true, - "containerId": "1d25431f-f8f1-43d9-b52f-6ffd401960a0", - "attributes": {} - } - ], - "master-realm": [ - { - "id": "e81e509c-696f-4d38-94fa-46fc74b45afc", - "name": "manage-realm", - "description": "${role_manage-realm}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "f28f6bd4-0926-4149-a304-a31b37957887", - "name": "manage-events", - "description": "${role_manage-events}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "083655bb-3ca1-402d-ab45-f95a7b60c0ac", - "name": "manage-users", - "description": "${role_manage-users}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "73b8acd2-7575-48ed-92f9-f05a19296eff", - "name": "view-authorization", - "description": "${role_view-authorization}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "3245528e-b5e5-4572-992f-c848dc7fabce", - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "master-realm": ["query-clients"] - } - }, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "39c25895-e357-49d5-90f7-73284737449a", - "name": "query-realms", - "description": "${role_query-realms}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "5e9f71e4-b94e-4967-92a2-64f6eb35b6b7", - "name": "query-users", - "description": "${role_query-users}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "dcc1f2d7-0ece-4342-b821-edc3b64ef71a", - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "1e0958dd-da64-4067-adc7-3f8bb4ce50bc", - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "edde1dcc-e5cd-4ffc-8ecc-d24c2426c741", - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "master-realm": ["query-users", "query-groups"] - } - }, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "cd6d9c79-8c14-466c-b1bb-958cc8eb5834", - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "f2e22435-6004-4b1e-a8dd-74f3d9158328", - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "892473ad-745d-40c6-82cf-07796f5e571e", - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "6efc78ea-1a5c-461c-ab39-6f3f22606986", - "name": "view-realm", - "description": "${role_view-realm}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "a1f69632-edfa-4347-b006-7c877b171c1b", - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "e2392de7-b8b1-4de7-970d-f06473e5567c", - "name": "create-client", - "description": "${role_create-client}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "46c8275a-39ef-44e1-8bb6-d5d1e678e9c2", - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - }, - { - "id": "72f0865f-fd4d-4f9e-89f9-1a2b6739656c", - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "attributes": {} - } - ], - "account": [ - { - "id": "33f45198-c2e1-4c1d-bb36-7064c9126694", - "name": "delete-account", - "description": "${role_delete-account}", - "composite": false, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "326ccf5d-d3c4-4055-9fe1-799f176087c4", - "name": "view-consent", - "description": "${role_view-consent}", - "composite": false, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "74733da0-81d1-458f-bcba-e1dbd4d2459f", - "name": "manage-account-links", - "description": "${role_manage-account-links}", - "composite": false, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "8ab6ca46-aeaf-486a-b3b0-9cb57fd425ba", - "name": "manage-account", - "description": "${role_manage-account}", - "composite": true, - "composites": { - "client": { - "account": ["manage-account-links"] - } - }, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "729ce8cd-5fca-41e8-8c34-a50d82fd3fa8", - "name": "manage-consent", - "description": "${role_manage-consent}", - "composite": true, - "composites": { - "client": { - "account": ["view-consent"] - } - }, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "debadeac-0276-4d60-90ef-4860b0cd818a", - "name": "view-profile", - "description": "${role_view-profile}", - "composite": false, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - }, - { - "id": "af057c83-685b-47d4-bcd8-f3808c5a428a", - "name": "view-applications", - "description": "${role_view-applications}", - "composite": false, - "clientRole": true, - "containerId": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "attributes": {} - } - ] - } - }, - "groups": [], - "defaultRole": { - "id": "e315590b-9048-4042-845f-c0d4273dc05c", - "name": "default-roles-master", - "description": "${role_default-roles}", - "composite": true, - "clientRole": false, - "containerId": "20388d40-3efd-4a62-9b29-bc5264fee031" - }, - "requiredCredentials": ["password"], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": ["ES256"], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], - "scopeMappings": [ - { - "clientScope": "offline_access", - "roles": ["offline_access"] - } - ], - "clientScopeMappings": { - "account": [ - { - "client": "account-console", - "roles": ["manage-account"] - } - ] - }, - "clients": [ - { - "id": "11498521-ec12-4c82-8965-b9bf6195bc6f", - "clientId": "account", - "name": "${client_account}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/master/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/master/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "e9cda72c-72e9-4172-ab13-eac98a971a90", - "clientId": "account-console", - "name": "${client_account-console}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/master/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/master/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "ac8090c7-d5e7-4d13-aa8b-ca29cf3ee54c", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "a3cebff9-24c6-4b77-acbd-feb4dcb7e696", - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "1d25431f-f8f1-43d9-b52f-6ffd401960a0", - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "ffed0f35-0734-4e12-b6f7-37bd86686814", - "clientId": "master-realm", - "name": "master Realm", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "707a367c-1e94-4d59-abfc-ded9fca4c631", - "clientId": "security-admin-console", - "name": "${client_security-admin-console}", - "rootUrl": "${authAdminUrl}", - "baseUrl": "/admin/master/console/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/master/console/*"], - "webOrigins": ["+"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "dd8cc573-a41b-4847-a1e7-94b4a75a0dfa", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "d5b1d311-11b9-4f41-8de6-25af3fd24580", - "clientId": "test-realm", - "name": "test Realm", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": [], - "optionalClientScopes": [] - } - ], - "clientScopes": [ - { - "id": "2f7f21d7-c276-4ed7-b931-0a8377358549", - "name": "address", - "description": "OpenID Connect built-in scope: address", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "6dc3f301-7f48-4e7e-9cb6-28c5c84f8754", - "name": "address", - "protocol": "openid-connect", - "protocolMapper": "oidc-address-mapper", - "consentRequired": false, - "config": { - "user.attribute.formatted": "formatted", - "user.attribute.country": "country", - "user.attribute.postal_code": "postal_code", - "userinfo.token.claim": "true", - "user.attribute.street": "street", - "id.token.claim": "true", - "user.attribute.region": "region", - "access.token.claim": "true", - "user.attribute.locality": "locality" - } - } - ] - }, - { - "id": "a0da2870-70fc-464e-ae66-be7ba1243e8e", - "name": "profile", - "description": "OpenID Connect built-in scope: profile", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "d9641ae9-ff2b-4aa2-84a5-17f50619ca03", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - }, - { - "id": "fa3380a6-165f-458c-b079-6c5b6c0fcf82", - "name": "middle name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "middleName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "middle_name", - "jsonType.label": "String" - } - }, - { - "id": "0e0827c1-43a5-4142-a426-9b1d4d415cdf", - "name": "updated at", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "updatedAt", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "updated_at", - "jsonType.label": "long" - } - }, - { - "id": "2f655fec-6706-4716-b69e-77f4f868994e", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "c7512648-de33-4483-bd6a-c1474685384a", - "name": "zoneinfo", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "zoneinfo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "zoneinfo", - "jsonType.label": "String" - } - }, - { - "id": "d8ff5289-e37e-4014-b1c5-2fda52ca8324", - "name": "picture", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "picture", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "picture", - "jsonType.label": "String" - } - }, - { - "id": "c381d77b-5134-41f5-8b30-215949d870e7", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "e037511f-9e75-4aae-a223-812fcab837cb", - "name": "profile", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "profile", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "profile", - "jsonType.label": "String" - } - }, - { - "id": "de57e719-c5ca-453e-99fa-0861af45944f", - "name": "nickname", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "nickname", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "nickname", - "jsonType.label": "String" - } - }, - { - "id": "735ae778-5687-4c2f-94ea-d4967cadd6a0", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "b68ef89d-4316-4782-a90c-b1a0ba472c7b", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "7c4d83de-bee7-4965-9686-363ffeb29262", - "name": "birthdate", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "birthdate", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "birthdate", - "jsonType.label": "String" - } - }, - { - "id": "a7d96168-6ec2-4490-be45-7d297d88a19a", - "name": "website", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "website", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "website", - "jsonType.label": "String" - } - }, - { - "id": "ae07ea06-7063-47cc-adca-0cc3166baa99", - "name": "gender", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "gender", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "gender", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "08c85588-13c9-4e99-bef8-8554d32b094d", - "name": "email", - "description": "OpenID Connect built-in scope: email", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "544eb635-33c9-4686-ac77-4d7535d98a2b", - "name": "email verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "emailVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email_verified", - "jsonType.label": "boolean" - } - }, - { - "id": "ee11fe75-86e9-427a-ac3d-1b69d9de0155", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "29735f4b-8677-4616-8a63-adf13828de72", - "name": "microprofile-jwt", - "description": "Microprofile - JWT built-in scope", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "a3634657-5207-4178-9d38-43a04b68ad0a", - "name": "upn", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "upn", - "jsonType.label": "String" - } - }, - { - "id": "3ebc0eeb-e3be-456a-89cd-2d64e1b8bb86", - "name": "groups", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "multivalued": "true", - "userinfo.token.claim": "true", - "user.attribute": "foo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "groups", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "5a18e9cd-8dc6-44b9-aed9-25902f2825a0", - "name": "role_list", - "description": "SAML role list", - "protocol": "saml", - "attributes": { - "consent.screen.text": "${samlRoleListScopeConsentText}", - "display.on.consent.screen": "true" - }, - "protocolMappers": [ - { - "id": "e5415bf8-69ef-4ca1-9df1-6ba09fc5600f", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - } - ] - }, - { - "id": "7740da88-631d-447f-a113-8a81d16afe99", - "name": "acr", - "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "0c9f7b69-0f50-4777-82de-1c58231715db", - "name": "acr loa level", - "protocol": "openid-connect", - "protocolMapper": "oidc-acr-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - } - ] - }, - { - "id": "7b5e0b5f-08b5-49eb-94e5-c87477403c4e", - "name": "roles", - "description": "OpenID Connect scope for add user roles to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "6ff666a5-3ae6-40c6-a00b-d87d73641987", - "name": "client roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "resource_access.${client_id}.roles", - "jsonType.label": "String", - "multivalued": "true" - } - }, - { - "id": "c52d0c77-566b-4cae-8a3d-ea972fa13c61", - "name": "realm roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "realm_access.roles", - "jsonType.label": "String", - "multivalued": "true" - } - }, - { - "id": "416bc5db-d082-4daf-bea3-42873f6f0db2", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ] - }, - { - "id": "dcce40e4-b3f9-495d-8f93-ccaeea333b82", - "name": "offline_access", - "description": "OpenID Connect built-in scope: offline_access", - "protocol": "openid-connect", - "attributes": { - "consent.screen.text": "${offlineAccessScopeConsentText}", - "display.on.consent.screen": "true" - } - }, - { - "id": "532d900b-fc29-40bd-88c3-f9814ff09480", - "name": "phone", - "description": "OpenID Connect built-in scope: phone", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "757bd5a7-4403-4226-b990-732fda429a2e", - "name": "phone number verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumberVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number_verified", - "jsonType.label": "boolean" - } - }, - { - "id": "cad043aa-f44f-474f-89b7-3990a1f20ec2", - "name": "phone number", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumber", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "4a59f880-cb35-402f-b537-806a2e1aae33", - "name": "web-origins", - "description": "OpenID Connect scope for add allowed web origins to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" - }, - "protocolMappers": [ - { - "id": "0256f6ca-eb21-41a6-b18a-128e2270b293", - "name": "allowed web origins", - "protocol": "openid-connect", - "protocolMapper": "oidc-allowed-origins-mapper", - "consentRequired": false, - "config": {} - } - ] - } - ], - "defaultDefaultClientScopes": ["role_list", "profile", "email", "roles", "web-origins", "acr"], - "defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt"], - "browserSecurityHeaders": { - "contentSecurityPolicyReportOnly": "", - "xContentTypeOptions": "nosniff", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection": "1; mode=block", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "identityProviders": [], - "identityProviderMappers": [], - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "id": "5dd1ff80-9768-48ff-a220-280ec49caf94", - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": ["true"], - "client-uris-must-match": ["true"] - } - }, - { - "id": "46248562-2755-4d99-be51-25447a0e9fc4", - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": ["200"] - } - }, - { - "id": "2fa55b56-9cae-4d5e-b3fd-fb49b9fc5767", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-full-name-mapper", - "saml-role-list-mapper", - "oidc-sha256-pairwise-sub-mapper", - "saml-user-property-mapper", - "oidc-usermodel-attribute-mapper", - "saml-user-attribute-mapper", - "oidc-address-mapper", - "oidc-usermodel-property-mapper" - ] - } - }, - { - "id": "d447eca6-b779-4c09-880e-c1769f3caf63", - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "53fe051a-99c5-4fb1-8269-ff0ee5765b91", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "id": "2598b8a9-0aec-4022-a312-013f4d317685", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "id": "5d7ee85c-c4c2-4ab8-b544-153d215da886", - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "294e06ee-367a-45d5-9bc8-4529a3c88de5", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-usermodel-attribute-mapper", - "saml-user-property-mapper", - "saml-role-list-mapper", - "saml-user-attribute-mapper", - "oidc-address-mapper", - "oidc-full-name-mapper", - "oidc-usermodel-property-mapper", - "oidc-sha256-pairwise-sub-mapper" - ] - } - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "id": "22f9a122-f854-42e9-9eaa-86fb0cee5512", - "name": "rsa-generated", - "providerId": "rsa-generated", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEpQIBAAKCAQEAw0QzBtMNNEDRsbJxDS1UjOx6DcYcYf8lA9hJPoi6SebahYXtC5JCejbsVPuX/tJt0PDucrse0pXmIBpBMTxlB8qn27Q7k1q78T5L6wZi7XPy/u9fYwU5F4g1FKCH1powLlrePJI7ishoqmsg0sF9bxmSJ1+y8FHB8QL1jgjEDQLkdqcO/YcQw1ZVaIcBCyDqiny1ybBeRNu6mSWnyV4QTuy2611t7pEAuCwZV9r6RN97AYKg7uYmlZ0I6/FRusa9CXTOdevfgIS2hu/EqRAza+heFpYKtK2H9CjJbDheW5rkKnbLV6xaquSZMdJDKYRKTzojkyxDtP89R3frLhCpAwIDAQABAoIBAQCOUKV0AtxryhZLEcftl6047J1kbpB/J+s9aySu7A4CELwRdvO2bEoBMwh6Q8ia05lwIDlZ62FikBfiK+B51w+3Zw+CRFDra8Tw5buFNulhMRHYlnQKAmOC8CXnu7xlP4mAL4Ar21SEKN6H3n5UvOsDzzpDci9DkgLs+oAnneHtrHkMZ5QEvL11L+tZ0xL7nrUVCs6d9AMfE7CUeXDf/tZddUm9Jp9HO2nVehbs/UWgI3GxcNPTOMyzPJS27lrCjbG4h50x15IvxB8RfloBQtDL0aDKA3TfCsMkYvVWPz/Rr5jDCVtR4P4zJdaRRrvyf3TJcb7OT1m2T67vJA2LpNvJAoGBAONs9KenP+9c5SooyWnTNs9ni/zUn55mR1qEwgRcJr7ML68wtZBGNJHYxW8ZFhrKJXU4h3Z6nDpV1lEfukcC1ZfUhomeKVdagx6huysnTlvzTrTFdHN0TQVEwgrkgYVi3BIM5uKJqogflDWKVoaOUEPF51ryadwpUDwPkIp8CvrPAoGBANvM22WScLr9ZdSfoxwuKsVWH/VOr1FCE2II9mXPhd3KYxRVHd7hY3moHhFEN/oq+dApxa/Em/0yJpRc4rXs2sdDUcP4LQQDAa4TR4sFosQ+LxlfFhLfad9nrHPM7hPc7U6rJpTGrG2Nkfh08OKVPGEsNWrRrkS4kAOvSdLz0GuNAoGBAN2F15kmVCR3O9EhAW9PCOhlV/nhsci8n2wQPFu61xKqGRhwPapWeAZIO6fHq7xUgr/3XBuPunUn/yXt3aglnp/Rp2qM4H/x7h38MzAJjU4LZPK4EoITGqKzIr8xbOfcL7h1UMAAkFcfS2ILadSO8qfRMVVpIqwModae4o/B24AJAoGAFB99Np9hkr0ARacX18yiPc7UvjCl/FqAS8lmRYvqRZPx3jqssWsRIGIz3oDQXybwGA66+oyVe2jL/2lBmEZHAVc4dSiTdnqP/M/nDONHM5A936TE8JVjrABh2Xg5k7f2Qe2JarWFs8HySzzpkNT44iyZ912bBs+e37d8msOXoUECgYEAvmqhjTG4dUVGQGbMcD719gCSpO+2OUJZtyRIe+ZDScRHrYih8bTp/cZ7dqedERtWTthffYhrXu1qZBPJwPhGKzrea+DV/T+QTXyGhUtDgSHPlzG2UvkUAbQho32DYSIhP89GoWzhIPIq+NtZZSC8yhk9fY0Xq29MiZqEYTNrlfo=" - ], - "keyUse": ["SIG"], - "certificate": [ - "MIICmzCCAYMCBgGA3taIBTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwNTIwMDAxOTAzWhcNMzIwNTIwMDAyMDQzWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDRDMG0w00QNGxsnENLVSM7HoNxhxh/yUD2Ek+iLpJ5tqFhe0LkkJ6NuxU+5f+0m3Q8O5yux7SleYgGkExPGUHyqfbtDuTWrvxPkvrBmLtc/L+719jBTkXiDUUoIfWmjAuWt48kjuKyGiqayDSwX1vGZInX7LwUcHxAvWOCMQNAuR2pw79hxDDVlVohwELIOqKfLXJsF5E27qZJafJXhBO7LbrXW3ukQC4LBlX2vpE33sBgqDu5iaVnQjr8VG6xr0JdM5169+AhLaG78SpEDNr6F4Wlgq0rYf0KMlsOF5bmuQqdstXrFqq5Jkx0kMphEpPOiOTLEO0/z1Hd+suEKkDAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAMS+oWv/yYeFXUK7SLxwbWTsOR1nDVkT0P1AHKxaz51ntGuqv7I/vEVJ/cPXtljTMwbBiv57FGTxtTNl3EXurwhZwaws/BdNhECKHzD0qnV8ILxTBZbN9T3z8ZBm73ZksXdR9c3JH2g8vCJzQAfi43eYboB+Ngm3BBYFYSGP3fZtlNnRChdti19bZzZStp1ykHGJjtzKEYqoCwD0FkHDW2F2XyK1NYMLVdQw654dfWNNjrl//ZjSOMSah4/ztLIAJ3MoQROOw60zSJz7SfJLnUTtkpw3Huvt6RTrYXVjLMhGVCWflDHO3EqiP91N5qNQW+VUck/CjykCmqRE+naMt4=" - ], - "priority": ["100"] - } - }, - { - "id": "7efb4e8e-26ee-4cb1-818e-ef33d29e450c", - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "kid": ["530174f6-1e45-426e-aabe-59a422925464"], - "secret": [ - "NIedIMHffvbYazduY2Xet43VFVZGRQHGtzhHQLBta2fNmWloe0VEV0cwKe3qr7LSqjhpWnSuprh81V_p679oRQ" - ], - "priority": ["100"], - "algorithm": ["HS256"] - } - }, - { - "id": "e188e220-8591-4a68-b812-b53f988101bc", - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "kid": ["442f37b3-813c-4374-8c37-03df48a9452f"], - "secret": ["3eakciixtY2J72p8hxvZvQ"], - "priority": ["100"] - } - }, - { - "id": "1527f052-ef65-4598-a52b-3bfa817d45fd", - "name": "rsa-enc-generated", - "providerId": "rsa-enc-generated", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEpAIBAAKCAQEA2+PiZFaTQz8+EobmsXtEmbZDnTCUugm3iId4gsQvhaeollkG4RsD3Mq4eg5n3BEctx0CYbK4/r1rQNHBB7cbF8+pmqVZGzIDMke35fsVkpt23AdtJ76AlYi8gY5QFuIGtRb6QzG6d6jYvr/XWR21IOE/PtwkjcD3XHfI3OAnXK3VjL8fYtF9CxHJEMiFMnPrRzv4sw62Nz/o0YqlN1tXIYM7/deqyfuv/T1Ee8bUPKXA+i0/r4y7xFvI4rL1i50/Wg9CYvenAb82nKFulFyl7ujH9uQI634K8acHZSvWFZDkSNw6MjRjqpxlJNBGuFnHL9gMqCmr4LMQ7RD5tNElVQIDAQABAoIBAQDXi5D2bm84chv0qUwmGgFq8+Ffiqmfm7rPGIynEozK5Ug66NX3fzXQDt+6tbhVXmjZjpQxFLKG80LZbUNcvr7OHn/Q2dh48hN0DB1WUUQaTE687IKxfOoqqrit/sS/SZztkrKLf4mblmWwxfjgoGBW9LW8dqtWURupBrpc2VV8YBFQAUIgtV9fJ/kdopxGhG40Ic5F2a+S2lFzKTPWONivGX4YZmmOOqK/tLeCO2pI0k0jzLNkU1ayHE6s7rJfTLEijuW190X0wm8SQ2d87/xW2QwrTcYJ2IEF0jdPolC4EV3Wg9q2tS95wYsZOkHirh2BkrlOys0MFrRDWQnIFUpBAoGBAPA26dJkL6Gmdxm6+gVcGgVUqw4rlTozfOO5ZClnG1icPfOCzgbmFE92ib4wxKhJVMit06iInN8L7pwVifpT2bckoDQqlEJA8ns1jRInVXk8snJJ8EX2c3AIdTTNgIpaAy9TawzjQBOuYAv5SiQtm0Q5oxtF6Eggz0+dfy11VGf5AoGBAOpXDuwe//b/nNwOPw7gPSwGOD/xj8MzChoWyvg9xOVkTnvKgM5Hhr8cdQctiEEuZQGdVyGHXIb3hRblMRTbXWuhvHUyCEhK6G+bQZ91hJ17ekgcB0Y/DCPMzRAm/BW+RVtYmK2/neLyIkR8Mo756GiNBQZKasknkhQcCgbWhRc9AoGAFYmW5Vo+Owwl2ZS+HgToAFpbC+Y0dwduxyw0ItIvzX4Uv+SWfF/cqlNCfl75Au2wQF31gELfbdAwEKaNvHgQKS+LfaHjsR0JoKpof5wvObOLfFvMwzkZmI3plFZok/8GEhQ0oO8vK5SS3//1+dBs/2z7htJ5xR3L+KNyZ60BaGECgYEA44VvOZXHk3sOQyGZ28mf2vSW/I5+tFkDv9zNaEQgvU+sW8h7UsxjaX4pqbRtjEuNSgoHpMgV7/y2PUp9YvCxPWUk4uyW2gnQrzugdT/WaEiQqx57eZqRINkhaENRkZLRKCVW56sZklThLHkhZWwCWDBC5p6uG2vOCrRGj+kMA0kCgYBS52n46hv5N/hz2PWJdXjJks8LdBkDXkVtnl2I2CPa8QnBIKQjFLx83F5vcRk42UG1m9nmqEeeAaEDQvT+yXuwE64tKo4/7VpDteUy4bWyGSixOWIWxqOXfw/k8e/rN1vKoqqRhRMCFU9ujHbf7grAz9llc6m7rfONgRUcsuUQAA==" - ], - "keyUse": ["ENC"], - "certificate": [ - "MIICmzCCAYMCBgGA3taIRDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwNTIwMDAxOTAzWhcNMzIwNTIwMDAyMDQzWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb4+JkVpNDPz4Shuaxe0SZtkOdMJS6CbeIh3iCxC+Fp6iWWQbhGwPcyrh6DmfcERy3HQJhsrj+vWtA0cEHtxsXz6mapVkbMgMyR7fl+xWSm3bcB20nvoCViLyBjlAW4ga1FvpDMbp3qNi+v9dZHbUg4T8+3CSNwPdcd8jc4CdcrdWMvx9i0X0LEckQyIUyc+tHO/izDrY3P+jRiqU3W1chgzv916rJ+6/9PUR7xtQ8pcD6LT+vjLvEW8jisvWLnT9aD0Ji96cBvzacoW6UXKXu6Mf25AjrfgrxpwdlK9YVkORI3DoyNGOqnGUk0Ea4Wccv2AyoKavgsxDtEPm00SVVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADIvwG8QW0aWzUGDj6LfAgjPmBHiAywO9e6YpPnbHBN/Gjs1vFtKQpCCHPcnFsfHQEA6kjxE1tc3DtrkC26EITmEKJZaB0VbF9dl5t2FoMazzko3fqtwLYzsAVFoKg5+j9f15wVCIMC73Bd1zdefHjTqDtT4Qrh0qqvtloCYlehtWUCoBwUiEZ1bmDa/fNrMUSphdn/D6rf21gkVTiNaDiJo0U3CtI0NCCVeY3YIYTB3xV/XWqF3y3yUKs1+K/Utq21zzkhgSHCKNu2icL2kdG8MjXT65eWYSW+u25tXFzcMFaFXAHB89mCAtkXLx/fRA9GIkcw/3v91lcyUK0L/Yxk=" - ], - "priority": ["100"], - "algorithm": ["RSA-OAEP"] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "id": "98552c19-5c27-4d8b-a7cd-84ab2156b23d", - "alias": "Account verification options", - "description": "Method with which to verity the existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-email-verification", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false - } - ] - }, - { - "id": "59d20471-de63-4f6c-9c56-218aabcb6152", - "alias": "Authentication Options", - "description": "Authentication options.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "basic-auth", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "basic-auth-otp", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "49bc83a9-cd83-43d3-9ea5-44c11bb67c9d", - "alias": "Browser - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "b0bab724-72ee-4ff5-9a3d-adb3a1ab6ea0", - "alias": "Direct Grant - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "60195269-9d24-47f4-9e3e-1a1182f5f478", - "alias": "First broker login - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "cdbe3210-96b5-4536-9358-6a1851c61e3f", - "alias": "Handle Existing Account", - "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Account verification options", - "userSetupAllowed": false - } - ] - }, - { - "id": "ff8b92f8-7366-41ce-82f0-a84171a53ab6", - "alias": "Reset - Conditional OTP", - "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "b9bef8cd-4f31-410c-89c5-30c74e5b5d2c", - "alias": "User creation or linking", - "description": "Flow for the existing/non-existing user alternatives", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false - } - ] - }, - { - "id": "3f46f403-27ac-4cfc-a407-c68457b1b09e", - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "af81892a-9baf-4e24-affc-2eeaf214aac3", - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "identity-provider-redirector", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 25, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "forms", - "userSetupAllowed": false - } - ] - }, - { - "id": "0f3b9550-a017-4ac7-b0bb-89075b7ca70c", - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-secret-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-x509", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "cbc4a149-bf50-428c-83e7-3b7958fc4181", - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "2b53ceb8-399e-4d64-a2f3-dee989b23415", - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "b5f84a15-a965-43b2-8b44-06cf11c6a2e1", - "alias": "first broker login", - "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "User creation or linking", - "userSetupAllowed": false - } - ] - }, - { - "id": "70709004-1cd7-48d4-811a-7625f30527fa", - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "14f89f9d-b7f5-4370-bfd9-24da934083c5", - "alias": "http challenge", - "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "no-cookie-redirect", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Authentication Options", - "userSetupAllowed": false - } - ] - }, - { - "id": "23bc5075-1a3d-44ce-ae25-ac406c654316", - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": true, - "flowAlias": "registration form", - "userSetupAllowed": false - } - ] - }, - { - "id": "a5c8eb5b-2713-4e11-a3a1-d02e573555f4", - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-profile-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-password-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 50, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-recaptcha-action", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 60, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "1219613d-94bb-495a-88e2-bf545c9b0603", - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-credential-email", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 40, - "autheticatorFlow": true, - "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "4b6a994e-0052-44aa-9f28-11516818dcdc", - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - } - ], - "authenticatorConfig": [ - { - "id": "24e69fea-fba5-40b8-9cb5-73584051fd48", - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "id": "32baf553-ba2b-4dfd-b592-ed9d9b382511", - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "priority": 10, - "config": {} - }, - { - "alias": "terms_and_conditions", - "name": "Terms and Conditions", - "providerId": "terms_and_conditions", - "enabled": false, - "defaultAction": false, - "priority": 20, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "priority": 30, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "priority": 40, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "priority": 50, - "config": {} - }, - { - "alias": "delete_account", - "name": "Delete Account", - "providerId": "delete_account", - "enabled": false, - "defaultAction": false, - "priority": 60, - "config": {} - }, - { - "alias": "update_user_locale", - "name": "Update User Locale", - "providerId": "update_user_locale", - "enabled": true, - "defaultAction": false, - "priority": 1000, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "cibaBackchannelTokenDeliveryMode": "poll", - "cibaExpiresIn": "120", - "cibaAuthRequestedUserHint": "login_hint", - "oauth2DeviceCodeLifespan": "600", - "clientOfflineSessionMaxLifespan": "0", - "oauth2DevicePollingInterval": "600", - "clientSessionIdleTimeout": "0", - "parRequestUriLifespan": "60", - "clientSessionMaxLifespan": "0", - "clientOfflineSessionIdleTimeout": "0", - "cibaInterval": "5" - }, - "keycloakVersion": "18.0.0", - "userManagedAccessAllowed": false, - "clientProfiles": { - "profiles": [] - }, - "clientPolicies": { - "policies": [] - } -} diff --git a/apps/openchallenges/keycloak/data/import/master-users-0.json b/apps/openchallenges/keycloak/data/import/master-users-0.json deleted file mode 100644 index f61e54d6b0..0000000000 --- a/apps/openchallenges/keycloak/data/import/master-users-0.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "realm": "master", - "users": [ - { - "id": "c3ff63c5-5714-4b51-b885-3aa22cacd45b", - "createdTimestamp": 1653006044822, - "username": "admin", - "enabled": true, - "totp": false, - "emailVerified": false, - "credentials": [ - { - "id": "f5f1c17f-3968-45d0-95f5-e57f952ded99", - "type": "password", - "createdDate": 1653006044917, - "secretData": "{\"value\":\"UGU9dhW5CXUvnrwNlV10wbFpBzhuQA6jQgHvsvoLMln3fZguRHFgDGw1wsVDN6c+AFV1edY6uqELcIyWZawLIw==\",\"salt\":\"n9LcTzf0td1EUxCiFZ/XhQ==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-master", "admin"], - "notBefore": 0, - "groups": [] - } - ] -} diff --git a/apps/openchallenges/keycloak/data/import/test-realm.json b/apps/openchallenges/keycloak/data/import/test-realm.json deleted file mode 100644 index 3497f1c56b..0000000000 --- a/apps/openchallenges/keycloak/data/import/test-realm.json +++ /dev/null @@ -1,2408 +0,0 @@ -{ - "id": "test", - "realm": "test", - "displayName": "Sage Bionetworks", - "notBefore": 0, - "defaultSignatureAlgorithm": "RS256", - "revokeRefreshToken": false, - "refreshTokenMaxReuse": 0, - "accessTokenLifespan": 300, - "accessTokenLifespanForImplicitFlow": 900, - "ssoSessionIdleTimeout": 1800, - "ssoSessionMaxLifespan": 36000, - "ssoSessionIdleTimeoutRememberMe": 0, - "ssoSessionMaxLifespanRememberMe": 0, - "offlineSessionIdleTimeout": 2592000, - "offlineSessionMaxLifespanEnabled": false, - "offlineSessionMaxLifespan": 5184000, - "clientSessionIdleTimeout": 0, - "clientSessionMaxLifespan": 0, - "clientOfflineSessionIdleTimeout": 0, - "clientOfflineSessionMaxLifespan": 0, - "accessCodeLifespan": 60, - "accessCodeLifespanUserAction": 300, - "accessCodeLifespanLogin": 1800, - "actionTokenGeneratedByAdminLifespan": 43200, - "actionTokenGeneratedByUserLifespan": 300, - "oauth2DeviceCodeLifespan": 600, - "oauth2DevicePollingInterval": 5, - "enabled": true, - "sslRequired": "external", - "registrationAllowed": false, - "registrationEmailAsUsername": false, - "rememberMe": false, - "verifyEmail": false, - "loginWithEmailAllowed": true, - "duplicateEmailsAllowed": false, - "resetPasswordAllowed": false, - "editUsernameAllowed": false, - "bruteForceProtected": false, - "permanentLockout": false, - "maxFailureWaitSeconds": 900, - "minimumQuickLoginWaitSeconds": 60, - "waitIncrementSeconds": 60, - "quickLoginCheckMilliSeconds": 1000, - "maxDeltaTimeSeconds": 43200, - "failureFactor": 30, - "roles": { - "realm": [ - { - "id": "4c3bed96-f837-4f18-9b8d-f27c47e4f61d", - "name": "app-user", - "composite": true, - "composites": { - "client": { - "openchallenges-user-service": ["user"] - } - }, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "8c24c5fe-b842-4d81-aed2-7afdb5752428", - "name": "uma_authorization", - "description": "${role_uma_authorization}", - "composite": false, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "38e96ed0-77f6-4436-b338-c28ef6118473", - "name": "test-visitor", - "composite": false, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "54d9fb16-84a5-4dac-8222-9d8517bd829a", - "name": "default-roles-test", - "description": "${role_default-roles}", - "composite": true, - "composites": { - "realm": ["offline_access", "uma_authorization"], - "client": { - "account": ["view-profile", "manage-account"] - } - }, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "1c53db7d-a625-42ca-ad8f-cfd1f0280254", - "name": "test-admin", - "composite": false, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "a48c52cd-3e68-4d37-8ec1-ea2f7f793575", - "name": "app-admin", - "composite": true, - "composites": { - "client": { - "openchallenges-user-service": ["admin"] - } - }, - "clientRole": false, - "containerId": "test", - "attributes": {} - }, - { - "id": "a32f43d6-7de0-43bd-be05-ad5275a937ab", - "name": "offline_access", - "description": "${role_offline-access}", - "composite": false, - "clientRole": false, - "containerId": "test", - "attributes": {} - } - ], - "client": { - "realm-management": [ - { - "id": "e0c87220-0ad6-4e19-a849-aea7c7e28a92", - "name": "query-users", - "description": "${role_query-users}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "6de6a35c-9185-477a-a518-048bc2ad3a72", - "name": "view-clients", - "description": "${role_view-clients}", - "composite": true, - "composites": { - "client": { - "realm-management": ["query-clients"] - } - }, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "16d54035-245f-4958-a4c7-da1034384366", - "name": "manage-clients", - "description": "${role_manage-clients}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "b5754f3c-945b-4e71-bca5-2b6d1c77d4be", - "name": "query-realms", - "description": "${role_query-realms}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "49613790-ce00-4246-956c-af624caa689a", - "name": "view-identity-providers", - "description": "${role_view-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "6f4357f3-eef4-463e-8152-06a3a8d04374", - "name": "impersonation", - "description": "${role_impersonation}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "7cd794d0-fa86-465f-907a-2ea2c76e6ac8", - "name": "query-clients", - "description": "${role_query-clients}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "c00595c0-b22a-40ea-a477-c2f2b3d6abbf", - "name": "view-authorization", - "description": "${role_view-authorization}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "0e835119-1cff-49bb-b43f-9363de1d6025", - "name": "manage-authorization", - "description": "${role_manage-authorization}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "8ad22b20-bcc3-4fa7-bc2e-4111530b82b3", - "name": "manage-events", - "description": "${role_manage-events}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "a497f24f-0fc6-401c-bc8f-7644deeea779", - "name": "manage-users", - "description": "${role_manage-users}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "f3f6ae0a-9789-4ae7-ab09-90ce2e902abe", - "name": "view-events", - "description": "${role_view-events}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "086cb508-426c-45eb-8e12-ec4245c34d40", - "name": "query-groups", - "description": "${role_query-groups}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "2f60b452-0512-45fd-a507-9ecd5ead23ed", - "name": "create-client", - "description": "${role_create-client}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "56f5a522-c12e-4706-87ec-699e12b93efb", - "name": "view-realm", - "description": "${role_view-realm}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "41d46604-33e3-454c-a0d9-84fc287481ee", - "name": "view-users", - "description": "${role_view-users}", - "composite": true, - "composites": { - "client": { - "realm-management": ["query-users", "query-groups"] - } - }, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "bf7fe8f0-c8c4-4959-b8b7-50ff673123a2", - "name": "manage-identity-providers", - "description": "${role_manage-identity-providers}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "e2e90016-1799-45d7-9741-e44ffb415d39", - "name": "manage-realm", - "description": "${role_manage-realm}", - "composite": false, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - }, - { - "id": "7743d490-2d68-4daa-97db-c8529f5dc481", - "name": "realm-admin", - "description": "${role_realm-admin}", - "composite": true, - "composites": { - "client": { - "realm-management": [ - "query-users", - "view-clients", - "manage-clients", - "view-identity-providers", - "query-realms", - "impersonation", - "query-clients", - "view-authorization", - "manage-authorization", - "manage-events", - "manage-users", - "view-events", - "query-groups", - "create-client", - "view-realm", - "view-users", - "manage-identity-providers", - "manage-realm" - ] - } - }, - "clientRole": true, - "containerId": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "attributes": {} - } - ], - "openchallenges-core-client": [], - "security-admin-console": [], - "admin-cli": [], - "challenge-api-client": [ - { - "id": "4b5faf03-0976-4236-897b-9e8a6e124051", - "name": "uma_protection", - "composite": false, - "clientRole": true, - "containerId": "724cb050-5602-4412-a6ab-1ea2e5c2c6a9", - "attributes": {} - } - ], - "openchallenges-user-service": [ - { - "id": "c43eac20-e023-44d4-940c-6cd41c483c38", - "name": "admin", - "composite": false, - "clientRole": true, - "containerId": "5cbf6b55-3936-4ded-848f-1520e97f22f1", - "attributes": {} - }, - { - "id": "90732d98-6a15-434e-822a-cc5bf854f180", - "name": "user", - "composite": false, - "clientRole": true, - "containerId": "5cbf6b55-3936-4ded-848f-1520e97f22f1", - "attributes": {} - } - ], - "test-client": [ - { - "id": "b7e76271-f0a5-41ae-9739-1dd666954570", - "name": "uma_protection", - "composite": false, - "clientRole": true, - "containerId": "221d4622-5864-4eed-895e-5246058d817d", - "attributes": {} - } - ], - "account-console": [], - "broker": [ - { - "id": "f285a723-eafc-4a84-829b-bb33e045855d", - "name": "read-token", - "description": "${role_read-token}", - "composite": false, - "clientRole": true, - "containerId": "98f68c54-3470-4c9e-8ecb-dcbc7ff4c2ed", - "attributes": {} - } - ], - "account": [ - { - "id": "a1c7fc34-fc90-45dd-9b50-fca30c90cebc", - "name": "view-consent", - "description": "${role_view-consent}", - "composite": false, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "1d1976cf-e967-4b8f-9024-fd062ed47d47", - "name": "view-profile", - "description": "${role_view-profile}", - "composite": false, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "8565c16a-1683-43e1-9b98-c3fcd8ca9892", - "name": "delete-account", - "description": "${role_delete-account}", - "composite": false, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "2c5c6596-a073-449d-b774-c73b8d4e7378", - "name": "manage-account", - "description": "${role_manage-account}", - "composite": true, - "composites": { - "client": { - "account": ["manage-account-links"] - } - }, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "c036f478-2582-4746-bbb3-8ec928998ac2", - "name": "manage-consent", - "description": "${role_manage-consent}", - "composite": true, - "composites": { - "client": { - "account": ["view-consent"] - } - }, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "e4cc2517-a55b-4f22-8110-5493cde493bb", - "name": "manage-account-links", - "description": "${role_manage-account-links}", - "composite": false, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - }, - { - "id": "3c3073cb-3eda-4562-bc6b-5ccb30e1bd17", - "name": "view-applications", - "description": "${role_view-applications}", - "composite": false, - "clientRole": true, - "containerId": "592f24bc-00d7-4ca4-8c27-952520162bed", - "attributes": {} - } - ] - } - }, - "groups": [], - "defaultRole": { - "id": "54d9fb16-84a5-4dac-8222-9d8517bd829a", - "name": "default-roles-test", - "description": "${role_default-roles}", - "composite": true, - "clientRole": false, - "containerId": "test" - }, - "requiredCredentials": ["password"], - "otpPolicyType": "totp", - "otpPolicyAlgorithm": "HmacSHA1", - "otpPolicyInitialCounter": 0, - "otpPolicyDigits": 6, - "otpPolicyLookAheadWindow": 1, - "otpPolicyPeriod": 30, - "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": ["ES256"], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], - "scopeMappings": [ - { - "clientScope": "offline_access", - "roles": ["offline_access"] - } - ], - "clientScopeMappings": { - "account": [ - { - "client": "account-console", - "roles": ["manage-account"] - } - ] - }, - "clients": [ - { - "id": "592f24bc-00d7-4ca4-8c27-952520162bed", - "clientId": "account", - "name": "${client_account}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/test/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/test/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "389933c0-7b15-43c6-8be0-403c989e7cc8", - "clientId": "account-console", - "name": "${client_account-console}", - "rootUrl": "${authBaseUrl}", - "baseUrl": "/realms/test/account/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/test/account/*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "2f3b0f4b-fa01-44a9-9e6f-b2c6bab0b72d", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "2623be63-ef04-4204-8c9b-06f4023bc2b1", - "clientId": "admin-cli", - "name": "${client_admin-cli}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": false, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "98f68c54-3470-4c9e-8ecb-dcbc7ff4c2ed", - "clientId": "broker", - "name": "${client_broker}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "724cb050-5602-4412-a6ab-1ea2e5c2c6a9", - "clientId": "challenge-api-client", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "mg2DrRcxHx19PIITibdOnbNEbJUKjGKb", - "redirectUris": ["*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": true, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "frontchannel.logout.session.required": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false", - "saml.server.signature.keyinfo.ext": "false", - "use.refresh.tokens": "true", - "oidc.ciba.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "client_credentials.use_refresh_token": "false", - "require.pushed.authorization.requests": "false", - "saml.client.signature": "false", - "saml.allow.ecp.flow": "false", - "id.token.as.detached.signature": "false", - "saml.assertion.signature": "false", - "client.secret.creation.time": "1653876202", - "saml.encrypt": "false", - "saml.server.signature": "false", - "exclude.session.state.from.auth.response": "false", - "saml.artifact.binding": "false", - "saml_force_name_id_format": "false", - "acr.loa.map": "{}", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "token.response.type.bearer.lower-case": "false", - "saml.onetimeuse.condition": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "1ee01d4c-1ca3-4a1f-b961-6fc978f2ffcb", - "name": "Client Host", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientHost", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientHost", - "jsonType.label": "String" - } - }, - { - "id": "e8969a1c-defb-4187-a2d0-0afa7b9b96cc", - "name": "Client ID", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientId", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientId", - "jsonType.label": "String" - } - }, - { - "id": "b3c3d10e-9fbe-4acb-b7b3-605f97f063c8", - "name": "Client IP Address", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientAddress", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientAddress", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "64819ac4-1632-40ff-8a7a-b022bc2f5a21", - "clientId": "challenge-core-client", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX", - "redirectUris": ["*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "frontchannel.logout.session.required": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false", - "saml.server.signature.keyinfo.ext": "false", - "use.refresh.tokens": "true", - "oidc.ciba.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "client_credentials.use_refresh_token": "false", - "require.pushed.authorization.requests": "false", - "saml.client.signature": "false", - "saml.allow.ecp.flow": "false", - "id.token.as.detached.signature": "false", - "saml.assertion.signature": "false", - "client.secret.creation.time": "1653866497", - "saml.encrypt": "false", - "saml.server.signature": "false", - "exclude.session.state.from.auth.response": "false", - "saml.artifact.binding": "false", - "saml_force_name_id_format": "false", - "acr.loa.map": "{}", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "token.response.type.bearer.lower-case": "false", - "saml.onetimeuse.condition": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "5cbf6b55-3936-4ded-848f-1520e97f22f1", - "clientId": "openchallenges-user-service", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "secret": "GFRau7cQTxhx19CUmSdrqrqrZ1pshjjB", - "redirectUris": ["*"], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "frontchannel.logout.session.required": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false", - "saml.server.signature.keyinfo.ext": "false", - "use.refresh.tokens": "true", - "oidc.ciba.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "client_credentials.use_refresh_token": "false", - "require.pushed.authorization.requests": "false", - "saml.client.signature": "false", - "saml.allow.ecp.flow": "false", - "id.token.as.detached.signature": "false", - "saml.assertion.signature": "false", - "client.secret.creation.time": "1657317060", - "saml.encrypt": "false", - "saml.server.signature": "false", - "exclude.session.state.from.auth.response": "false", - "saml.artifact.binding": "false", - "saml_force_name_id_format": "false", - "acr.loa.map": "{}", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "token.response.type.bearer.lower-case": "false", - "saml.onetimeuse.condition": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "4cb5ef74-bcf5-4796-bb98-cb03ebcc1aa8", - "clientId": "realm-management", - "name": "${client_realm-management}", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [], - "webOrigins": [], - "notBefore": 0, - "bearerOnly": true, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": false, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": {}, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "cc6820e8-ff87-4e48-85a9-7e3e02ad802f", - "clientId": "security-admin-console", - "name": "${client_security-admin-console}", - "rootUrl": "${authAdminUrl}", - "baseUrl": "/admin/test/console/", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/test/console/*"], - "webOrigins": ["+"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": false, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "pkce.code.challenge.method": "S256" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": false, - "nodeReRegistrationTimeout": 0, - "protocolMappers": [ - { - "id": "e269bd7a-9b04-4390-97a3-ba74f19342f6", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - }, - { - "id": "221d4622-5864-4eed-895e-5246058d817d", - "clientId": "test-client", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": ["http://localhost:80/*", "http://localhost:4200/*"], - "webOrigins": ["+"], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": true, - "publicClient": true, - "frontchannelLogout": false, - "protocol": "openid-connect", - "attributes": { - "saml.force.post.binding": "false", - "saml.multivalued.roles": "false", - "frontchannel.logout.session.required": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.revoke.offline.tokens": "false", - "saml.server.signature.keyinfo.ext": "false", - "use.refresh.tokens": "true", - "oidc.ciba.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "client_credentials.use_refresh_token": "false", - "require.pushed.authorization.requests": "false", - "saml.client.signature": "false", - "saml.allow.ecp.flow": "false", - "id.token.as.detached.signature": "false", - "saml.assertion.signature": "false", - "client.secret.creation.time": "1653435163", - "saml.encrypt": "false", - "saml.server.signature": "false", - "exclude.session.state.from.auth.response": "false", - "saml.artifact.binding": "false", - "saml_force_name_id_format": "false", - "acr.loa.map": "{}", - "tls.client.certificate.bound.access.tokens": "false", - "saml.authnstatement": "false", - "display.on.consent.screen": "false", - "token.response.type.bearer.lower-case": "false", - "saml.onetimeuse.condition": "false" - }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "protocolMappers": [ - { - "id": "974f99e4-0596-48ee-a850-189591b9ef7b", - "name": "Client Host", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientHost", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientHost", - "jsonType.label": "String" - } - }, - { - "id": "74f6396f-3771-45e8-8b68-3adcef1f200c", - "name": "Client IP Address", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientAddress", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientAddress", - "jsonType.label": "String" - } - }, - { - "id": "7de6877a-3b6e-44cf-a0a8-041b07be4560", - "name": "Client ID", - "protocol": "openid-connect", - "protocolMapper": "oidc-usersessionmodel-note-mapper", - "consentRequired": false, - "config": { - "user.session.note": "clientId", - "userinfo.token.claim": "true", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "clientId", - "jsonType.label": "String" - } - } - ], - "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "email"], - "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] - } - ], - "clientScopes": [ - { - "id": "e439c0fe-3a2b-4c38-aef1-ee01c421ec32", - "name": "acr", - "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "5a875744-a88f-4a35-8d10-cfb1f76670e9", - "name": "acr loa level", - "protocol": "openid-connect", - "protocolMapper": "oidc-acr-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - } - ] - }, - { - "id": "d0e654be-395b-4a53-9e3f-c98c924fb540", - "name": "profile", - "description": "OpenID Connect built-in scope: profile", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "0c2d5b2b-4160-4031-81e9-d009f33e4b8b", - "name": "picture", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "picture", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "picture", - "jsonType.label": "String" - } - }, - { - "id": "33b0a3be-87ce-4729-89ba-d827f520063d", - "name": "given name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "firstName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "given_name", - "jsonType.label": "String" - } - }, - { - "id": "fa67e5aa-5453-402d-8d11-0fdb8dd91ce2", - "name": "locale", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "locale", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "locale", - "jsonType.label": "String" - } - }, - { - "id": "f827e7c9-0ab9-4749-9df6-0cc59a0a9c11", - "name": "updated at", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "updatedAt", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "updated_at", - "jsonType.label": "long" - } - }, - { - "id": "612acd11-1160-4f7e-9e6e-dbb19afbfea4", - "name": "zoneinfo", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "zoneinfo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "zoneinfo", - "jsonType.label": "String" - } - }, - { - "id": "aa62ee3b-53ee-4e83-9f1d-8adae8adb895", - "name": "username", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "preferred_username", - "jsonType.label": "String" - } - }, - { - "id": "cb6d3d99-c87f-413d-b444-1e2616d62c86", - "name": "full name", - "protocol": "openid-connect", - "protocolMapper": "oidc-full-name-mapper", - "consentRequired": false, - "config": { - "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" - } - }, - { - "id": "b45b9761-c7fa-491f-8120-b2d2f5c2f5de", - "name": "family name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "lastName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "family_name", - "jsonType.label": "String" - } - }, - { - "id": "8198a7f1-d2e4-4949-a680-caeae032e5d2", - "name": "nickname", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "nickname", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "nickname", - "jsonType.label": "String" - } - }, - { - "id": "1e9d2549-51d5-4162-a38d-68489691030d", - "name": "profile", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "profile", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "profile", - "jsonType.label": "String" - } - }, - { - "id": "d98d4b19-0109-4cb0-b396-db0dabde5755", - "name": "gender", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "gender", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "gender", - "jsonType.label": "String" - } - }, - { - "id": "69c51d6c-c246-4f3b-8d63-336f70a7dade", - "name": "birthdate", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "birthdate", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "birthdate", - "jsonType.label": "String" - } - }, - { - "id": "c6f170f4-f4ab-4d51-b7bb-a95e9eae0192", - "name": "middle name", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "middleName", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "middle_name", - "jsonType.label": "String" - } - }, - { - "id": "785bb096-1b78-41f0-985c-e0699220d9bf", - "name": "website", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "website", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "website", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "6ef0c756-8e45-4e88-8527-954ba7afa7c0", - "name": "microprofile-jwt", - "description": "Microprofile - JWT built-in scope", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "false" - }, - "protocolMappers": [ - { - "id": "fe39e07f-6fbb-4ad8-9719-caa8485747e2", - "name": "groups", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "multivalued": "true", - "userinfo.token.claim": "true", - "user.attribute": "foo", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "groups", - "jsonType.label": "String" - } - }, - { - "id": "6f6eb5ec-297a-4781-8faa-f8400e3d8962", - "name": "upn", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "username", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "upn", - "jsonType.label": "String" - } - } - ] - }, - { - "id": "06f7b761-f567-4151-a38c-e740cb699a7e", - "name": "email", - "description": "OpenID Connect built-in scope: email", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "0a5bcb99-eecd-43c7-9522-371850248a3a", - "name": "email", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "email", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email", - "jsonType.label": "String" - } - }, - { - "id": "af4d2e1b-387b-44e5-8aeb-d262a11ccc7e", - "name": "email verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-property-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "emailVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "email_verified", - "jsonType.label": "boolean" - } - } - ] - }, - { - "id": "bd1e6887-122f-4970-b2d9-1e9ff7def55f", - "name": "address", - "description": "OpenID Connect built-in scope: address", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "e13c4648-2fb9-4d08-8800-beb5229c87ac", - "name": "address", - "protocol": "openid-connect", - "protocolMapper": "oidc-address-mapper", - "consentRequired": false, - "config": { - "user.attribute.formatted": "formatted", - "user.attribute.country": "country", - "user.attribute.postal_code": "postal_code", - "userinfo.token.claim": "true", - "user.attribute.street": "street", - "id.token.claim": "true", - "user.attribute.region": "region", - "access.token.claim": "true", - "user.attribute.locality": "locality" - } - } - ] - }, - { - "id": "993e30e1-f926-4c30-a48b-9e9f5b54d424", - "name": "web-origins", - "description": "OpenID Connect scope for add allowed web origins to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" - }, - "protocolMappers": [ - { - "id": "761cb3ca-e074-40a1-92fa-bdefe20f1d64", - "name": "allowed web origins", - "protocol": "openid-connect", - "protocolMapper": "oidc-allowed-origins-mapper", - "consentRequired": false, - "config": {} - } - ] - }, - { - "id": "767b5914-e8b9-49f7-baac-72b7b010ea74", - "name": "offline_access", - "description": "OpenID Connect built-in scope: offline_access", - "protocol": "openid-connect", - "attributes": { - "consent.screen.text": "${offlineAccessScopeConsentText}", - "display.on.consent.screen": "true" - } - }, - { - "id": "9a1afa98-7370-440a-8378-93fe4c808c14", - "name": "role_list", - "description": "SAML role list", - "protocol": "saml", - "attributes": { - "consent.screen.text": "${samlRoleListScopeConsentText}", - "display.on.consent.screen": "true" - }, - "protocolMappers": [ - { - "id": "d02e9a12-5bae-49a8-bdfc-0d894a824dbc", - "name": "role list", - "protocol": "saml", - "protocolMapper": "saml-role-list-mapper", - "consentRequired": false, - "config": { - "single": "false", - "attribute.nameformat": "Basic", - "attribute.name": "Role" - } - } - ] - }, - { - "id": "e5ab11c2-d812-4588-b576-177081400c7e", - "name": "phone", - "description": "OpenID Connect built-in scope: phone", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "dea34d1d-4c00-4e58-a5b9-a9b8918db0ad", - "name": "phone number", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumber", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number", - "jsonType.label": "String" - } - }, - { - "id": "0a1f4b2d-9b77-419d-8356-6f4f31d75566", - "name": "phone number verified", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-attribute-mapper", - "consentRequired": false, - "config": { - "userinfo.token.claim": "true", - "user.attribute": "phoneNumberVerified", - "id.token.claim": "true", - "access.token.claim": "true", - "claim.name": "phone_number_verified", - "jsonType.label": "boolean" - } - } - ] - }, - { - "id": "b3f4cc10-7183-4175-888e-6e90acc0cb54", - "name": "roles", - "description": "OpenID Connect scope for add user roles to the access token", - "protocol": "openid-connect", - "attributes": { - "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" - }, - "protocolMappers": [ - { - "id": "d5602d93-259c-4092-bd1d-eba01bcaeee3", - "name": "client roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-client-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "resource_access.${client_id}.roles", - "jsonType.label": "String", - "multivalued": "true" - } - }, - { - "id": "1b4ced2d-8809-4987-9c2c-43d0b16fa626", - "name": "realm roles", - "protocol": "openid-connect", - "protocolMapper": "oidc-usermodel-realm-role-mapper", - "consentRequired": false, - "config": { - "user.attribute": "foo", - "access.token.claim": "true", - "claim.name": "realm_access.roles", - "jsonType.label": "String", - "multivalued": "true" - } - }, - { - "id": "ca7478cc-f23f-4ec9-a02f-1f1d0d4e06c4", - "name": "audience resolve", - "protocol": "openid-connect", - "protocolMapper": "oidc-audience-resolve-mapper", - "consentRequired": false, - "config": {} - } - ] - } - ], - "defaultDefaultClientScopes": ["role_list", "profile", "email", "roles", "web-origins", "acr"], - "defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt"], - "browserSecurityHeaders": { - "contentSecurityPolicyReportOnly": "", - "xContentTypeOptions": "nosniff", - "xRobotsTag": "none", - "xFrameOptions": "SAMEORIGIN", - "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection": "1; mode=block", - "strictTransportSecurity": "max-age=31536000; includeSubDomains" - }, - "smtpServer": {}, - "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], - "enabledEventTypes": [], - "adminEventsEnabled": false, - "adminEventsDetailsEnabled": false, - "identityProviders": [], - "identityProviderMappers": [], - "components": { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ - { - "id": "433dd41f-76ed-45d6-a3d5-ef29ed98203d", - "name": "Consent Required", - "providerId": "consent-required", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "e09b8e4c-d3da-4a96-bb77-69e1f1c732dc", - "name": "Max Clients Limit", - "providerId": "max-clients", - "subType": "anonymous", - "subComponents": {}, - "config": { - "max-clients": ["200"] - } - }, - { - "id": "c18eb82e-4a69-485d-8410-eea9904d1aa9", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "id": "f3aabba7-3b06-4163-9eee-3965de61b5fa", - "name": "Full Scope Disabled", - "providerId": "scope", - "subType": "anonymous", - "subComponents": {}, - "config": {} - }, - { - "id": "8f98b3d0-b329-47b7-95e8-ff483a04da90", - "name": "Trusted Hosts", - "providerId": "trusted-hosts", - "subType": "anonymous", - "subComponents": {}, - "config": { - "host-sending-registration-request-must-match": ["true"], - "client-uris-must-match": ["true"] - } - }, - { - "id": "40762585-55aa-4360-aefa-4d55e34abdb3", - "name": "Allowed Client Scopes", - "providerId": "allowed-client-templates", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allow-default-scopes": ["true"] - } - }, - { - "id": "1a0e466c-0c80-4577-a12d-760832dbc3f1", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "anonymous", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-address-mapper", - "saml-user-attribute-mapper", - "saml-role-list-mapper", - "saml-user-property-mapper", - "oidc-usermodel-property-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-attribute-mapper", - "oidc-full-name-mapper" - ] - } - }, - { - "id": "d771eb2e-8b82-43c1-be76-7c37fbeee712", - "name": "Allowed Protocol Mapper Types", - "providerId": "allowed-protocol-mappers", - "subType": "authenticated", - "subComponents": {}, - "config": { - "allowed-protocol-mapper-types": [ - "oidc-address-mapper", - "saml-user-property-mapper", - "oidc-usermodel-attribute-mapper", - "saml-user-attribute-mapper", - "saml-role-list-mapper", - "oidc-full-name-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper" - ] - } - } - ], - "org.keycloak.keys.KeyProvider": [ - { - "id": "d5305d0b-702c-4a4c-8d36-a84f7f4c5934", - "name": "rsa-generated", - "providerId": "rsa-generated", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEpAIBAAKCAQEAiN30RqkCq6cvENMgVIWCA1mL/ReiperZfbsHEQ0Gr/WIoxlFO0EifUPXDRaRyrRdmfBl+lv2pHvAOJui7g42bJND2GGWZmBOoVsYtzgA2iw4CL1w3iYDFy4WFTGLi9noe6ZA8LzVGyqLrDu8yMg5RFBNmL7moUT/IAN607UGNBUX1rGLREBqWCLR7IxmBgiOZUfygKTVUPwel1zWPxpx/X7b5OfVFHhOuOUbP8behzmyCsyiXz+2bDT9n+rZvIc5FZdGv8cUpX2RiAWnxFkG3Rtvvm0EsP9F8A0SOy/ruSwBtRcbKqWVn9DghL3harH+7Fifz3bXuC3/Yudy2arpVQIDAQABAoIBACxg2431yznhUa+A5aDCUW/In02HVmO6DJ9p/1aWBK8VTCKmVP5rlkVLs/U4djll1lvNySMuooCe56rzPBc4YIlADuWgbuXvIE/Ne+wcqpoTcG/VDT3a0XG4rcxdbkQAehNtyuzELc1LNFK6Y/IuifMOnrqCW8G634Z/2lm+/q3wEw/8qeJEvWXo2cKa9ysQ2Ww8/azIiCtwv3TJot5DIN5gOWthq7VTyZ0TCNNymRACR3JQgZ12G6ivxt1jjB4UjgtvYwz5ypqXxdlggBFeWiq+LtKN5+csoYpJhGj3JE0LtxuZggNsaAfnGoB3vmfFdheq2qFEjq8eBf5/ItRH0MECgYEA9ZjXFCP/jH6CFyp6AwwtI4BvE8aMkeFI6S7VeY7l4Q1Ept0qQCAviizsytj1ZKhocQYiRsUyZLK+U4TadC9oiKjCFGy6EB5hbx3DA1CQae+DQJ8CdLTOevjbJ6zvy94t+tRgiBsMgpD96otibGEwZybWfFRQ7D9HTDG3ol1hmkkCgYEAjqoWY5MN6Vv5WgbaDquZZM8iJCs8/tc4reJujJzbmNjkHw+nhcl5noo6NnJLRANBcVgIG9+K9OFYmbUqQXBLkzL3MAHLm3LdxgY9zmCsBNU0/mtspzLm4zeiTpCFTrdbXZnL+YWhReSMoA9Bf06cGRhbS5imLGLACiQ8D/TYdq0CgYEA3qLpBncDqEbeg0WcSbr2CGLbTuAEehHx2ID+QC3MMlGm1GVVccA7mFTVa6V1/wOzehIHbIRDBIoMgwXCSuTXLFtfZMiTex0vUl12dUDJSV84apI61bU6ILwLX1IkJ3mH0QRJZdivZIUvyTkFxh3slPXeJeMBYMiUR3wibeudGRECgYAfuJn50fzJ4nVzGwrpMCaPT68bs7GASIKFx08lXMtHFRLEBUsOaFnucnW3HXq2yLKIbTaAXBgdku2MX8fhfQKQ3EgyGHQZQFUnbsPXW49kE7QQzfn+w0arRveD2IaLliFukn/UH25YiBTskffSQrxaqBvEl1B7lgl1N9FTCvIGkQKBgQCTIYIboRY9xr4iGu0vjVNXIuMUoRGLFFydbDhroCF/1eezZJs0aQE2IsXg3rzjjcMix1SYMT6YvU7ET7Q4ZiUpQ5+RYqIlLUWsPC7yajSCalRBafUcKbXSEKg1Kyz8VrugGODK+UK1g/0VA2K3DPIiRF1xQKxaiV+NDflwLoztzQ==" - ], - "keyUse": ["SIG"], - "certificate": [ - "MIIClzCCAX8CBgGA3snB1jANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUyMDAwMDUwNVoXDTMyMDUyMDAwMDY0NVowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIjd9EapAqunLxDTIFSFggNZi/0XoqXq2X27BxENBq/1iKMZRTtBIn1D1w0Wkcq0XZnwZfpb9qR7wDibou4ONmyTQ9hhlmZgTqFbGLc4ANosOAi9cN4mAxcuFhUxi4vZ6HumQPC81Rsqi6w7vMjIOURQTZi+5qFE/yADetO1BjQVF9axi0RAalgi0eyMZgYIjmVH8oCk1VD8Hpdc1j8acf1+2+Tn1RR4TrjlGz/G3oc5sgrMol8/tmw0/Z/q2byHORWXRr/HFKV9kYgFp8RZBt0bb75tBLD/RfANEjsv67ksAbUXGyqllZ/Q4IS94Wqx/uxYn89217gt/2Lnctmq6VUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVv4VL8N6W7B4X5XC1jYJF7cBEP1Ci48UBehJ4Aupv1LQB9vp5nu7ivvB5yNdzYclQ7uM6j6dGagY/7wU0n18si8HWicgszRx6DsoH6p37H6RnV1DoS6Ju9pk8zy9Yte3eeoz9XqqH5WTv/w2q+0glGyb+cadGWenJnP0pg+5S0AUCPdLEPBqAz/6ztikpqFM0afVuq/H1TD4PDJkvdt8rbaTck3zD8u5ioLMuEfH/Lo1cECTz2TOBb+z0MMGwb8B+4nbqq0g7cuB+zCD9eMgqhEe42pBQXSqRcsRdAbssmwPtuEQdWhadYKVfud3ndYscgB86dJqKbdqO4mwJxxOEw==" - ], - "priority": ["100"] - } - }, - { - "id": "04307bb4-06d6-4075-9658-57c11ffc0442", - "name": "rsa-enc-generated", - "providerId": "rsa-enc-generated", - "subComponents": {}, - "config": { - "privateKey": [ - "MIIEogIBAAKCAQEAiTWSd9sKFdGEOk6L8/ZwRY99716AIKggGLQRstgbjEfl0vVnR6fsbVA43qAK0YFLTj4cpQ8RlHR3ndZM0VGZmxLC4mcCLftNwPlKe5LlrMZWc4wZxQymch0CWwMRhaDGJkTc96d3tzpDbWOL7HQJhRGGSdjSxY4A3DoBNI+0ZwO+iZsrh/5CvzLP/pq2NtFhKmymChtuG8M1YOLG0NU2DKTCjqjGpr9BOLPiym5Ffo4bj3gIcnFoO3LMNMcheSPy90xobRU2clkhxKRI4Ju//hg2oQVlgg2EoJ0uIYML4rYiAEvAmo81QkbbpoCgk8Y7vuXymxLqAtRg7uf94DdVQwIDAQABAoIBAD2YGwRLBB//SgiSsij2fDf9zl7iYUpHrwDn9PoAIX272OP23kJAk9YOqTjnR3P9Qr0BDQLb3Nxz3C6ywRFGPFps9aeE7mmN4r26K0GGFCyXbL+6KOdYYm/fresFWZxPLCSJzVgviYJrWVGF2o5vGMbvswT7nMKqmKBEOb5KtIV5dIy5mVl7CdpDDFO5qRrkNpp+mNhLw9WVSKiFFxId0jMHfQGXOrtVqR7Zd3hqZBsZfiF8ObDoFb9c1SWWZctJNoL8Z+BauBYvi3k00k9t7aijUIst4GHDraDFWzQwSWTdKd63BX2qIJGXIggapzYoQNX9TKmq03ACBygxbL67OIECgYEAvSoq1huVF1NGGkQ0I9cgkWLduIc0tXT5sha8LlEcLeSyL7no+DrSHIiBBwKn/AnLrYnw24TDIwPtp/l7FXaggZtuKslQp2DPk3AURFsBP9ZVaQ1QiN8dzR1hvvf7AxH/LvjNNgdw7PWScUcKfJwfyrnhX1fLrQKd+d+5vWtgu7ECgYEAubAQ8k2jgr05ZnwmWNWRZ0SAl+XHz+zXqNR0ooevhum77WwenaK+JqILePLz8r2hP6smhpRaOZitO+D6kiNy5On06NhWZKqnOzTZ/DN1bCd5Wf+en94tFec9R3g7lgyPJFnNp3U5GT54ueCEYdiJvC59BEC96wHnv2m90cVqQTMCgYBBi/v95t7Zj0RqU5nRlXi2iBCWIVuVSOxA1nnsfrSJ/oXz3bKpWRB3TKGRAU2eGvLVtkeceTt4xaKzItKQlGN3pZkeAFYPOY+T4jFTokkjiqxb035FfzJ/YZ/+IUV8Qd8vsozBHQD3SvysfdZXaW3mvFQoym5MC+0bqXdd+HgzcQKBgAFWW0jjdkICyt8KFdyd7SYpQCPX69B7jDITFaP2BjWaiVf0Z2QLQANXN23vkgI1i2vB980QVHYFu3mq7SMQTkuHw9PJexXOhNXQU6GVXUsiHaWgSawtFXYK7E4Pd55lCMLiQrDqDzc3U/dCvrFVteg9DXlyXevLX7fpMwzHtzQxAoGAZ6iMC8mqOFkQtEvdwNTr/Mr+ip30JCRuPgbdX295BM6Su1SES3umVjo59vFezRRAjlFUMo1TeIM4dnqIA2S1NkNdSR01Lc24xukQ7Dd9FTzc4ZsXKGuOdaDn+/2z+hdURJai55HCn2Qk8mPIfkRC0hq3Kkss2+1It5GWgS7H6wU=" - ], - "keyUse": ["ENC"], - "certificate": [ - "MIIClzCCAX8CBgGA3snCGjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUyMDAwMDUwNloXDTMyMDUyMDAwMDY0NlowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIk1knfbChXRhDpOi/P2cEWPfe9egCCoIBi0EbLYG4xH5dL1Z0en7G1QON6gCtGBS04+HKUPEZR0d53WTNFRmZsSwuJnAi37TcD5SnuS5azGVnOMGcUMpnIdAlsDEYWgxiZE3Pend7c6Q21ji+x0CYURhknY0sWOANw6ATSPtGcDvombK4f+Qr8yz/6atjbRYSpspgobbhvDNWDixtDVNgykwo6oxqa/QTiz4spuRX6OG494CHJxaDtyzDTHIXkj8vdMaG0VNnJZIcSkSOCbv/4YNqEFZYINhKCdLiGDC+K2IgBLwJqPNUJG26aAoJPGO77l8psS6gLUYO7n/eA3VUMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABAUxoBR2GORuPM1FaezXimtQaunSanW6VCAy8sLUcdOqeD5we6GMrmeTGQfha8AW6DQAEPoVulzxVXyk1vaFoW5M2paWuuH4MD5aGsVB/zBf00M5MUgBeaD/TmQPII0bKFgpAwenuq1uvzizF7w4XyXHS5GNq2FTdheaSIXL3qHw+beAMAJxqqf2cFygwuofQ8jiaN06DN2GQWcEqxiStpRQe8lQldSS1ebNUuVybGcR0A5140d+TK1+XMElWQnSlBTrpzMiDmGQcfonfW9t5qqzspeGVVQoHqu/oY7VpPr++bS0/2gReKUlOsIZ6xBLzwcXcExycHrfynz1q6j5YQ==" - ], - "priority": ["100"], - "algorithm": ["RSA-OAEP"] - } - }, - { - "id": "374d7b93-c119-49d2-9fe3-a80a95574d80", - "name": "hmac-generated", - "providerId": "hmac-generated", - "subComponents": {}, - "config": { - "kid": ["8590d7d0-5d1b-4f1b-913f-3fe3aaca332c"], - "secret": [ - "LhcXhbldGHJNJG4PcPSRnH8sOvw9v5O8_2T8v6HUzhvzrQjOMPN-iQg1P6Sb_vAjjjJL-QbOCG9VThzkvspefQ" - ], - "priority": ["100"], - "algorithm": ["HS256"] - } - }, - { - "id": "57f22280-a024-4d4d-9c85-d3020224353b", - "name": "aes-generated", - "providerId": "aes-generated", - "subComponents": {}, - "config": { - "kid": ["45369d18-b1a9-4180-8007-fcba10d52c77"], - "secret": ["uGs86Yi6xN9UEKp_dgBTZg"], - "priority": ["100"] - } - } - ] - }, - "internationalizationEnabled": false, - "supportedLocales": [], - "authenticationFlows": [ - { - "id": "c7f39c73-e55f-4e7b-a7e9-9c420d539114", - "alias": "Account verification options", - "description": "Method with which to verity the existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-email-verification", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false - } - ] - }, - { - "id": "5c73dfd3-932f-4308-8b6a-0a7abf27479b", - "alias": "Authentication Options", - "description": "Authentication options.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "basic-auth", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "basic-auth-otp", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "ce9c44b7-0d2a-4871-813e-f215365f36b6", - "alias": "Browser - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "31a405a9-d7ee-4342-86fe-b736a5496e44", - "alias": "Direct Grant - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "fc83df46-f487-40a9-b09b-f1e04edfc856", - "alias": "First broker login - Conditional OTP", - "description": "Flow to determine if the OTP is required for the authentication", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-otp-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "f1f4f794-e1fd-4e56-a8ad-a4b938eac206", - "alias": "Handle Existing Account", - "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-confirm-link", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Account verification options", - "userSetupAllowed": false - } - ] - }, - { - "id": "25d132f0-d4bf-4828-8497-6bdae92dbb79", - "alias": "Reset - Conditional OTP", - "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "conditional-user-configured", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-otp", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "1abe64db-dadf-457c-8ee2-cf01befebd0e", - "alias": "User creation or linking", - "description": "Flow for the existing/non-existing user alternatives", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "create unique user config", - "authenticator": "idp-create-user-if-unique", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Handle Existing Account", - "userSetupAllowed": false - } - ] - }, - { - "id": "9af02c9b-cf03-4b80-851a-2b69bd9542d1", - "alias": "Verify Existing Account by Re-authentication", - "description": "Reauthentication of existing account", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "idp-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "23c3552b-06d9-4004-8633-d423fef9cb3e", - "alias": "browser", - "description": "browser based authentication", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-cookie", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "auth-spnego", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "identity-provider-redirector", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 25, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "forms", - "userSetupAllowed": false - } - ] - }, - { - "id": "c93a96cd-98d6-442c-b803-3d4e5e109074", - "alias": "clients", - "description": "Base authentication for clients", - "providerId": "client-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "client-secret", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-secret-jwt", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "client-x509", - "authenticatorFlow": false, - "requirement": "ALTERNATIVE", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "d194ad5f-0187-47a6-a944-79fa627fcaef", - "alias": "direct grant", - "description": "OpenID Connect Resource Owner Grant", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "direct-grant-validate-username", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "direct-grant-validate-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 30, - "autheticatorFlow": true, - "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "f11b4916-d306-4c18-be28-8789725654af", - "alias": "docker auth", - "description": "Used by Docker clients to authenticate against the IDP", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "docker-http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "81065c15-378a-4ed6-913e-80adc3a22a68", - "alias": "first broker login", - "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticatorConfig": "review profile config", - "authenticator": "idp-review-profile", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "User creation or linking", - "userSetupAllowed": false - } - ] - }, - { - "id": "a1d952d0-22d7-47aa-a7d2-67f9df058174", - "alias": "forms", - "description": "Username, password, otp and other auth forms.", - "providerId": "basic-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "auth-username-password-form", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "e73d5069-f13d-4ff9-babf-b74ee7f5fcb5", - "alias": "http challenge", - "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "no-cookie-redirect", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": true, - "flowAlias": "Authentication Options", - "userSetupAllowed": false - } - ] - }, - { - "id": "632d3e41-7514-4917-a588-475d68f14a89", - "alias": "registration", - "description": "registration flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-page-form", - "authenticatorFlow": true, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": true, - "flowAlias": "registration form", - "userSetupAllowed": false - } - ] - }, - { - "id": "df103280-f127-4c1c-aae0-0e19bf3cfdf5", - "alias": "registration form", - "description": "registration form", - "providerId": "form-flow", - "topLevel": false, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "registration-user-creation", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-profile-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 40, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-password-action", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 50, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "registration-recaptcha-action", - "authenticatorFlow": false, - "requirement": "DISABLED", - "priority": 60, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - }, - { - "id": "92f23d4d-900b-4684-9539-c709b9a22933", - "alias": "reset credentials", - "description": "Reset credentials for a user if they forgot their password or something", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "reset-credentials-choose-user", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-credential-email", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 20, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticator": "reset-password", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 30, - "autheticatorFlow": false, - "userSetupAllowed": false - }, - { - "authenticatorFlow": true, - "requirement": "CONDITIONAL", - "priority": 40, - "autheticatorFlow": true, - "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false - } - ] - }, - { - "id": "d2ed6fd9-2ca0-4a0f-bab5-88128ba8212a", - "alias": "saml ecp", - "description": "SAML ECP Profile Authentication Flow", - "providerId": "basic-flow", - "topLevel": true, - "builtIn": true, - "authenticationExecutions": [ - { - "authenticator": "http-basic-authenticator", - "authenticatorFlow": false, - "requirement": "REQUIRED", - "priority": 10, - "autheticatorFlow": false, - "userSetupAllowed": false - } - ] - } - ], - "authenticatorConfig": [ - { - "id": "6c1eee10-5c80-419a-9831-eea8b5325d57", - "alias": "create unique user config", - "config": { - "require.password.update.after.registration": "false" - } - }, - { - "id": "f5b3995e-aec6-4ecd-8e77-b20d93371210", - "alias": "review profile config", - "config": { - "update.profile.on.first.login": "missing" - } - } - ], - "requiredActions": [ - { - "alias": "CONFIGURE_TOTP", - "name": "Configure OTP", - "providerId": "CONFIGURE_TOTP", - "enabled": true, - "defaultAction": false, - "priority": 10, - "config": {} - }, - { - "alias": "terms_and_conditions", - "name": "Terms and Conditions", - "providerId": "terms_and_conditions", - "enabled": false, - "defaultAction": false, - "priority": 20, - "config": {} - }, - { - "alias": "UPDATE_PASSWORD", - "name": "Update Password", - "providerId": "UPDATE_PASSWORD", - "enabled": true, - "defaultAction": false, - "priority": 30, - "config": {} - }, - { - "alias": "UPDATE_PROFILE", - "name": "Update Profile", - "providerId": "UPDATE_PROFILE", - "enabled": true, - "defaultAction": false, - "priority": 40, - "config": {} - }, - { - "alias": "VERIFY_EMAIL", - "name": "Verify Email", - "providerId": "VERIFY_EMAIL", - "enabled": true, - "defaultAction": false, - "priority": 50, - "config": {} - }, - { - "alias": "delete_account", - "name": "Delete Account", - "providerId": "delete_account", - "enabled": false, - "defaultAction": false, - "priority": 60, - "config": {} - }, - { - "alias": "update_user_locale", - "name": "Update User Locale", - "providerId": "update_user_locale", - "enabled": true, - "defaultAction": false, - "priority": 1000, - "config": {} - } - ], - "browserFlow": "browser", - "registrationFlow": "registration", - "directGrantFlow": "direct grant", - "resetCredentialsFlow": "reset credentials", - "clientAuthenticationFlow": "clients", - "dockerAuthenticationFlow": "docker auth", - "attributes": { - "cibaBackchannelTokenDeliveryMode": "poll", - "cibaExpiresIn": "120", - "cibaAuthRequestedUserHint": "login_hint", - "oauth2DeviceCodeLifespan": "600", - "clientOfflineSessionMaxLifespan": "0", - "oauth2DevicePollingInterval": "5", - "clientSessionIdleTimeout": "0", - "userProfileEnabled": "false", - "parRequestUriLifespan": "60", - "clientSessionMaxLifespan": "0", - "clientOfflineSessionIdleTimeout": "0", - "cibaInterval": "5" - }, - "keycloakVersion": "18.0.0", - "userManagedAccessAllowed": false, - "clientProfiles": { - "profiles": [] - }, - "clientPolicies": { - "policies": [] - } -} diff --git a/apps/openchallenges/keycloak/data/import/test-users-0.json b/apps/openchallenges/keycloak/data/import/test-users-0.json deleted file mode 100644 index da5a7f0eda..0000000000 --- a/apps/openchallenges/keycloak/data/import/test-users-0.json +++ /dev/null @@ -1,184 +0,0 @@ -{ - "realm": "test", - "users": [ - { - "id": "e7baa612-8659-4010-b094-12ad77332457", - "createdTimestamp": 1653006494716, - "username": "luke", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Luke", - "lastName": "Skywalker", - "email": "luke@jedi.com", - "credentials": [ - { - "id": "fbf30662-c9a2-4938-afdf-09e9ad74cb43", - "type": "password", - "createdDate": 1653006507813, - "secretData": "{\"value\":\"iHwHlDx3QUjRF/684BzwSTm7ly5gtOEi2tlYqmyYuHpjZ9qcgYC5D+RxAX2ic5+UM0T6fYCy/Ogq+wdO67/Vqg==\",\"salt\":\"oiiKQitbbnXMU6R55mu7Jg==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["app-user", "test-visitor", "default-roles-test"], - "notBefore": 0, - "groups": [] - }, - { - "id": "1212f921-6ab0-444f-a5ea-9dc154199a3c", - "createdTimestamp": 1659302384564, - "username": "oakenshield", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "oakenshield@gmail.com", - "credentials": [ - { - "id": "b3007bb6-e1ec-40f3-9ddd-f4034b290d9f", - "type": "password", - "createdDate": 1659302384610, - "secretData": "{\"value\":\"R3e03ubioHYAzNCLIXmzos6FD4K1kUfonLSyrhsccZbKlXM+3+sJktoCQUdGiPCoseC9o83KZvLboLyAr5UTpA==\",\"salt\":\"nQxU4fIFyNZrh15mmOoexA==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "notBefore": 0, - "groups": [] - }, - { - "id": "9f6abcb0-25aa-49d7-aa1b-5292c8f3e26a", - "createdTimestamp": 1659302483317, - "username": "rrchai", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "rchai@sagebase.org", - "credentials": [ - { - "id": "2ce53a48-82fc-418b-a913-e20c647cb959", - "type": "password", - "createdDate": 1659302483363, - "secretData": "{\"value\":\"wQjuq7Ic1E4hgaOUTb6hswhJe4KWHPQslc5PrMmGFRzMOoGHvomM+SkrFGiQnBAYQZG7haKEduu2+JK/iuceUQ==\",\"salt\":\"XIBlzbpasw47L2CpJ4uZOw==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "notBefore": 0, - "groups": [] - }, - { - "id": "8182baec-24dc-4522-ab2a-a446fd6c3f6d", - "createdTimestamp": 1653876202095, - "username": "service-account-challenge-api-client", - "enabled": true, - "totp": false, - "emailVerified": false, - "serviceAccountClientId": "challenge-api-client", - "credentials": [], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "clientRoles": { - "realm-management": ["manage-users", "view-users"], - "challenge-api-client": ["uma_protection"] - }, - "notBefore": 0, - "groups": [] - }, - { - "id": "033a4bac-80df-4eac-8478-b7f67d44e2d5", - "createdTimestamp": 1653006044524, - "username": "service-account-test-client", - "enabled": true, - "totp": false, - "emailVerified": false, - "serviceAccountClientId": "test-client", - "credentials": [], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "clientRoles": { - "test-client": ["uma_protection"] - }, - "notBefore": 0, - "groups": [] - }, - { - "id": "b9302586-876b-47e8-900c-2c09f161b888", - "createdTimestamp": 1659292473628, - "username": "test", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "test@sagebase.com", - "credentials": [ - { - "id": "8f08ec0e-ed3b-4da8-ab56-b5a720a90b21", - "type": "password", - "createdDate": 1659292473676, - "secretData": "{\"value\":\"oaEnlBFxomZ3peABlJXFGLVCAgT3DN9maMHL/nJEBYhGVuD7dsdbqEx4AQRaQxUrPvkqmxGGQklTjd40NDERtQ==\",\"salt\":\"XswKAPOIy6uVi0dIBXzgJQ==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "notBefore": 0, - "groups": [] - }, - { - "id": "3739ed0d-6da2-4d65-ba5a-223eb2dfe2b6", - "createdTimestamp": 1653006571091, - "username": "tschaffter", - "enabled": true, - "totp": false, - "emailVerified": false, - "firstName": "Thomas", - "lastName": "Schaffter", - "email": "thomas.schaffter@sagebase.org", - "credentials": [ - { - "id": "6047fdfb-5169-42c6-9e0d-c5e68d0b6d52", - "type": "password", - "createdDate": 1653006581157, - "secretData": "{\"value\":\"Rxnk4XGQga/sxIs9cpfabO1inRpVAKqAq9Za2VTHron5CNFm24DmrvmsvxCftIsL1Ub4lFkgxgryHNOW6lgvMg==\",\"salt\":\"IXjf6nLQFtaeb+gTDIqG9Q==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test", "test-admin", "app-admin"], - "notBefore": 0, - "groups": [] - }, - { - "id": "04b800f3-c431-4862-93c4-2bef14fc204d", - "createdTimestamp": 1659302507439, - "username": "vpchung", - "enabled": true, - "totp": false, - "emailVerified": false, - "email": "verena.chung@sagebase.org", - "credentials": [ - { - "id": "21b520c8-f055-405f-b558-d14079c3518c", - "type": "password", - "createdDate": 1659302507485, - "secretData": "{\"value\":\"vqs4gmZgm0e0OnBfEJzCAoqHw/QJbIV1R2IknL43MIkv25//UI8MrSixiNejcYtXYpi3kuD2VR+2Gh4n0Lk9sQ==\",\"salt\":\"tjQqX6y/1nD145vhhf8Jqg==\",\"additionalParameters\":{}}", - "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" - } - ], - "disableableCredentialTypes": [], - "requiredActions": [], - "realmRoles": ["default-roles-test"], - "notBefore": 0, - "groups": [] - } - ] -} diff --git a/apps/openchallenges/keycloak/docker-compose.yml b/apps/openchallenges/keycloak/docker-compose.yml deleted file mode 100644 index 1fd6e612a2..0000000000 --- a/apps/openchallenges/keycloak/docker-compose.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: '3.8' - -services: - openchallenges-keycloak: - image: ghcr.io/sage-bionetworks/openchallenges-keycloak:local - container_name: openchallenges-keycloak - restart: always - env_file: - - .env - volumes: - - ./data/h2:/opt/keycloak/data/h2 - - ./themes/openchallenges:/opt/keycloak/themes/openchallenges - networks: - - openchallenges - ports: - - '8080:8080' - command: start-dev - deploy: - resources: - limits: - memory: 1G - -networks: - openchallenges: - name: openchallenges diff --git a/apps/openchallenges/keycloak/project.json b/apps/openchallenges/keycloak/project.json deleted file mode 100644 index ba6fbc8bfe..0000000000 --- a/apps/openchallenges/keycloak/project.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "openchallenges-keycloak", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/openchallenges/keycloak/src", - "projectType": "application", - "targets": { - "create-config": { - "executor": "nx:run-commands", - "options": { - "command": "cp -n .env.example .env", - "cwd": "{projectRoot}" - } - }, - "serve": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up", - "cwd": "apps/openchallenges/keycloak" - }, - "dependsOn": [] - }, - "serve-detach": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up -d", - "cwd": "apps/openchallenges/keycloak" - }, - "dependsOn": [] - }, - "import-dev-data": { - "executor": "nx:run-commands", - "options": { - "commands": [ - "docker run --rm --env-file .env -v \"$(pwd)\"/data/h2:/opt/keycloak/data/h2 -v \"$(pwd)\"/data/import:/opt/keycloak/data/import:ro ghcr.io/sage-bionetworks/openchallenges-keycloak:local import --dir /opt/keycloak/data/import --override true" - ], - "cwd": "apps/openchallenges/keycloak" - }, - "dependsOn": [] - }, - "export-dev-data": { - "executor": "nx:run-commands", - "options": { - "commands": [ - "docker run --rm --env-file .env -v \"$(pwd)\"/data/h2:/opt/keycloak/data/h2 -v \"$(pwd)\"/data/import:/opt/keycloak/data/import ghcr.io/sage-bionetworks/openchallenges-keycloak:local export --dir /opt/keycloak/data/import" - ], - "cwd": "apps/openchallenges/keycloak" - }, - "dependsOn": [] - }, - "build-image": { - "executor": "@nx-tools/nx-container:build", - "options": { - "context": "apps/openchallenges/keycloak", - "push": false, - "tags": ["ghcr.io/sage-bionetworks/openchallenges-keycloak:local"] - } - } - }, - "tags": ["type:service", "scope:backend"], - "implicitDependencies": [] -} diff --git a/apps/openchallenges/keycloak/requests.http b/apps/openchallenges/keycloak/requests.http deleted file mode 100644 index 4551a085f2..0000000000 --- a/apps/openchallenges/keycloak/requests.http +++ /dev/null @@ -1,22 +0,0 @@ -@clientId = test-client -@clientSecret = MiZwMWx3Tuw1mkySKId10lk7kPgKV9IZ - -### Get token -POST http://localhost:8081/realms/test/protocol/openid-connect/token -Content-Type: application/x-www-form-urlencoded - -grant_type=client_credentials -&scope=openid -&client_id={{clientId}} -&client_secret={{clientSecret}} - -### Get user token -POST http://localhost:8081/realms/test/protocol/openid-connect/token -Content-Type: application/x-www-form-urlencoded - -grant_type=password -&scope=openid -&client_id={{clientId}} -&client_secret={{clientSecret}} -&username=luke -&password=changeme \ No newline at end of file diff --git a/apps/openchallenges/keycloak/themes/challenge-registry/README.md b/apps/openchallenges/keycloak/themes/challenge-registry/README.md deleted file mode 100644 index 88fa6000a0..0000000000 --- a/apps/openchallenges/keycloak/themes/challenge-registry/README.md +++ /dev/null @@ -1 +0,0 @@ -https://www.keycloak.org/docs/19.0.3/server_development/index.html#_themes diff --git a/apps/openchallenges/keycloak/themes/challenge-registry/login/terms.ftl b/apps/openchallenges/keycloak/themes/challenge-registry/login/terms.ftl deleted file mode 100644 index 687b192c94..0000000000 --- a/apps/openchallenges/keycloak/themes/challenge-registry/login/terms.ftl +++ /dev/null @@ -1,15 +0,0 @@ -<#import "template.ftl" as layout> -<@layout.registrationLayout displayMessage=false; section> - <#if section = "header"> - ${msg("termsTitle")} - <#elseif section = "form"> -
- ${kcSanitize(msg("termsText"))?no_esc} -
-
- - -
-
- - diff --git a/apps/openchallenges/keycloak/themes/challenge-registry/login/theme.properties b/apps/openchallenges/keycloak/themes/challenge-registry/login/theme.properties deleted file mode 100644 index e67231fee9..0000000000 --- a/apps/openchallenges/keycloak/themes/challenge-registry/login/theme.properties +++ /dev/null @@ -1,2 +0,0 @@ -parent=base -import=common/keycloak \ No newline at end of file diff --git a/apps/openchallenges/organization-service/.env.example b/apps/openchallenges/organization-service/.env.example index aa08ecef25..b2dbca48e2 100644 --- a/apps/openchallenges/organization-service/.env.example +++ b/apps/openchallenges/organization-service/.env.example @@ -1,4 +1,3 @@ SERVER_PORT=8084 -KEYCLOAK_URL=http://openchallenges-keycloak:8080 SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka DB_URL=jdbc:mysql://openchallenges-mariadb:3306/organization_service?allowLoadLocalInfile=true \ No newline at end of file diff --git a/apps/openchallenges/organization-service/build.gradle b/apps/openchallenges/organization-service/build.gradle index d300d48844..f4cbf62590 100644 --- a/apps/openchallenges/organization-service/build.gradle +++ b/apps/openchallenges/organization-service/build.gradle @@ -34,8 +34,6 @@ dependencies { implementation "org.flywaydb:flyway-mysql:${flywaydbVersion}" implementation "org.hibernate.search:hibernate-search-backend-elasticsearch:${hibernateSearchVersion}" implementation "org.hibernate.search:hibernate-search-mapper-orm:${hibernateSearchVersion}" - implementation "org.keycloak:keycloak-admin-client:${keycloakVersion}" - implementation "org.keycloak:keycloak-spring-boot-starter:${keycloakVersion}" implementation "org.sagebionetworks:util:${openchallengesVersion}" implementation "org.springframework.boot:spring-boot-devtools:${springBootVersion}" implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" @@ -110,7 +108,6 @@ jacocoTestReport { '**/configuration/Flyway*.*', '**/configuration/HibernateSearch*.*', '**/configuration/HomeController*.*', - '**/configuration/Keycloak*.*', '**/configuration/SpringDocConfiguration*.*', '**/model/dto/**', '**/RFC3339DateFormat.*' @@ -130,7 +127,6 @@ sonar { '**/configuration/Flyway*.*', '**/configuration/HibernateSearch*.*', '**/configuration/HomeController*.*', - '**/configuration/Keycloak*.*', '**/configuration/SpringDocConfiguration*.*', '**/model/dto/**', '**/RFC3339DateFormat.*' diff --git a/apps/openchallenges/organization-service/gradle.properties b/apps/openchallenges/organization-service/gradle.properties index edec945fa6..e0ee139751 100644 --- a/apps/openchallenges/organization-service/gradle.properties +++ b/apps/openchallenges/organization-service/gradle.properties @@ -1,7 +1,6 @@ fasterxmlVersion=2.13.4 flywaydbVersion=9.4.0 hibernateSearchVersion=6.1.7.Final -keycloakVersion=19.0.3 lombokVersion=1.18.24 openchallengesVersion=0.0.1-SNAPSHOT sonarqubeVersion=4.3.0.3225 diff --git a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakConfiguration.java b/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakConfiguration.java deleted file mode 100644 index 53a48220af..0000000000 --- a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.openchallenges.organization.service.configuration; - -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class KeycloakConfiguration { - - @Bean - public KeycloakConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } -} diff --git a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManager.java b/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManager.java deleted file mode 100644 index 2327609ee1..0000000000 --- a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManager.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.sagebionetworks.openchallenges.organization.service.configuration; - -import lombok.extern.slf4j.Slf4j; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.admin.client.resource.RealmResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class KeycloakManager { - - @Autowired - private KeycloakManagerProperties keycloakProperties; - - private static Keycloak keycloakInstance = null; - - public RealmResource getKeycloakInstanceWithRealm() { - return getInstance().realm(keycloakProperties.getRealm()); - } - - public Keycloak getInstance() { - if (keycloakInstance == null) { - log.info("KC SERVER URL: {}", keycloakProperties.getServerUrl()); - keycloakInstance = KeycloakBuilder.builder() - .serverUrl(keycloakProperties.getServerUrl()) - .realm(keycloakProperties.getRealm()) - .grantType("client_credentials") - .clientId(keycloakProperties.getClientId()) - .clientSecret(keycloakProperties.getClientSecret()) - .build(); - } - return keycloakInstance; - } -} diff --git a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManagerProperties.java b/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManagerProperties.java deleted file mode 100644 index ebf196e29d..0000000000 --- a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/KeycloakManagerProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.openchallenges.organization.service.configuration; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "app.config.keycloak") -public class KeycloakManagerProperties { - - private String serverUrl; - private String realm; - private String clientId; - private String clientSecret; -} diff --git a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/SecurityConfiguration.java b/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/SecurityConfiguration.java index 3150b1d929..1fd97ecc03 100644 --- a/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/SecurityConfiguration.java +++ b/apps/openchallenges/organization-service/src/main/java/org/sagebionetworks/openchallenges/organization/service/configuration/SecurityConfiguration.java @@ -1,71 +1,31 @@ package org.sagebionetworks.openchallenges.organization.service.configuration; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; -@KeycloakConfiguration +@Configuration +@EnableWebSecurity @EnableGlobalMethodSecurity(jsr250Enabled = true) -class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { +public class SecurityConfiguration { - // @Autowired - // RestAccessDeniedHandler restAccessDeniedHandler; - - // @Autowired - // CustomKeycloakAuthenticationHandler customKeycloakAuthenticationHandler; - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf() .disable() .cors() .disable() .authorizeRequests() - // .antMatchers("/api/v1/users/register", "/api/v1/**", "/swagger-ui/**", - // "/swagger-ui.html") .antMatchers("**") .permitAll() .anyRequest() - .authenticated(); - // Custom error handler - // http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = - keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); + .authenticated() + .and() + .httpBasic(); // Use Basic Auth for simplicity; replace as needed + return http.build(); } - - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); - } - // // Keycloak auth exception handler - // @Bean - // @Override - // protected KeycloakAuthenticationProcessingFilter - // keycloakAuthenticationProcessingFilter() - // throws Exception { - // KeycloakAuthenticationProcessingFilter filter = - // new KeycloakAuthenticationProcessingFilter(this.authenticationManagerBean()); - // filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy()); - // filter.setAuthenticationFailureHandler(customKeycloakAuthenticationHandler); - // return filter; - // } - } diff --git a/apps/openchallenges/postgres/.env.example b/apps/openchallenges/postgres/.env.example deleted file mode 100644 index 355d4c1880..0000000000 --- a/apps/openchallenges/postgres/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -POSTGRES_USER=postgres -POSTGRES_PASSWORD=changeme -POSTGRES_DB=challenge \ No newline at end of file diff --git a/apps/openchallenges/postgres/Dockerfile b/apps/openchallenges/postgres/Dockerfile deleted file mode 100644 index ac2145e43a..0000000000 --- a/apps/openchallenges/postgres/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM postgres:14.5-alpine \ No newline at end of file diff --git a/apps/openchallenges/postgres/docker-compose.yml b/apps/openchallenges/postgres/docker-compose.yml deleted file mode 100644 index 09e83edeaa..0000000000 --- a/apps/openchallenges/postgres/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '3.8' - -services: - openchallenges-postgres: - image: ghcr.io/sage-bionetworks/openchallenges-postgres:local - container_name: openchallenges-postgres - env_file: - - .env - volumes: - - openchallenges-postgres:/var/lib/postgresql/data - - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d - networks: - - openchallenges - ports: - - '5432:5432' - deploy: - resources: - limits: - memory: 500M - -volumes: - openchallenges-postgres: - name: openchallenges-postgres - -networks: - openchallenges: - name: openchallenges diff --git a/apps/openchallenges/postgres/docker-entrypoint-initdb.d/init-db.sql b/apps/openchallenges/postgres/docker-entrypoint-initdb.d/init-db.sql deleted file mode 100644 index 7f0ca21ceb..0000000000 --- a/apps/openchallenges/postgres/docker-entrypoint-initdb.d/init-db.sql +++ /dev/null @@ -1,9 +0,0 @@ --- sqlfluff:dialect:postgres --- keycloak -CREATE USER keycloak -WITH - ENCRYPTED PASSWORD 'changeme'; - -CREATE DATABASE keycloak; - -GRANT all PRIVILEGES ON DATABASE keycloak TO keycloak; diff --git a/apps/openchallenges/postgres/project.json b/apps/openchallenges/postgres/project.json deleted file mode 100644 index 1f5847f3bd..0000000000 --- a/apps/openchallenges/postgres/project.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "openchallenges-postgres", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/openchallenges/postgres/src", - "projectType": "application", - "targets": { - "create-config": { - "executor": "nx:run-commands", - "options": { - "command": "cp -n .env.example .env", - "cwd": "{projectRoot}" - } - }, - "serve": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up", - "cwd": "apps/openchallenges/postgres" - }, - "dependsOn": [] - }, - "serve-detach": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up -d", - "cwd": "apps/openchallenges/postgres" - }, - "dependsOn": [] - }, - "build-image": { - "executor": "@nx-tools/nx-container:build", - "options": { - "context": "apps/openchallenges/postgres", - "push": false, - "tags": ["ghcr.io/sage-bionetworks/openchallenges-postgres:local"] - } - } - }, - "tags": ["type:db", "scope:backend"], - "implicitDependencies": [] -} diff --git a/apps/openchallenges/user-service/.env.example b/apps/openchallenges/user-service/.env.example deleted file mode 100644 index bc53e5779d..0000000000 --- a/apps/openchallenges/user-service/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -KEYCLOAK_URL=http://openchallenges-keycloak:8080 -SERVICE_REGISTRY_URL=http://openchallenges-service-registry:8081/eureka -DB_URL=jdbc:mysql://openchallenges-mariadb:3306/user_service \ No newline at end of file diff --git a/apps/openchallenges/user-service/.gitignore b/apps/openchallenges/user-service/.gitignore deleted file mode 100644 index 14b576cb10..0000000000 --- a/apps/openchallenges/user-service/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -.gradle -bin/ -lib/*.jar -maven-legacy/ -tmp/ - -HELP.md -target/ -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/apps/openchallenges/user-service/.openapi-generator-ignore b/apps/openchallenges/user-service/.openapi-generator-ignore deleted file mode 100644 index edc8152794..0000000000 --- a/apps/openchallenges/user-service/.openapi-generator-ignore +++ /dev/null @@ -1,28 +0,0 @@ -# OpenAPI Generator Ignore -# Generated by openapi-generator https://github.com/openapitools/openapi-generator - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: -# ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -# foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -# foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -# docs/*.md -# Then explicitly reverse the ignore rule for a single file: -# !docs/README.md -pom.xml -README.md -**/application.properties -**/model/dto/*AllOf*.java -**/OpenApiGeneratorApplication.java \ No newline at end of file diff --git a/apps/openchallenges/user-service/.openapi-generator/FILES b/apps/openchallenges/user-service/.openapi-generator/FILES deleted file mode 100644 index fd6bc6256e..0000000000 --- a/apps/openchallenges/user-service/.openapi-generator/FILES +++ /dev/null @@ -1,18 +0,0 @@ -AUTHORS.md -src/main/java/org/sagebionetworks/challenge/RFC3339DateFormat.java -src/main/java/org/sagebionetworks/challenge/api/ApiUtil.java -src/main/java/org/sagebionetworks/challenge/api/UserApi.java -src/main/java/org/sagebionetworks/challenge/api/UserApiController.java -src/main/java/org/sagebionetworks/challenge/api/UserApiDelegate.java -src/main/java/org/sagebionetworks/challenge/configuration/HomeController.java -src/main/java/org/sagebionetworks/challenge/configuration/SpringDocConfiguration.java -src/main/java/org/sagebionetworks/challenge/model/dto/BasicErrorDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataPagingDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateRequestDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateResponseDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/UserDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/UserStatusDto.java -src/main/java/org/sagebionetworks/challenge/model/dto/UsersPageDto.java -src/main/resources/openapi.yaml -src/test/java/org/sagebionetworks/challenge/OpenApiGeneratorApplicationTests.java diff --git a/apps/openchallenges/user-service/.openapi-generator/VERSION b/apps/openchallenges/user-service/.openapi-generator/VERSION deleted file mode 100644 index 358e78e607..0000000000 --- a/apps/openchallenges/user-service/.openapi-generator/VERSION +++ /dev/null @@ -1 +0,0 @@ -6.1.0 \ No newline at end of file diff --git a/apps/openchallenges/user-service/AUTHORS.md b/apps/openchallenges/user-service/AUTHORS.md deleted file mode 100644 index 01c10d9f06..0000000000 --- a/apps/openchallenges/user-service/AUTHORS.md +++ /dev/null @@ -1,11 +0,0 @@ -# Authors - -Ordered by first contribution. - -- [Thomas Schaffter](https://github.com/tschaffter) - - - - - diff --git a/apps/openchallenges/user-service/build.gradle b/apps/openchallenges/user-service/build.gradle deleted file mode 100644 index 70047199a9..0000000000 --- a/apps/openchallenges/user-service/build.gradle +++ /dev/null @@ -1,80 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "org.flywaydb:flyway-mysql:${flywaydbVersion}" - } -} - -plugins { - id 'java' - id 'org.springframework.boot' version "${springBootVersion}" - id "io.spring.dependency-management" version "${springDependencyManagementVersion}" - id "org.flywaydb.flyway" version "${flywaydbVersion}" -} - -repositories { - mavenCentral() - mavenLocal() -} - -dependencies { - annotationProcessor "org.projectlombok:lombok:${lombokVersion}" - compileOnly "org.projectlombok:lombok:${lombokVersion}" - implementation 'com.google.code.findbugs:jsr305:3.0.2' - implementation 'org.openapitools:jackson-databind-nullable:0.2.3' - implementation 'org.springdoc:springdoc-openapi-ui:1.6.12' - implementation "com.fasterxml.jackson.core:jackson-databind:${fasterxmlVersion}" - implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${fasterxmlVersion}" - implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${fasterxmlVersion}" - implementation "org.flywaydb:flyway-core:${flywaydbVersion}" - implementation "org.flywaydb:flyway-mysql:${flywaydbVersion}" - implementation "org.keycloak:keycloak-admin-client:${keycloakVersion}" - implementation "org.keycloak:keycloak-spring-boot-starter:${keycloakVersion}" - implementation "org.sagebionetworks:util:${openchallengesVersion}" - implementation "org.springframework.boot:spring-boot-devtools:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-security:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-validation:${springBootVersion}" - implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:${springCloudVersion}" - implementation "org.springframework.data:spring-data-commons:${springDataVersion}" - runtimeOnly 'mysql:mysql-connector-java:8.0.30' - testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}" -} - -group = 'org.sagebionetworks.challenge' -version = '0.0.1-SNAPSHOT' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -springBoot { - mainClass = 'org.sagebionetworks.challenge.ChallengeUserServiceApplication' -} - -bootBuildImage { - imageName = 'ghcr.io/sage-bionetworks/openchallenges-user-service:local' -} - -flyway { - url = 'jdbc:mysql://openchallenges-mariadb:3306/challenge' - user = 'maria' - password = 'changeme' - cleanDisabled = false - // schemas = ['schema1', 'schema2', 'schema3'] - // placeholders = [ - // 'keyABC': 'valueXYZ', - // 'otherplaceholder': 'value123' - // ] -} - diff --git a/apps/openchallenges/user-service/build.gradle.old b/apps/openchallenges/user-service/build.gradle.old deleted file mode 100644 index 29791f733e..0000000000 --- a/apps/openchallenges/user-service/build.gradle.old +++ /dev/null @@ -1,108 +0,0 @@ -plugins { - id 'io.spring.dependency-management' version '1.0.12.RELEASE' - id 'java' - id 'jvm-test-suite' - id 'org.springframework.boot' version '2.7.3' - id 'org.springdoc.openapi-gradle-plugin' version '1.4.0' -} - -ext { - springVersion = '2.7.3' - springCloudVersion = '3.1.3' - keycloakVersion = '18.0.0' -} - -group = 'org.sagebionetworks.challenge' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '17' - -repositories { - mavenCentral() - mavenLocal() -} - -testing { - suites { - test { - useJUnitJupiter() - } - - integrationTest(JvmTestSuite) { - dependencies { - implementation project - } - sources { - java { - srcDirs = ['src/integrationTest/java'] - } - } - targets { - all { - testTask.configure { - shouldRunAfter(test) - } - } - } - } - } -} - -dependencies { - annotationProcessor 'org.projectlombok:lombok:1.18.24' - compileOnly 'org.projectlombok:lombok:1.18.24' - implementation 'com.h2database:h2:2.1.214' - implementation 'org.flywaydb:flyway-core:9.3.0' - implementation 'org.flywaydb:flyway-mysql:9.3.0' - implementation "org.keycloak:keycloak-admin-client:${keycloakVersion}" - implementation "org.keycloak:keycloak-spring-boot-starter:${keycloakVersion}" - implementation 'org.sagebionetworks:util:0.0.1-SNAPSHOT' - implementation 'org.springdoc:springdoc-openapi-ui:1.6.11' - implementation "org.springframework.boot:spring-boot-devtools:${springVersion}" - implementation "org.springframework.boot:spring-boot-starter-actuator:${springVersion}" - implementation "org.springframework.boot:spring-boot-starter-data-jpa:${springVersion}" - implementation "org.springframework.boot:spring-boot-starter-security:${springVersion}" - implementation "org.springframework.boot:spring-boot-starter-web:${springVersion}" - implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:${springCloudVersion}" - implementation "org.springframework.cloud:spring-cloud-starter-openfeign:${springCloudVersion}" - integrationTestImplementation 'org.assertj:assertj-core:3.23.1' - integrationTestImplementation "org.springframework.boot:spring-boot-starter-test:${springVersion}" - runtimeOnly 'mysql:mysql-connector-java:8.0.30' - testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation "org.springframework.boot:spring-boot-starter-test:${springVersion}" -} - -configurations { - integrationTestImplementation.extendsFrom implementation - integrationTestRuntimeOnly.extendsFrom runtimeOnly -} - -// configure the integrationTest to run as part of a regular build -// tasks.named('check') { -// dependsOn(testing.suites.integrationTest) -// } - -test { - useJUnitPlatform() - - testLogging.showStandardStreams = true - - beforeTest { descriptor -> - logger.lifecycle("Running test: " + descriptor) - } - - failFast = true - - testLogging { - events("passed", "skipped", "failed") - } -} - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' -} - -openApi { - apiDocsUrl.set("http://localhost:8083/api/v1/api-docs/openapi.json.yaml") - outputDir.set(file("docs")) - outputFileName.set("openapi.yaml") -} diff --git a/apps/openchallenges/user-service/docker-compose.yml b/apps/openchallenges/user-service/docker-compose.yml deleted file mode 100644 index 29daf035aa..0000000000 --- a/apps/openchallenges/user-service/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '3.8' - -services: - openchallenges-user-service: - image: ghcr.io/sage-bionetworks/openchallenges-user-service:local - container_name: openchallenges-user-service - restart: always - env_file: - - .env - networks: - - openchallenges - ports: - - '8083:8083' - command: start-dev - -networks: - openchallenges: - name: openchallenges diff --git a/apps/openchallenges/user-service/docs/openapi.yaml b/apps/openchallenges/user-service/docs/openapi.yaml deleted file mode 100644 index 293f3d62a4..0000000000 --- a/apps/openchallenges/user-service/docs/openapi.yaml +++ /dev/null @@ -1,150 +0,0 @@ -openapi: 3.0.1 -info: - title: User API - description: This is the User Service of the OpenChallenges. - termsOfService: TOC - contact: - name: 'This is the User ' - url: https://openchallenges.org - license: - name: Apache 2.0 - url: https://github.com/Sage-Bionetworks/sage-monorepo/blob/main/LICENSE.txt - version: v1.0 -servers: - - url: http://localhost:8083 - description: Generated server url -paths: - /api/v1/users/register: - post: - tags: - - user-controller - operationId: createUser - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - required: true - responses: - '200': - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/User' - /api/v1/users/{id}: - get: - tags: - - user-controller - operationId: getUser - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - responses: - '200': - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/User' - patch: - tags: - - user-controller - operationId: updateUser - parameters: - - name: id - in: path - required: true - schema: - type: integer - format: int64 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UserUpdateRequest' - required: true - responses: - '200': - description: OK - content: - '*/*': - schema: - $ref: '#/components/schemas/User' - /api/v1/users/: - get: - tags: - - user-controller - operationId: listUsers - parameters: - - name: page - in: query - description: Zero-based page index (0..N) - required: false - schema: - minimum: 0 - type: integer - default: 0 - - name: size - in: query - description: The size of the page to be returned - required: false - schema: - minimum: 1 - type: integer - default: 20 - - name: sort - in: query - description: "Sorting criteria in the format: property,(asc|desc). Default\ - \ sort order is ascending. Multiple sort criteria are supported." - required: false - schema: - type: array - items: - type: string - responses: - '200': - description: OK - content: - '*/*': - schema: - type: array - items: - $ref: '#/components/schemas/User' -components: - schemas: - User: - type: object - properties: - id: - type: integer - format: int64 - username: - type: string - email: - type: string - password: - type: string - authId: - type: string - status: - type: string - enum: - - PENDING - - APPROVED - - DISABLED - - BLACKLIST - UserUpdateRequest: - type: object - properties: - status: - type: string - enum: - - PENDING - - APPROVED - - DISABLED - - BLACKLIST diff --git a/apps/openchallenges/user-service/gradle.properties b/apps/openchallenges/user-service/gradle.properties deleted file mode 100644 index 9d19d1b598..0000000000 --- a/apps/openchallenges/user-service/gradle.properties +++ /dev/null @@ -1,9 +0,0 @@ -openchallengesVersion=0.0.1-SNAPSHOT -fasterxmlVersion=2.13.4 -flywaydbVersion=9.4.0 -keycloakVersion=19.0.3 -lombokVersion=1.18.24 -springBootVersion=2.7.8 -springCloudVersion=3.1.4 -springDataVersion=2.7.7 -springDependencyManagementVersion=1.0.14.RELEASE \ No newline at end of file diff --git a/apps/openchallenges/user-service/gradle/wrapper/gradle-wrapper.jar b/apps/openchallenges/user-service/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/apps/openchallenges/user-service/openapitools.json b/apps/openchallenges/user-service/openapitools.json deleted file mode 100644 index c09387f98f..0000000000 --- a/apps/openchallenges/user-service/openapitools.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "../../../node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "6.1.0", - "generators": { - "openchallenges-user-service": { - "config": "templates/config.yaml", - "generatorName": "spring", - "inputSpec": "#{cwd}/../../../libs/openchallenges/api-description/build/user.openapi.yaml", - "output": "#{cwd}", - "templateDir": "templates", - "additionalProperties": { - "apiPackage": "org.sagebionetworks.challenge.api", - "basePackage": "org.sagebionetworks.challenge", - "configPackage": "org.sagebionetworks.challenge.configuration", - "delegatePattern": true, - "hideGenerationTimestamp": true, - "library": "spring-boot", - "modelNameSuffix": "Dto", - "modelPackage": "org.sagebionetworks.challenge.model.dto", - "useTags": true - } - } - } - } -} diff --git a/apps/openchallenges/user-service/project.json b/apps/openchallenges/user-service/project.json deleted file mode 100644 index a9b2b38896..0000000000 --- a/apps/openchallenges/user-service/project.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "openchallenges-user-service", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/openchallenges/user-service/src", - "projectType": "application", - "targets": { - "create-config": { - "executor": "nx:run-commands", - "options": { - "command": "cp -n .env.example .env", - "cwd": "{projectRoot}" - } - }, - "prepare": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew --version 1> /dev/null"], - "cwd": "{projectRoot}" - } - }, - "build": { - "executor": "nx:run-commands", - "outputs": ["{projectRoot}/build"], - "options": { - "command": "./gradlew build", - "cwd": "{projectRoot}" - }, - "dependsOn": ["^install"] - }, - "clean": { - "executor": "nx:run-commands", - "options": { - "command": "./gradlew clean", - "cwd": "{projectRoot}" - } - }, - "serve": { - "executor": "nx:run-commands", - "options": { - "commands": ["./gradlew build --continuous", "./gradlew bootRun"], - "cwd": "apps/openchallenges/user-service", - "parallel": true - }, - "dependsOn": ["^install"] - }, - "serve-detach": { - "executor": "nx:run-commands", - "options": { - "command": "docker compose up -d", - "cwd": "apps/openchallenges/user-service" - }, - "dependsOn": [] - }, - "generate": { - "executor": "nx:run-commands", - "options": { - "commands": ["xargs rm -fr <.openapi-generator/FILES", "openapi-generator-cli generate"], - "cwd": "{projectRoot}", - "parallel": false - }, - "dependsOn": ["^build"] - } - }, - "tags": ["type:service", "scope:backend", "language:java", "package-manager:gradle"], - "implicitDependencies": [ - "openchallenges-api-description", - "openchallenges-api-gateway", - "openchallenges-keycloak", - "openchallenges-mariadb", - "openchallenges-service-registry", - "shared-java-util" - ] -} diff --git a/apps/openchallenges/user-service/requests.http b/apps/openchallenges/user-service/requests.http deleted file mode 100644 index fb6045ffd0..0000000000 --- a/apps/openchallenges/user-service/requests.http +++ /dev/null @@ -1,65 +0,0 @@ -@serviceRegistryHost = http://openchallenges-service-registry:8081 -@apiGatewayHost = http://openchallenges-api-gateway:8082 -@userServiceHost = http://openchallenges-user-service:8083 -@keycloakHost = http://openchallenges-keycloak:8080 - -### Check user service info - -# GET {{userServiceHost}}/actuator/info - -### Check API gateway actuator (expected to redirect to Keycloak login page) - -# GET {{apiGatewayHost}}/actuator/info - -### Check user service info via the API gateway (expected to redirect to -### Keycloak login page) - -# GET {{apiGatewayHost}}/user/actuator/info - -### Get access token from Keycloak - -# @clientId = challenge-core-client -# @clientSecret = O0cNRMWg3LHsdHW8BNPlY96qKooDPhPX -@clientId = challenge-api-client -@clientSecret = mg2DrRcxHx19PIITibdOnbNEbJUKjGKb -@username = luke -@password = changeme - -# @name login - -POST {{keycloakHost}}/realms/test/protocol/openid-connect/token -Content-Type: application/x-www-form-urlencoded - -grant_type=password -&scope=email -&client_id={{clientId}} -&client_secret={{clientSecret}} -&username={{username}} -&password={{password}} - -### Check user service info - -# GET {{apiGatewayHost}}/user/actuator/info -# Authorization: Bearer {{login.response.body.$.access_token}} - -### Create user (autorized without token) - -POST {{apiGatewayHost}}/api/v1/users/register -Content-Type: application/json - -{ - "username": "test", - "email": "test@gmail.com", - "password": "changeme" -} - -### List all the users - -GET {{apiGatewayHost}}/api/v1/users -Authorization: Bearer {{login.response.body.$.access_token}} - -### List all the users (bypass the API gateway) - -GET {{userServiceHost}}/api/v1/users -Authorization: Bearer {{login.response.body.$.access_token}} - diff --git a/apps/openchallenges/user-service/session.sql b/apps/openchallenges/user-service/session.sql deleted file mode 100644 index 2f6d2ef569..0000000000 --- a/apps/openchallenges/user-service/session.sql +++ /dev/null @@ -1,17 +0,0 @@ --- sqlfluff:dialect:mariadb --- @block list users -SELECT - * -FROM - challenge_user; - --- @block delete user by id -DELETE FROM challenge_user -WHERE - id = 1; - --- @block delete all users -DELETE FROM challenge_user; - --- @block delete flyway schema history -DELETE FROM flyway_schema_history; diff --git a/apps/openchallenges/user-service/settings.gradle b/apps/openchallenges/user-service/settings.gradle deleted file mode 100644 index ef5f089b7b..0000000000 --- a/apps/openchallenges/user-service/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'openchallenges-user-service' diff --git a/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/ChallengeUserServiceApplicationTests.java b/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/ChallengeUserServiceApplicationTests.java deleted file mode 100644 index 2dbc007ef0..0000000000 --- a/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/ChallengeUserServiceApplicationTests.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ChallengeUserServiceApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/model/repository/UserRepositoryIntegrationTest.java b/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/model/repository/UserRepositoryIntegrationTest.java deleted file mode 100644 index f162a6a969..0000000000 --- a/apps/openchallenges/user-service/src.old/integrationTest/java/org/sagebionetworks/challenge/model/repository/UserRepositoryIntegrationTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.sagebionetworks.challenge.model.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.sagebionetworks.challenge.model.dto.UserStatus; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; - -@AutoConfigureWebClient -@DataJpaTest -public class UserRepositoryIntegrationTest { - - @Autowired - private TestEntityManager entityManager; - - @Autowired - UserRepository repository; - - @Test - public void shouldFindNoUsersIfRepositoryIsEmpty() { - Iterable users = repository.findAll(); - assertThat(users).isEmpty(); - } - - @Test - public void shouldStoreGivenUser() { - UserEntity user = repository.save( - new UserEntity("test", "1212f921-6ab0-444f-a5ea-9dc154199a3c", UserStatus.PENDING) - ); - assertThat(user).hasFieldOrProperty("id"); - assertThat(user).hasFieldOrPropertyWithValue("username", "test"); - assertThat(user).hasFieldOrPropertyWithValue("authId", "1212f921-6ab0-444f-a5ea-9dc154199a3c"); - assertThat(user).hasFieldOrPropertyWithValue("status", UserStatus.PENDING); - } - - @Test - public void shouldFindAllUsers() { - UserEntity user1 = new UserEntity( - "test1", - "1212f921-6ab0-444f-a5ea-9dc154199a31", - UserStatus.PENDING - ); - UserEntity user2 = new UserEntity( - "test2", - "1212f921-6ab0-444f-a5ea-9dc154199a32", - UserStatus.PENDING - ); - entityManager.persist(user1); - entityManager.persist(user2); - Iterable users = repository.findAll(); - assertThat(users).hasSize(2).contains(user1, user2); - } - - @Test - public void shouldDeleteAllUsers() { - entityManager.persist( - new UserEntity("test1", "1212f921-6ab0-444f-a5ea-9dc154199a31", UserStatus.PENDING) - ); - entityManager.persist( - new UserEntity("test2", "1212f921-6ab0-444f-a5ea-9dc154199a32", UserStatus.PENDING) - ); - repository.deleteAll(); - assertThat(repository.findAll()).isEmpty(); - } -} diff --git a/apps/openchallenges/user-service/src.old/integrationTest/resources/application.yml b/apps/openchallenges/user-service/src.old/integrationTest/resources/application.yml deleted file mode 100644 index bbb5261943..0000000000 --- a/apps/openchallenges/user-service/src.old/integrationTest/resources/application.yml +++ /dev/null @@ -1,55 +0,0 @@ -spring: - #admin: - #enabled: true - #jmx-name: org.springframework.boot:type=Admin,name=SpringApplicationIT - datasource: - url: jdbc:h2:mem:challenge - driver-class-name: org.h2.Driver - #jpa: - #properties: - #hibernate: - #dialect: org.hibernate.dialect.H2Dialect - #hibernate: - #ddl-auto: create-drop - #database-platform: org.hibernate.dialect.H2Dialect - flyway: - enabled: false - -server: - port: 8083 - -eureka: - client: - enabled: false - -keycloak: - realm: test - resource: openchallenges-user-service - credentials: - secret: GFRau7cQTxhx19CUmSdrqrqrZ1pshjjB - auth-server-url: http://openchallenges-keycloak:8080 - ssl-required: external - use-resource-role-mappings: true - bearer-only: true - -app: - config: - keycloak: - server-url: http://openchallenges-keycloak:8080 - realm: test - # TODO: Which client ID property is used? - client-id: challenge-api-client - clientId: challenge-api-client - client-secret: mg2DrRcxHx19PIITibdOnbNEbJUKjGKb - -springdoc: - swagger-ui: - path: /api/v1/api-docs/ui - api-docs: - path: /api/v1/api-docs/openapi.json - #path: /api/v1/openapi.json - packagesToScan: org.sagebionetworks.challenge.controller - pathsToMatch: /** - -example: - firstProperty: 'integration-test' diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java deleted file mode 100644 index 5f05a53437..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.sagebionetworks.challenge; - -import io.swagger.v3.oas.models.info.Info; -import org.springdoc.core.GroupedOpenApi; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; - -@EnableFeignClients -@EnableEurekaClient -@SpringBootApplication -public class ChallengeUserServiceApplication { - - public static void main(String[] args) { - SpringApplication.run(ChallengeUserServiceApplication.class, args); - } - - @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder) { - return builder.build(); - } - // @Bean - // public GroupedOpenApi usersGroup(@Value("${springdoc.version}") String appVersion) { - // return GroupedOpenApi.builder().group("users") - // // .addOperationCustomizer((operation, handlerMethod) -> { - // // operation.addSecurityItem(new SecurityRequirement().addList("basicScheme")); - // // return operation; - // // }) - // .addOpenApiCustomiser( - // openApi -> openApi.info(new Info().title("Users API").version(appVersion))) - // .packagesToScan("org.springdoc.demo.app2").build(); - // } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignClientConfiguration.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignClientConfiguration.java deleted file mode 100644 index c9a0cfe11c..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignClientConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import feign.codec.ErrorDecoder; -import org.springframework.cloud.openfeign.FeignClientProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class CustomFeignClientConfiguration extends FeignClientProperties.FeignClientConfiguration { - - @Bean - public ErrorDecoder errorDecoder() { - return new CustomFeignErrorDecoder(); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignErrorDecoder.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignErrorDecoder.java deleted file mode 100644 index 3f1086bb4c..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/CustomFeignErrorDecoder.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import feign.Response; -import feign.codec.ErrorDecoder; -import java.io.IOException; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; - -@Slf4j -public class CustomFeignErrorDecoder implements ErrorDecoder { - - @Override - public Exception decode(String methodKey, Response response) { - SimpleChallengeGlobalException simpleChallengeGlobalException = - extractChallengeCoreGlobalException(response); - - switch (response.status()) { - case 400: - log.error( - "Error in request went through feign client {} ", - simpleChallengeGlobalException.getMessage() + - " - " + - simpleChallengeGlobalException.getCode() - ); - return simpleChallengeGlobalException; - case 401: - log.error("Unauthorized Request Through Feign"); - return new Exception("Unauthorized Request Through Feign"); - case 404: - log.error("Unidentified Request Through Feign"); - return new Exception("Unidentified Request Through Feign"); - default: - log.error("Error in request went through feign client"); - return new Exception("Common Feign Exception"); - } - } - - private SimpleChallengeGlobalException extractChallengeCoreGlobalException(Response response) { - SimpleChallengeGlobalException exceptionMessage = null; - Reader reader = null; - // capturing error message from response body. - try { - reader = response.body().asReader(StandardCharsets.UTF_8); - String result = IOUtils.toString(reader); - ObjectMapper mapper = new ObjectMapper(); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - exceptionMessage = mapper.readValue(result, SimpleChallengeGlobalException.class); - } catch (IOException e) { - log.error("IO Exception on reading exception message feign client" + e); - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException e) { - log.error("IO Exception on reading exception message feign client" + e); - } - } - return exceptionMessage; - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java deleted file mode 100644 index 49a9c28fcb..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class KeycloakConfiguration { - - @Bean - public KeycloakConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java deleted file mode 100644 index 50121886f6..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import lombok.extern.slf4j.Slf4j; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.admin.client.resource.RealmResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class KeycloakManager { - - @Autowired - private KeycloakManagerProperties keycloakProperties; - - private static Keycloak keycloakInstance = null; - - public RealmResource getKeycloakInstanceWithRealm() { - return getInstance().realm(keycloakProperties.getRealm()); - } - - public Keycloak getInstance() { - if (keycloakInstance == null) { - log.info("KC SERVER URL: {}", keycloakProperties.getServerUrl()); - keycloakInstance = KeycloakBuilder.builder() - .serverUrl(keycloakProperties.getServerUrl()) - .realm(keycloakProperties.getRealm()) - .grantType("client_credentials") - .clientId(keycloakProperties.getClientId()) - .clientSecret(keycloakProperties.getClientSecret()) - .build(); - } - return keycloakInstance; - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java deleted file mode 100644 index 9bd4344836..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "app.config.keycloak") -public class KeycloakManagerProperties { - - private String serverUrl; - private String realm; - private String clientId; - private String clientSecret; -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/OpenApiConfiguration.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/OpenApiConfiguration.java deleted file mode 100644 index efc402d170..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/OpenApiConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class OpenApiConfiguration { - - @Bean - public OpenAPI openApi() { - return new OpenAPI() - .info( - new Info() - .title("User API") - .description("This is the User Service of the OpenChallenges.") - .version("v1.0") - .contact(new Contact().name("This is the User ").url("https://openchallenges.org")) - .termsOfService("TOC") - .license( - new License() - .name("Apache 2.0") - .url("https://github.com/Sage-Bionetworks/sage-monorepo/blob/main/LICENSE.txt") - ) - ); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java deleted file mode 100644 index a6f98e0646..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; - -@KeycloakConfiguration -@EnableGlobalMethodSecurity(jsr250Enabled = true) -class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - - // @Autowired - // RestAccessDeniedHandler restAccessDeniedHandler; - - // @Autowired - // CustomKeycloakAuthenticationHandler customKeycloakAuthenticationHandler; - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - http - .csrf() - .disable() - .cors() - .disable() - .authorizeRequests() - .antMatchers("/api/v1/users/register", "/api/v1/api-docs/**", "/v3/**", "/api/v1/**") - .permitAll() - .anyRequest() - .authenticated(); - // Custom error handler - // http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = - keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); - } - - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); - } - // // Keycloak auth exception handler - // @Bean - // @Override - // protected KeycloakAuthenticationProcessingFilter - // keycloakAuthenticationProcessingFilter() - // throws Exception { - // KeycloakAuthenticationProcessingFilter filter = - // new KeycloakAuthenticationProcessingFilter(this.authenticationManagerBean()); - // filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy()); - // filter.setAuthenticationFailureHandler(customKeycloakAuthenticationHandler); - // return filter; - // } - -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/controller/UserController.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/controller/UserController.java deleted file mode 100644 index 5432d3d736..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/controller/UserController.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.sagebionetworks.challenge.controller; - -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.dto.UserUpdateRequest; -import org.sagebionetworks.challenge.service.UserService; -import org.springdoc.api.annotations.ParameterObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Pageable; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@Slf4j -@RestController -@RequestMapping(value = "/api/v1/users") -public class UserController { - - @Autowired - UserService userService; - - @PostMapping(value = "/register") - public ResponseEntity createUser(@RequestBody User request) { - log.info("Create the user with {}", request.toString()); - return ResponseEntity.ok(userService.createUser(request)); - // return ResponseEntity.ok(null); - } - - @PatchMapping(value = "/{id}") - public ResponseEntity updateUser( - @PathVariable("id") Long userId, - @RequestBody UserUpdateRequest userUpdateRequest - ) { - log.info("Update the user with {}", userUpdateRequest.toString()); - return ResponseEntity.ok(userService.updateUser(userId, userUpdateRequest)); - // return ResponseEntity.ok(null); - } - - // @RolesAllowed("user") - @GetMapping(value = "/") - public ResponseEntity> listUsers(@ParameterObject Pageable pageable) { - log.info("List all the users"); - return ResponseEntity.ok(userService.listUsers(pageable)); - // return ResponseEntity.ok(null); - } - - @GetMapping(value = "/{id}") - public ResponseEntity getUser(@PathVariable("id") Long id) { - log.info("Get the user by id {}", id); - return ResponseEntity.ok(userService.getUser(id)); - // return ResponseEntity.ok(null); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java deleted file mode 100644 index 23fdcd5db7..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/GlobalErrorCode.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class GlobalErrorCode { - - public static final String ERROR_ENTITY_NOT_FOUND = "CHALLENGE-USER-SERVICE-1000"; - public static final String ERROR_USERNAME_REGISTERED = "CHALLENGE-USER-SERVICE-1001"; - public static final String ERROR_INVALID_EMAIL = "CHALLENGE-USER-SERVICE-1002"; - public static final String ERROR_INVALID_USER = "CHALLENGE-USER-SERVICE-1003"; -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java deleted file mode 100644 index 0db45dc58f..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; - -public class InvalidEmailException extends SimpleChallengeGlobalException { - - public InvalidEmailException(String message, String code) { - super(message, code); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java deleted file mode 100644 index f8be1e7a55..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; - -public class InvalidUserException extends SimpleChallengeGlobalException { - - public InvalidUserException(String message, String code) { - super(message, code); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/UserAlreadyRegisteredException.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/UserAlreadyRegisteredException.java deleted file mode 100644 index c21bad1e5b..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/exception/UserAlreadyRegisteredException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import org.sagebionetworks.util.exception.SimpleChallengeGlobalException; - -public class UserAlreadyRegisteredException extends SimpleChallengeGlobalException { - - public UserAlreadyRegisteredException(String message, String code) { - super(message, code); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/User.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/User.java deleted file mode 100644 index b6a2579c2d..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/User.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class User { - - private Long id; - private String username; - private String email; - private String password; - private String authId; - private UserStatus status; - // public User(String username, String email, String password) { - // this.username = username; - // this.email = email; - // this.password = password; - // } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserStatus.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserStatus.java deleted file mode 100644 index 0e586d4c9f..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -public enum UserStatus { - PENDING, - APPROVED, - DISABLED, - BLACKLIST, -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserUpdateRequest.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserUpdateRequest.java deleted file mode 100644 index 54f909f012..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/dto/UserUpdateRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import lombok.Data; - -@Data -public class UserUpdateRequest { - - private UserStatus status; -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java deleted file mode 100644 index f555403c83..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.sagebionetworks.challenge.model.entity; - -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.sagebionetworks.challenge.model.dto.UserStatus; - -@Getter -@Setter -@Entity -@Table(name = "challenge_user") -@NoArgsConstructor -public class UserEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String username; - private String authId; - - @Enumerated(EnumType.STRING) - private UserStatus status; - - public UserEntity(String username, String authId, UserStatus status) { - this.username = username; - this.authId = authId; - this.status = status; - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java deleted file mode 100644 index 6ca7d644f3..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.sagebionetworks.challenge.model.mapper; - -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.util.model.mapper.BaseMapper; -import org.springframework.beans.BeanUtils; - -public class UserMapper extends BaseMapper { - - @Override - public UserEntity convertToEntity(User dto, Object... args) { - UserEntity userEntity = new UserEntity(); - if (dto != null) { - BeanUtils.copyProperties(dto, userEntity); - } - return userEntity; - } - - @Override - public User convertToDto(UserEntity entity, Object... args) { - User user = new User(); - if (entity != null) { - BeanUtils.copyProperties(entity, user); - } - return user; - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java deleted file mode 100644 index abdc24355d..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.sagebionetworks.challenge.model.repository; - -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserRepository extends JpaRepository {} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java deleted file mode 100644 index 8125d94553..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.sagebionetworks.challenge.model.rest.response; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AccountResponse { - - private String number; - private Integer id; - private String type; - private String status; -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java deleted file mode 100644 index 591aa491bf..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.challenge.model.rest.response; - -import java.util.List; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserResponse { - - private String firstName; - private String lastName; - private List challengeAccounts; - private Integer id; - private String email; -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java deleted file mode 100644 index db9f6e6374..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import java.util.Optional; -import javax.ws.rs.core.Response; -import org.keycloak.admin.client.resource.UserResource; -import org.keycloak.representations.idm.UserRepresentation; -import org.sagebionetworks.challenge.configuration.KeycloakManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class KeycloakUserService { - - @Autowired - private KeycloakManager keycloakManager; - - public Integer createUser(UserRepresentation userRepresentation) { - Response response = keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .create(userRepresentation); - return response.getStatus(); - } - - public void updateUser(UserRepresentation userRepresentation) { - keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .get(userRepresentation.getId()) - .update(userRepresentation); - } - - public Optional getUserByUsername(String username) { - return keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .search(username) - .stream() - .filter(userRep -> username.equals(userRep.getUsername())) - .findFirst(); - } - - public UserRepresentation getUser(String authId) { - try { - UserResource userResource = keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .get(authId); - return userResource.toRepresentation(); - } catch (Exception e) { - throw new RuntimeException("User not found under given ID"); - } - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/UserService.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/UserService.java deleted file mode 100644 index 1320e0e870..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/UserService.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import javax.persistence.EntityNotFoundException; -import lombok.extern.slf4j.Slf4j; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.UserRepresentation; -import org.sagebionetworks.challenge.exception.GlobalErrorCode; -import org.sagebionetworks.challenge.exception.InvalidUserException; -import org.sagebionetworks.challenge.exception.UserAlreadyRegisteredException; -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.dto.UserStatus; -import org.sagebionetworks.challenge.model.dto.UserUpdateRequest; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.challenge.model.mapper.UserMapper; -import org.sagebionetworks.challenge.model.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; - -@Slf4j -// @RequiredArgsConstructor -@Service -public class UserService { - - @Autowired - private KeycloakUserService keycloakUserService; - - @Autowired - private UserRepository userRepository; - - private UserMapper userMapper = new UserMapper(); - - public User createUser(User user) { - if (keycloakUserService.getUserByUsername(user.getUsername()).isPresent()) { - throw new UserAlreadyRegisteredException( - "This username is already registered.", - GlobalErrorCode.ERROR_USERNAME_REGISTERED - ); - } - - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setEmail(user.getEmail()); - userRepresentation.setEmailVerified(false); - userRepresentation.setEnabled(true); - userRepresentation.setUsername(user.getUsername()); - - CredentialRepresentation credentialRepresentation = new CredentialRepresentation(); - credentialRepresentation.setValue(user.getPassword()); - credentialRepresentation.setTemporary(false); - userRepresentation.setCredentials(Collections.singletonList(credentialRepresentation)); - - Integer userCreationResponse = keycloakUserService.createUser(userRepresentation); - - if (userCreationResponse == 201) { - Optional representation = keycloakUserService.getUserByUsername( - user.getUsername() - ); - user.setAuthId(representation.get().getId()); - user.setStatus(UserStatus.PENDING); - UserEntity userEntity = userRepository.save(userMapper.convertToEntity(user)); - return userMapper.convertToDto(userEntity); - } - - throw new InvalidUserException( - "Unable to create the new user", - GlobalErrorCode.ERROR_INVALID_USER - ); - } - - public List listUsers(Pageable pageable) { - Page allUsersInDb = userRepository.findAll(pageable); - List users = userMapper.convertToDtoList(allUsersInDb.getContent()); - users.forEach(user -> { - UserRepresentation userRepresentation = keycloakUserService.getUser(user.getAuthId()); - user.setId(user.getId()); - user.setEmail(userRepresentation.getEmail()); - }); - return users; - } - - public User getUser(Long userId) { - return userMapper.convertToDto( - userRepository.findById(userId).orElseThrow(EntityNotFoundException::new) - ); - } - - public User updateUser(Long id, UserUpdateRequest userUpdateRequest) { - UserEntity userEntity = userRepository.findById(id).orElseThrow(EntityNotFoundException::new); - - if (userUpdateRequest.getStatus() == UserStatus.APPROVED) { - UserRepresentation userRepresentation = keycloakUserService.getUser(userEntity.getAuthId()); - userRepresentation.setEnabled(true); - userRepresentation.setEmailVerified(true); - keycloakUserService.updateUser(userRepresentation); - } - - userEntity.setStatus(userUpdateRequest.getStatus()); - return userMapper.convertToDto(userRepository.save(userEntity)); - } -} diff --git a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/rest/ChallengeCoreRestClient.java b/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/rest/ChallengeCoreRestClient.java deleted file mode 100644 index 700da5f326..0000000000 --- a/apps/openchallenges/user-service/src.old/main/java/org/sagebionetworks/challenge/service/rest/ChallengeCoreRestClient.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.sagebionetworks.challenge.service.rest; - -import org.sagebionetworks.challenge.configuration.CustomFeignClientConfiguration; -import org.sagebionetworks.challenge.model.rest.response.UserResponse; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -@FeignClient( - name = "openchallenges-core-service", - configuration = CustomFeignClientConfiguration.class -) -public interface ChallengeCoreRestClient { - @RequestMapping(method = RequestMethod.GET, value = "/api/v1/user/{identification}") - UserResponse readUser(@PathVariable("identification") String identification); -} diff --git a/apps/openchallenges/user-service/src.old/main/resources/application.yml b/apps/openchallenges/user-service/src.old/main/resources/application.yml deleted file mode 100644 index 27222b7b1e..0000000000 --- a/apps/openchallenges/user-service/src.old/main/resources/application.yml +++ /dev/null @@ -1,72 +0,0 @@ -spring: - application: - name: openchallenges-user-service - datasource: - # url: jdbc:mysql://openchallenges-mariadb:3306/challenge - url: ${db.url} - # url: jdbc:h2:mem:challenge - username: challenge_user_service - password: changeme - jpa: - hibernate: - ddl-auto: update - -server: - port: 8083 - -eureka: - client: - service-url: - # defaultZone: ${service.registry.url} - defaultZone: http://openchallenges-service-registry:8081/eureka - instance: - preferIpAddress: true - -management: - info: - env: - enabled: true - endpoints: - web: - exposure: - include: info - -info: - application: - name: ${spring.application.name} - -keycloak: - realm: test - resource: openchallenges-user-service - credentials: - secret: GFRau7cQTxhx19CUmSdrqrqrZ1pshjjB - # auth-server-url: ${keycloak.url} - auth-server-url: http://openchallenges-keycloak:8080 - ssl-required: external - use-resource-role-mappings: true - bearer-only: true - -app: - config: - keycloak: - # server-url: ${keycloak.url} - server-url: http://openchallenges-keycloak:8080 - realm: test - # TODO: Which client ID property is used? - client-id: challenge-api-client - clientId: challenge-api-client - client-secret: mg2DrRcxHx19PIITibdOnbNEbJUKjGKb - -springdoc: - version: openapi_3_1 - swagger-ui: - path: /api/v1/api-docs/ui - # url: /api/v1/api-docs/openapi.json - api-docs: - enabled: true - path: /api/v1/api-docs/openapi.json - packagesToScan: org.sagebionetworks.challenge.controller - pathsToMatch: /** - -example: - firstProperty: 'main' diff --git a/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql b/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql deleted file mode 100644 index 61662f30d7..0000000000 --- a/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql +++ /dev/null @@ -1,13 +0,0 @@ --- challenge.challenge_user definition - -CREATE TABLE `challenge_user` -( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `auth_id` varchar(255) DEFAULT NULL, - `status` varchar(255) DEFAULT NULL, - `username` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -); - --- KEY `FKk9w2ogq595jbe8r2due7vv3xr` (`account_id`), --- CONSTRAINT `FKk9w2ogq595jbe8r2due7vv3xr` FOREIGN KEY (`account_id`) REFERENCES `banking_core_account` (`id`) \ No newline at end of file diff --git a/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174721__temp_data.sql b/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174721__temp_data.sql deleted file mode 100644 index 1ec5e58149..0000000000 --- a/apps/openchallenges/user-service/src.old/main/resources/db/migration/V1.0.20210427174721__temp_data.sql +++ /dev/null @@ -1,7 +0,0 @@ -INSERT INTO challenge_user - (id, auth_id, status, username) -VALUES ('1', 'b9302586-876b-47e8-900c-2c09f161b888', 'PENDING', 'test'), - ('2', '3739ed0d-6da2-4d65-ba5a-223eb2dfe2b6', 'PENDING', 'tschaffter'), - ('3', '9f6abcb0-25aa-49d7-aa1b-5292c8f3e26a', 'PENDING', 'rrchai'), - ('4', '04b800f3-c431-4862-93c4-2bef14fc204d', 'PENDING', 'vpchung'), - ('5', '1212f921-6ab0-444f-a5ea-9dc154199a3c', 'PENDING', 'oakenshield'); \ No newline at end of file diff --git a/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/controller/UserControllerTest.java b/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/controller/UserControllerTest.java deleted file mode 100644 index c342745977..0000000000 --- a/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/controller/UserControllerTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.sagebionetworks.challenge.controller; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.sagebionetworks.challenge.model.repository.UserRepository; -import org.sagebionetworks.challenge.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.web.servlet.MockMvc; - -// import lombok.extern.slf4j.Slf4j; - -// @Slf4j -@SpringBootTest -@AutoConfigureMockMvc -public class UserControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private UserRepository userRepository; - - @MockBean - private UserService userService; - - // private UserMapper userMapper = new UserMapper(); - - // @Autowired - // private TestRestTemplate restTemplate; - - @BeforeEach - void setup() { - userRepository.deleteAll(); - } - - // @DisplayName("Single test successful") - // @Disabled("Not implemented yet") - @Test - public void givenUserObject_whenCreateUser_thenStatus201() throws Exception { - // User user = new User("test", "test@gmail.com", "changeme"); - // Mockito.when(userService.createUser(user)).thenReturn(user); - // mockMvc.perform(post("/api/v1/users/register").contentType(MediaType.APPLICATION_JSON) - // .content(objectMapper.writeValueAsString(user))).andExpect(status().isCreated()); - - // .accept(MediaType.APPLICATION_JSON)) - // .andExpect(status().isCreated()) - // .perform(post("/forums/{forumId}/register", 42L).contentType("application/json") - // .param("sendWelcomeMail", "true").content(objectMapper.writeValueAsString(user))) - // .andExpect(status().isOk()); - } -} diff --git a/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/service/UserServiceTest.java b/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/service/UserServiceTest.java deleted file mode 100644 index 1f9ca1f6ab..0000000000 --- a/apps/openchallenges/user-service/src.old/test/java/org/sagebionetworks/challenge/service/UserServiceTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.when; - -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.keycloak.representations.idm.UserRepresentation; -import org.sagebionetworks.challenge.model.dto.User; -import org.sagebionetworks.challenge.model.dto.UserStatus; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.challenge.model.mapper.UserMapper; -import org.sagebionetworks.challenge.model.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; - -// @Slf4j -@SpringBootTest -public class UserServiceTest { - - @Autowired - private UserService userService; - - @MockBean - private UserRepository userRepository; - - @MockBean - private KeycloakUserService keycloakUserService; - - private User user; - private UserMapper userMapper = new UserMapper(); - - @Value("${example.firstProperty}") - String firstProperty; - - @BeforeEach - public void setup() { - user = User.builder() - .id(1L) - .username("test") - .email("test@gmail.com") - .password("changeme") - .authId("1") - .status(UserStatus.PENDING) - .build(); - } - - @Test - public void test() { - assertThat(firstProperty).isEqualTo("test"); - } - - @Test - void givenUserList_whenGetAllUsers_thenReturnUserList() { - // given - User user2 = User.builder() - .id(2L) - .username("test2") - .email("test2@gmail.com") - .password("changeme") - .authId("2") - .status(UserStatus.PENDING) - .build(); - List givenUsers = List.of(user, user2); - List givenUserList = List.of( - userMapper.convertToEntity(user), - userMapper.convertToEntity(user2) - ); - Page userPage = new PageImpl<>(givenUserList); - when(userRepository.findAll(isA(Pageable.class))).thenReturn(userPage); - when(keycloakUserService.getUser(anyString())).thenAnswer(input -> { - String authId = input.getArgument(0); - User user = givenUsers.stream().filter(u -> authId.equals(u.getAuthId())).findFirst().get(); - UserRepresentation representation = new UserRepresentation(); - representation.setId(user.getId().toString()); - representation.setEmail(user.getEmail()); - return representation; - }); - - // when - List users = userService.listUsers(Pageable.unpaged()); - - // then - assertThat(users.size()).isEqualTo(givenUserList.size()); - } -} diff --git a/apps/openchallenges/user-service/src.old/test/resources/application.yml b/apps/openchallenges/user-service/src.old/test/resources/application.yml deleted file mode 100644 index f4dec6ef3f..0000000000 --- a/apps/openchallenges/user-service/src.old/test/resources/application.yml +++ /dev/null @@ -1,23 +0,0 @@ -spring: - #admin: - # enabled: true - # jmx-name: org.springframework.boot:type=Admin,name=SpringApplicationTest - datasource: - url: jdbc:h2:mem:challenge - driver-class-name: org.h2.Driver - #jpa: - #hibernate: - # ddl-auto: none - #database-platform: org.hibernate.dialect.H2Dialect - flyway: - enabled: false - -server: - port: 8083 - -eureka: - client: - enabled: false - -example: - firstProperty: 'test' diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java deleted file mode 100644 index dbef048b85..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/ChallengeUserServiceApplication.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.sagebionetworks.challenge; - -import com.fasterxml.jackson.databind.Module; -import org.openapitools.jackson.nullable.JsonNullableModule; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; - -// @EnableEurekaClient -@SpringBootApplication -@ComponentScan( - basePackages = { - "org.sagebionetworks.challenge", - "org.sagebionetworks.challenge.api", - "org.sagebionetworks.challenge.configuration", - } -) -public class ChallengeUserServiceApplication { - - public static void main(String[] args) { - SpringApplication.run(ChallengeUserServiceApplication.class, args); - } - - @Bean - public Module jsonNullableModule() { - return new JsonNullableModule(); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/RFC3339DateFormat.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/RFC3339DateFormat.java deleted file mode 100644 index 4a290b06a6..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/RFC3339DateFormat.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.sagebionetworks.challenge; - -import com.fasterxml.jackson.databind.util.StdDateFormat; -import java.text.DateFormat; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -public class RFC3339DateFormat extends DateFormat { - - private static final long serialVersionUID = 1L; - private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC"); - - private final StdDateFormat fmt = new StdDateFormat() - .withTimeZone(TIMEZONE_Z) - .withColonInTimeZone(true); - - public RFC3339DateFormat() { - this.calendar = new GregorianCalendar(); - } - - @Override - public Date parse(String source, ParsePosition pos) { - return fmt.parse(source, pos); - } - - @Override - public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { - return fmt.format(date, toAppendTo, fieldPosition); - } - - @Override - public Object clone() { - return this; - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/ApiUtil.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/ApiUtil.java deleted file mode 100644 index c337239813..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/ApiUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.sagebionetworks.challenge.api; - -import java.io.IOException; -import javax.servlet.http.HttpServletResponse; -import org.springframework.web.context.request.NativeWebRequest; - -public class ApiUtil { - - public static void setExampleResponse(NativeWebRequest req, String contentType, String example) { - try { - HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class); - res.setCharacterEncoding("UTF-8"); - res.addHeader("Content-Type", contentType); - res.getWriter().print(example); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApi.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApi.java deleted file mode 100644 index ee6e8c8f00..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApi.java +++ /dev/null @@ -1,339 +0,0 @@ -/** - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (6.1.0). - * https://openapi-generator.tech Do not edit the class manually. - */ -package org.sagebionetworks.challenge.api; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import javax.annotation.Generated; -import javax.validation.Valid; -import javax.validation.constraints.*; -import org.sagebionetworks.challenge.model.dto.BasicErrorDto; -import org.sagebionetworks.challenge.model.dto.UserCreateRequestDto; -import org.sagebionetworks.challenge.model.dto.UserCreateResponseDto; -import org.sagebionetworks.challenge.model.dto.UserDto; -import org.sagebionetworks.challenge.model.dto.UsersPageDto; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -@Validated -@Tag(name = "User", description = "Operations about users") -@RequestMapping("${openapi.challengeUser.base-path:}") -public interface UserApi { - default UserApiDelegate getDelegate() { - return new UserApiDelegate() {}; - } - - /** - * POST /users/register : Create a user Create a user with the specified account name - * - * @param userCreateRequestDto (required) - * @return Account created (status code 201) or Invalid request (status code 400) or The request - * conflicts with current state of the target resource (status code 409) or The request cannot - * be fulfilled due to an unexpected server error (status code 500) - */ - @Operation( - operationId = "createUser", - summary = "Create a user", - tags = { "User" }, - responses = { - @ApiResponse( - responseCode = "201", - description = "Account created", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = UserCreateResponseDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = UserCreateResponseDto.class) - ), - } - ), - @ApiResponse( - responseCode = "400", - description = "Invalid request", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - @ApiResponse( - responseCode = "409", - description = "The request conflicts with current state of the target resource", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - @ApiResponse( - responseCode = "500", - description = "The request cannot be fulfilled due to an unexpected server error", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - } - ) - @RequestMapping( - method = RequestMethod.POST, - value = "/users/register", - produces = { "application/json", "application/problem+json" }, - consumes = { "application/json" } - ) - default ResponseEntity createUser( - @Parameter( - name = "UserCreateRequestDto", - description = "", - required = true - ) @Valid @RequestBody UserCreateRequestDto userCreateRequestDto - ) { - return getDelegate().createUser(userCreateRequestDto); - } - - /** - * DELETE /users/{userId} : Delete a user Deletes the user specified - * - * @param userId The unique identifier of the user, either the user account ID or login (required) - * @return Deleted (status code 200) or The specified resource was not found (status code 400) or - * The request cannot be fulfilled due to an unexpected server error (status code 500) - */ - @Operation( - operationId = "deleteUser", - summary = "Delete a user", - tags = { "User" }, - responses = { - @ApiResponse( - responseCode = "200", - description = "Deleted", - content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = Object.class)), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = Object.class) - ), - } - ), - @ApiResponse( - responseCode = "400", - description = "The specified resource was not found", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - @ApiResponse( - responseCode = "500", - description = "The request cannot be fulfilled due to an unexpected server error", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - } - ) - @RequestMapping( - method = RequestMethod.DELETE, - value = "/users/{userId}", - produces = { "application/json", "application/problem+json" } - ) - default ResponseEntity deleteUser( - @Parameter( - name = "userId", - description = "The unique identifier of the user, either the user account ID or login", - required = true - ) @PathVariable("userId") Long userId - ) { - return getDelegate().deleteUser(userId); - } - - /** - * GET /users/{userId} : Get a user Returns the user specified - * - * @param userId The unique identifier of the user, either the user account ID or login (required) - * @return A user (status code 200) or The specified resource was not found (status code 404) or - * The request cannot be fulfilled due to an unexpected server error (status code 500) - */ - @Operation( - operationId = "getUser", - summary = "Get a user", - tags = { "User" }, - responses = { - @ApiResponse( - responseCode = "200", - description = "A user", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = UserDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = UserDto.class) - ), - } - ), - @ApiResponse( - responseCode = "404", - description = "The specified resource was not found", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - @ApiResponse( - responseCode = "500", - description = "The request cannot be fulfilled due to an unexpected server error", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - } - ) - @RequestMapping( - method = RequestMethod.GET, - value = "/users/{userId}", - produces = { "application/json", "application/problem+json" } - ) - default ResponseEntity getUser( - @Parameter( - name = "userId", - description = "The unique identifier of the user, either the user account ID or login", - required = true - ) @PathVariable("userId") Long userId - ) { - return getDelegate().getUser(userId); - } - - /** - * GET /users : Get all users Returns the users - * - * @param pageNumber The page number (optional, default to 0) - * @param pageSize The number of items in a single page (optional, default to 100) - * @return Success (status code 200) or Invalid request (status code 400) or The request cannot be - * fulfilled due to an unexpected server error (status code 500) - */ - @Operation( - operationId = "listUsers", - summary = "Get all users", - tags = { "User" }, - responses = { - @ApiResponse( - responseCode = "200", - description = "Success", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = UsersPageDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = UsersPageDto.class) - ), - } - ), - @ApiResponse( - responseCode = "400", - description = "Invalid request", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - @ApiResponse( - responseCode = "500", - description = "The request cannot be fulfilled due to an unexpected server error", - content = { - @Content( - mediaType = "application/json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - @Content( - mediaType = "application/problem+json", - schema = @Schema(implementation = BasicErrorDto.class) - ), - } - ), - } - ) - @RequestMapping( - method = RequestMethod.GET, - value = "/users", - produces = { "application/json", "application/problem+json" } - ) - default ResponseEntity listUsers( - @Min(0) @Parameter(name = "pageNumber", description = "The page number") @Valid @RequestParam( - value = "pageNumber", - required = false, - defaultValue = "0" - ) Integer pageNumber, - @Min(10) @Parameter( - name = "pageSize", - description = "The number of items in a single page" - ) @Valid @RequestParam( - value = "pageSize", - required = false, - defaultValue = "100" - ) Integer pageSize - ) { - return getDelegate().listUsers(pageNumber, pageSize); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiController.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiController.java deleted file mode 100644 index f82513704f..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiController.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.sagebionetworks.challenge.api; - -import java.util.Optional; -import javax.annotation.Generated; -import javax.validation.constraints.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; - -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -@Controller -public class UserApiController implements UserApi { - - private final UserApiDelegate delegate; - - public UserApiController(@Autowired(required = false) UserApiDelegate delegate) { - this.delegate = Optional.ofNullable(delegate).orElse(new UserApiDelegate() {}); - } - - @Override - public UserApiDelegate getDelegate() { - return delegate; - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegate.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegate.java deleted file mode 100644 index cc2de97d4a..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegate.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.sagebionetworks.challenge.api; - -import java.util.Optional; -import javax.annotation.Generated; -import org.sagebionetworks.challenge.model.dto.UserCreateRequestDto; -import org.sagebionetworks.challenge.model.dto.UserCreateResponseDto; -import org.sagebionetworks.challenge.model.dto.UserDto; -import org.sagebionetworks.challenge.model.dto.UsersPageDto; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.NativeWebRequest; - -/** - * A delegate to be called by the {@link UserApiController}}. Implement this interface with a {@link - * org.springframework.stereotype.Service} annotated class. - */ -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -public interface UserApiDelegate { - default Optional getRequest() { - return Optional.empty(); - } - - /** - * POST /users/register : Create a user Create a user with the specified account name - * - * @param userCreateRequestDto (required) - * @return Account created (status code 201) or Invalid request (status code 400) or The request - * conflicts with current state of the target resource (status code 409) or The request cannot - * be fulfilled due to an unexpected server error (status code 500) - * @see UserApi#createUser - */ - default ResponseEntity createUser( - UserCreateRequestDto userCreateRequestDto - ) { - getRequest() - .ifPresent(request -> { - for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { - if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { - String exampleString = "{ \"id\" : \"507f1f77bcf86cd799439011\" }"; - ApiUtil.setExampleResponse(request, "application/json", exampleString); - break; - } - if (mediaType.isCompatibleWith(MediaType.valueOf("application/problem+json"))) { - String exampleString = - "Custom MIME type example not yet supported: application/problem+json"; - ApiUtil.setExampleResponse(request, "application/problem+json", exampleString); - break; - } - } - }); - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - /** - * DELETE /users/{userId} : Delete a user Deletes the user specified - * - * @param userId The unique identifier of the user, either the user account ID or login (required) - * @return Deleted (status code 200) or The specified resource was not found (status code 400) or - * The request cannot be fulfilled due to an unexpected server error (status code 500) - * @see UserApi#deleteUser - */ - default ResponseEntity deleteUser(Long userId) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - /** - * GET /users/{userId} : Get a user Returns the user specified - * - * @param userId The unique identifier of the user, either the user account ID or login (required) - * @return A user (status code 200) or The specified resource was not found (status code 404) or - * The request cannot be fulfilled due to an unexpected server error (status code 500) - * @see UserApi#getUser - */ - default ResponseEntity getUser(Long userId) { - getRequest() - .ifPresent(request -> { - for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { - if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { - String exampleString = - "{ \"login\" : \"awesome-user\", \"email\" : \"awesome-user@example.org\", \"name\" : \"Awesome User\", \"status\" : \"approved\", \"avatarUrl\" : \"https://example.com/awesome-avatar.png\", \"bio\" : \"A great bio\", \"createdAt\" : \"2017-07-08T16:18:44-04:00\", \"updatedAt\" : \"2017-07-08T16:18:44-04:00\", \"type\" : \"User\" }"; - ApiUtil.setExampleResponse(request, "application/json", exampleString); - break; - } - if (mediaType.isCompatibleWith(MediaType.valueOf("application/problem+json"))) { - String exampleString = - "Custom MIME type example not yet supported: application/problem+json"; - ApiUtil.setExampleResponse(request, "application/problem+json", exampleString); - break; - } - } - }); - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - /** - * GET /users : Get all users Returns the users - * - * @param pageNumber The page number (optional, default to 0) - * @param pageSize The number of items in a single page (optional, default to 100) - * @return Success (status code 200) or Invalid request (status code 400) or The request cannot be - * fulfilled due to an unexpected server error (status code 500) - * @see UserApi#listUsers - */ - default ResponseEntity listUsers(Integer pageNumber, Integer pageSize) { - getRequest() - .ifPresent(request -> { - for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { - if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { - String exampleString = "null"; - ApiUtil.setExampleResponse(request, "application/json", exampleString); - break; - } - if (mediaType.isCompatibleWith(MediaType.valueOf("application/problem+json"))) { - String exampleString = - "Custom MIME type example not yet supported: application/problem+json"; - ApiUtil.setExampleResponse(request, "application/problem+json", exampleString); - break; - } - } - }); - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegateImpl.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegateImpl.java deleted file mode 100644 index e74fa5c1a7..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/api/UserApiDelegateImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.sagebionetworks.challenge.api; - -import lombok.extern.slf4j.Slf4j; -import org.sagebionetworks.challenge.model.dto.UserCreateRequestDto; -import org.sagebionetworks.challenge.model.dto.UserCreateResponseDto; -import org.sagebionetworks.challenge.model.dto.UserDto; -import org.sagebionetworks.challenge.model.dto.UsersPageDto; -import org.sagebionetworks.challenge.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class UserApiDelegateImpl implements UserApiDelegate { - - @Autowired - UserService userService; - - @Override - public ResponseEntity createUser(UserCreateRequestDto userCreateRequest) { - return new ResponseEntity<>(userService.createUser(userCreateRequest), HttpStatus.CREATED); - } - - @Override - public ResponseEntity getUser(Long userId) { - return ResponseEntity.ok(userService.getUser(userId)); - } - - @Override - public ResponseEntity listUsers(Integer pageNumber, Integer pageSize) { - return ResponseEntity.ok(userService.listUsers(pageNumber, pageSize)); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/HomeController.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/HomeController.java deleted file mode 100644 index 531460baad..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/HomeController.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -/** Home redirection to OpenAPI api documentation */ -@Controller -public class HomeController { - - @RequestMapping("/") - public String index() { - return "redirect:swagger-ui.html"; - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java deleted file mode 100644 index 49a9c28fcb..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class KeycloakConfiguration { - - @Bean - public KeycloakConfigResolver keycloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java deleted file mode 100644 index 50121886f6..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManager.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import lombok.extern.slf4j.Slf4j; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.admin.client.resource.RealmResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class KeycloakManager { - - @Autowired - private KeycloakManagerProperties keycloakProperties; - - private static Keycloak keycloakInstance = null; - - public RealmResource getKeycloakInstanceWithRealm() { - return getInstance().realm(keycloakProperties.getRealm()); - } - - public Keycloak getInstance() { - if (keycloakInstance == null) { - log.info("KC SERVER URL: {}", keycloakProperties.getServerUrl()); - keycloakInstance = KeycloakBuilder.builder() - .serverUrl(keycloakProperties.getServerUrl()) - .realm(keycloakProperties.getRealm()) - .grantType("client_credentials") - .clientId(keycloakProperties.getClientId()) - .clientSecret(keycloakProperties.getClientSecret()) - .build(); - } - return keycloakInstance; - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java deleted file mode 100644 index 9bd4344836..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/KeycloakManagerProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "app.config.keycloak") -public class KeycloakManagerProperties { - - private String serverUrl; - private String realm; - private String clientId; - private String clientSecret; -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java deleted file mode 100644 index 0e75826adf..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SecurityConfiguration.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; - -@KeycloakConfiguration -@EnableGlobalMethodSecurity(jsr250Enabled = true) -class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { - - // @Autowired - // RestAccessDeniedHandler restAccessDeniedHandler; - - // @Autowired - // CustomKeycloakAuthenticationHandler customKeycloakAuthenticationHandler; - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - http - .csrf() - .disable() - .cors() - .disable() - .authorizeRequests() - // .antMatchers("/api/v1/users/register", "/api/v1/**", "/swagger-ui/**", - // "/swagger-ui.html") - .antMatchers("**") - .permitAll() - .anyRequest() - .authenticated(); - // Custom error handler - // http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler); - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - KeycloakAuthenticationProvider keycloakAuthenticationProvider = - keycloakAuthenticationProvider(); - keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keycloakAuthenticationProvider); - } - - @Bean - @Override - protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); - } - // // Keycloak auth exception handler - // @Bean - // @Override - // protected KeycloakAuthenticationProcessingFilter - // keycloakAuthenticationProcessingFilter() - // throws Exception { - // KeycloakAuthenticationProcessingFilter filter = - // new KeycloakAuthenticationProcessingFilter(this.authenticationManagerBean()); - // filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy()); - // filter.setAuthenticationFailureHandler(customKeycloakAuthenticationHandler); - // return filter; - // } - -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SpringDocConfiguration.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SpringDocConfiguration.java deleted file mode 100644 index 9ed13eb52a..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/configuration/SpringDocConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.sagebionetworks.challenge.configuration; - -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SpringDocConfiguration { - - @Bean - OpenAPI apiInfo() { - return new OpenAPI() - .info( - new Info() - .title("Challenge User API") - .description( - "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" - ) - .contact( - new Contact().name("Support").url("https://github.com/Sage-Bionetworks/sage-monorepo") - ) - .license( - new License() - .name("Apache 2.0") - .url("https://github.com/Sage-Bionetworks/sage-monorepo") - ) - .version("0.6.0") - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/ErrorConstants.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/ErrorConstants.java deleted file mode 100644 index 11b5a55f98..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/ErrorConstants.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum ErrorConstants { - ENTITY_NOT_FOUND("CHALLENGE-USER-SERVICE-1000", "Entity not found", HttpStatus.NOT_FOUND), - USERNAME_ALREADY_EXISTS( - "CHALLENGE-USER-SERVICE-1001", - "Username already exists", - HttpStatus.CONFLICT - ), - INVALID_EMAIL("CHALLENGE-USER-SERVICE-1002", "Invalid email", HttpStatus.BAD_REQUEST), - INVALID_USER("CHALLENGE-USER-SERVICE-1003", "Invalid user", HttpStatus.BAD_REQUEST); - - private String type; - private String title; - private HttpStatus status; -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java deleted file mode 100644 index cf2e55f9d2..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import java.util.Locale; -import org.sagebionetworks.challenge.model.dto.BasicErrorDto; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - -@ControllerAdvice -public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { - - @ExceptionHandler(SimpleChallengeGlobalException.class) - protected ResponseEntity handleGlobalException( - SimpleChallengeGlobalException simpleChallengeGlobalException, - Locale locale - ) { - return new ResponseEntity<>( - BasicErrorDto.builder() - .type(simpleChallengeGlobalException.getType()) - .title(simpleChallengeGlobalException.getTitle()) - .status(simpleChallengeGlobalException.getStatus().value()) - .detail(simpleChallengeGlobalException.getDetail()) - .build(), - simpleChallengeGlobalException.getStatus() - ); - } - - @ExceptionHandler({ Exception.class }) - protected ResponseEntity handleException(Exception e, Locale locale) { - return ResponseEntity.internalServerError() - .body( - BasicErrorDto.builder() - .title("An exception occured") - .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) - .build() - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java deleted file mode 100644 index 42607c9f8d..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidEmailException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class InvalidEmailException extends SimpleChallengeGlobalException { - - public InvalidEmailException(String detail) { - super( - ErrorConstants.INVALID_EMAIL.getType(), - ErrorConstants.INVALID_EMAIL.getTitle(), - ErrorConstants.INVALID_EMAIL.getStatus(), - detail - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java deleted file mode 100644 index 10ccdda8b4..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/InvalidUserException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class InvalidUserException extends SimpleChallengeGlobalException { - - public InvalidUserException(String detail) { - super( - ErrorConstants.INVALID_USER.getType(), - ErrorConstants.INVALID_USER.getTitle(), - ErrorConstants.INVALID_USER.getStatus(), - detail - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/SimpleChallengeGlobalException.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/SimpleChallengeGlobalException.java deleted file mode 100644 index ce882ad3bf..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/SimpleChallengeGlobalException.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.springframework.http.HttpStatus; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class SimpleChallengeGlobalException extends RuntimeException { - - private String type; - private String title; - private HttpStatus status; - private String detail; - - public SimpleChallengeGlobalException(String details) { - super(details); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UserNotFoundException.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UserNotFoundException.java deleted file mode 100644 index 88adbd233c..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UserNotFoundException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class UserNotFoundException extends SimpleChallengeGlobalException { - - public UserNotFoundException(String detail) { - super( - ErrorConstants.ENTITY_NOT_FOUND.getType(), - ErrorConstants.ENTITY_NOT_FOUND.getTitle(), - ErrorConstants.ENTITY_NOT_FOUND.getStatus(), - detail - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UsernameAlreadyExistsException.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UsernameAlreadyExistsException.java deleted file mode 100644 index 6fd63f822c..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/exception/UsernameAlreadyExistsException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sagebionetworks.challenge.exception; - -public class UsernameAlreadyExistsException extends SimpleChallengeGlobalException { - - public UsernameAlreadyExistsException(String detail) { - super( - ErrorConstants.USERNAME_ALREADY_EXISTS.getType(), - ErrorConstants.USERNAME_ALREADY_EXISTS.getTitle(), - ErrorConstants.USERNAME_ALREADY_EXISTS.getStatus(), - detail - ); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/BasicErrorDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/BasicErrorDto.java deleted file mode 100644 index 0e1075d846..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/BasicErrorDto.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.constraints.*; - -/** Problem details (tools.ietf.org/html/rfc7807) */ -@Schema(name = "BasicError", description = "Problem details (tools.ietf.org/html/rfc7807)") -@JsonTypeName("BasicError") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -@lombok.Builder -public class BasicErrorDto { - - @JsonProperty("title") - private String title; - - @JsonProperty("status") - private Integer status; - - @JsonProperty("detail") - private String detail; - - @JsonProperty("type") - private String type; - - public BasicErrorDto title(String title) { - this.title = title; - return this; - } - - /** - * A human readable documentation for the problem type - * - * @return title - */ - @NotNull - @Schema( - name = "title", - description = "A human readable documentation for the problem type", - required = true - ) - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public BasicErrorDto status(Integer status) { - this.status = status; - return this; - } - - /** - * The HTTP status code - * - * @return status - */ - @NotNull - @Schema(name = "status", description = "The HTTP status code", required = true) - public Integer getStatus() { - return status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - public BasicErrorDto detail(String detail) { - this.detail = detail; - return this; - } - - /** - * A human readable explanation specific to this occurrence of the problem - * - * @return detail - */ - @Schema( - name = "detail", - description = "A human readable explanation specific to this occurrence of the problem", - required = false - ) - public String getDetail() { - return detail; - } - - public void setDetail(String detail) { - this.detail = detail; - } - - public BasicErrorDto type(String type) { - this.type = type; - return this; - } - - /** - * An absolute URI that identifies the problem type - * - * @return type - */ - @Schema( - name = "type", - description = "An absolute URI that identifies the problem type", - required = false - ) - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BasicErrorDto basicError = (BasicErrorDto) o; - return ( - Objects.equals(this.title, basicError.title) && - Objects.equals(this.status, basicError.status) && - Objects.equals(this.detail, basicError.detail) && - Objects.equals(this.type, basicError.type) - ); - } - - @Override - public int hashCode() { - return Objects.hash(title, status, detail, type); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class BasicErrorDto {\n"); - sb.append(" title: ").append(toIndentedString(title)).append("\n"); - sb.append(" status: ").append(toIndentedString(status)).append("\n"); - sb.append(" detail: ").append(toIndentedString(detail)).append("\n"); - sb.append(" type: ").append(toIndentedString(type)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataDto.java deleted file mode 100644 index 8d88ba5cdc..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataDto.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.Valid; -import javax.validation.constraints.*; - -/** The metadata of a page */ -@Schema(name = "PageMetadata", description = "The metadata of a page") -@JsonTypeName("PageMetadata") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -// TODO Add x-java-class-annotations -public class PageMetadataDto { - - @JsonProperty("paging") - private PageMetadataPagingDto paging; - - @JsonProperty("totalResults") - private Integer totalResults; - - public PageMetadataDto paging(PageMetadataPagingDto paging) { - this.paging = paging; - return this; - } - - /** - * Get paging - * - * @return paging - */ - @NotNull - @Valid - @Schema(name = "paging", required = true) - public PageMetadataPagingDto getPaging() { - return paging; - } - - public void setPaging(PageMetadataPagingDto paging) { - this.paging = paging; - } - - public PageMetadataDto totalResults(Integer totalResults) { - this.totalResults = totalResults; - return this; - } - - /** - * Total number of results in the result set - * - * @return totalResults - */ - @NotNull - @Schema( - name = "totalResults", - description = "Total number of results in the result set", - required = true - ) - public Integer getTotalResults() { - return totalResults; - } - - public void setTotalResults(Integer totalResults) { - this.totalResults = totalResults; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PageMetadataDto pageMetadata = (PageMetadataDto) o; - return ( - Objects.equals(this.paging, pageMetadata.paging) && - Objects.equals(this.totalResults, pageMetadata.totalResults) - ); - } - - @Override - public int hashCode() { - return Objects.hash(paging, totalResults); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class PageMetadataDto {\n"); - sb.append(" paging: ").append(toIndentedString(paging)).append("\n"); - sb.append(" totalResults: ").append(toIndentedString(totalResults)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataPagingDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataPagingDto.java deleted file mode 100644 index 85347f0105..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/PageMetadataPagingDto.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.constraints.*; - -/** Links to navigate to different pages of results */ -@Schema( - name = "PageMetadata_paging", - description = "Links to navigate to different pages of results" -) -@JsonTypeName("PageMetadata_paging") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -// TODO Add x-java-class-annotations -public class PageMetadataPagingDto { - - @JsonProperty("next") - private String next; - - public PageMetadataPagingDto next(String next) { - this.next = next; - return this; - } - - /** - * Link to the next page of results - * - * @return next - */ - @Schema(name = "next", description = "Link to the next page of results", required = false) - public String getNext() { - return next; - } - - public void setNext(String next) { - this.next = next; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PageMetadataPagingDto pageMetadataPaging = (PageMetadataPagingDto) o; - return Objects.equals(this.next, pageMetadataPaging.next); - } - - @Override - public int hashCode() { - return Objects.hash(next); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class PageMetadataPagingDto {\n"); - sb.append(" next: ").append(toIndentedString(next)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateRequestDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateRequestDto.java deleted file mode 100644 index c4f02ce961..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateRequestDto.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.Arrays; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.constraints.*; -import org.openapitools.jackson.nullable.JsonNullable; - -/** The information required to create a user account */ -@Schema( - name = "UserCreateRequest", - description = "The information required to create a user account" -) -@JsonTypeName("UserCreateRequest") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -// TODO Add x-java-class-annotations -public class UserCreateRequestDto { - - @JsonProperty("login") - private String login; - - @JsonProperty("email") - private String email; - - @JsonProperty("password") - private String password; - - @JsonProperty("name") - private JsonNullable name = JsonNullable.undefined(); - - @JsonProperty("avatarUrl") - private JsonNullable avatarUrl = JsonNullable.undefined(); - - @JsonProperty("bio") - private JsonNullable bio = JsonNullable.undefined(); - - public UserCreateRequestDto login(String login) { - this.login = login; - return this; - } - - /** - * Get login - * - * @return login - */ - @NotNull - @Schema(name = "login", required = true) - public String getLogin() { - return login; - } - - public void setLogin(String login) { - this.login = login; - } - - public UserCreateRequestDto email(String email) { - this.email = email; - return this; - } - - /** - * An email address - * - * @return email - */ - @NotNull - @Email - @Schema( - name = "email", - example = "john.smith@example.com", - description = "An email address", - required = true - ) - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public UserCreateRequestDto password(String password) { - this.password = password; - return this; - } - - /** - * Get password - * - * @return password - */ - @NotNull - @Schema(name = "password", required = true) - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public UserCreateRequestDto name(String name) { - this.name = JsonNullable.of(name); - return this; - } - - /** - * Get name - * - * @return name - */ - @Schema(name = "name", required = false) - public JsonNullable getName() { - return name; - } - - public void setName(JsonNullable name) { - this.name = name; - } - - public UserCreateRequestDto avatarUrl(String avatarUrl) { - this.avatarUrl = JsonNullable.of(avatarUrl); - return this; - } - - /** - * Get avatarUrl - * - * @return avatarUrl - */ - @Schema(name = "avatarUrl", example = "https://example.com/awesome-avatar.png", required = false) - public JsonNullable getAvatarUrl() { - return avatarUrl; - } - - public void setAvatarUrl(JsonNullable avatarUrl) { - this.avatarUrl = avatarUrl; - } - - public UserCreateRequestDto bio(String bio) { - this.bio = JsonNullable.of(bio); - return this; - } - - /** - * Get bio - * - * @return bio - */ - @Schema(name = "bio", required = false) - public JsonNullable getBio() { - return bio; - } - - public void setBio(JsonNullable bio) { - this.bio = bio; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserCreateRequestDto userCreateRequest = (UserCreateRequestDto) o; - return ( - Objects.equals(this.login, userCreateRequest.login) && - Objects.equals(this.email, userCreateRequest.email) && - Objects.equals(this.password, userCreateRequest.password) && - Objects.equals(this.name, userCreateRequest.name) && - Objects.equals(this.avatarUrl, userCreateRequest.avatarUrl) && - Objects.equals(this.bio, userCreateRequest.bio) - ); - } - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return ( - a == b || - (a != null && - b != null && - a.isPresent() && - b.isPresent() && - Objects.deepEquals(a.get(), b.get())) - ); - } - - @Override - public int hashCode() { - return Objects.hash(login, email, password, name, avatarUrl, bio); - } - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[] { a.get() }) : 31; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class UserCreateRequestDto {\n"); - sb.append(" login: ").append(toIndentedString(login)).append("\n"); - sb.append(" email: ").append(toIndentedString(email)).append("\n"); - sb.append(" password: ").append(toIndentedString(password)).append("\n"); - sb.append(" name: ").append(toIndentedString(name)).append("\n"); - sb.append(" avatarUrl: ").append(toIndentedString(avatarUrl)).append("\n"); - sb.append(" bio: ").append(toIndentedString(bio)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateResponseDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateResponseDto.java deleted file mode 100644 index fc09c66fce..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserCreateResponseDto.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.constraints.*; - -/** The response returned after the creation of the user */ -@Schema( - name = "UserCreateResponse", - description = "The response returned after the creation of the user" -) -@JsonTypeName("UserCreateResponse") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -@lombok.Builder -public class UserCreateResponseDto { - - @JsonProperty("id") - private Long id; - - public UserCreateResponseDto id(Long id) { - this.id = id; - return this; - } - - /** - * The unique identifier of an account - * - * @return id - */ - @NotNull - @Schema( - name = "id", - example = "1", - description = "The unique identifier of an account", - required = true - ) - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserCreateResponseDto userCreateResponse = (UserCreateResponseDto) o; - return Objects.equals(this.id, userCreateResponse.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class UserCreateResponseDto {\n"); - sb.append(" id: ").append(toIndentedString(id)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserDto.java deleted file mode 100644 index ed216ec386..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserDto.java +++ /dev/null @@ -1,337 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.time.OffsetDateTime; -import java.util.*; -import java.util.Arrays; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.Valid; -import javax.validation.constraints.*; -import org.openapitools.jackson.nullable.JsonNullable; -import org.springframework.format.annotation.DateTimeFormat; - -/** A simple user */ -@Schema(name = "User", description = "A simple user") -@JsonTypeName("User") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -// TODO Add x-java-class-annotations -public class UserDto { - - @JsonProperty("id") - private Long id; - - @JsonProperty("login") - private String login; - - @JsonProperty("email") - private String email; - - @JsonProperty("name") - private JsonNullable name = JsonNullable.undefined(); - - @JsonProperty("status") - private UserStatusDto status; - - @JsonProperty("avatarUrl") - private JsonNullable avatarUrl = JsonNullable.undefined(); - - @JsonProperty("createdAt") - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - private OffsetDateTime createdAt; - - @JsonProperty("updatedAt") - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - private OffsetDateTime updatedAt; - - @JsonProperty("type") - private String type; - - @JsonProperty("bio") - private JsonNullable bio = JsonNullable.undefined(); - - public UserDto id(Long id) { - this.id = id; - return this; - } - - /** - * The unique identifier of an account - * - * @return id - */ - @Schema( - name = "id", - example = "1", - description = "The unique identifier of an account", - required = false - ) - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public UserDto login(String login) { - this.login = login; - return this; - } - - /** - * Get login - * - * @return login - */ - @NotNull - @Schema(name = "login", required = true) - public String getLogin() { - return login; - } - - public void setLogin(String login) { - this.login = login; - } - - public UserDto email(String email) { - this.email = email; - return this; - } - - /** - * An email address - * - * @return email - */ - @NotNull - @Email - @Schema( - name = "email", - example = "john.smith@example.com", - description = "An email address", - required = true - ) - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public UserDto name(String name) { - this.name = JsonNullable.of(name); - return this; - } - - /** - * Get name - * - * @return name - */ - @Schema(name = "name", required = false) - public JsonNullable getName() { - return name; - } - - public void setName(JsonNullable name) { - this.name = name; - } - - public UserDto status(UserStatusDto status) { - this.status = status; - return this; - } - - /** - * Get status - * - * @return status - */ - @Valid - @Schema(name = "status", required = false) - public UserStatusDto getStatus() { - return status; - } - - public void setStatus(UserStatusDto status) { - this.status = status; - } - - public UserDto avatarUrl(String avatarUrl) { - this.avatarUrl = JsonNullable.of(avatarUrl); - return this; - } - - /** - * Get avatarUrl - * - * @return avatarUrl - */ - @Schema(name = "avatarUrl", example = "https://example.com/awesome-avatar.png", required = false) - public JsonNullable getAvatarUrl() { - return avatarUrl; - } - - public void setAvatarUrl(JsonNullable avatarUrl) { - this.avatarUrl = avatarUrl; - } - - public UserDto createdAt(OffsetDateTime createdAt) { - this.createdAt = createdAt; - return this; - } - - /** - * Get createdAt - * - * @return createdAt - */ - @NotNull - @Valid - @Schema(name = "createdAt", required = true) - public OffsetDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(OffsetDateTime createdAt) { - this.createdAt = createdAt; - } - - public UserDto updatedAt(OffsetDateTime updatedAt) { - this.updatedAt = updatedAt; - return this; - } - - /** - * Get updatedAt - * - * @return updatedAt - */ - @NotNull - @Valid - @Schema(name = "updatedAt", required = true) - public OffsetDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(OffsetDateTime updatedAt) { - this.updatedAt = updatedAt; - } - - public UserDto type(String type) { - this.type = type; - return this; - } - - /** - * Get type - * - * @return type - */ - @NotNull - @Schema(name = "type", example = "User", required = true) - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public UserDto bio(String bio) { - this.bio = JsonNullable.of(bio); - return this; - } - - /** - * Get bio - * - * @return bio - */ - @Schema(name = "bio", required = false) - public JsonNullable getBio() { - return bio; - } - - public void setBio(JsonNullable bio) { - this.bio = bio; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserDto user = (UserDto) o; - return ( - Objects.equals(this.id, user.id) && - Objects.equals(this.login, user.login) && - Objects.equals(this.email, user.email) && - Objects.equals(this.name, user.name) && - Objects.equals(this.status, user.status) && - Objects.equals(this.avatarUrl, user.avatarUrl) && - Objects.equals(this.createdAt, user.createdAt) && - Objects.equals(this.updatedAt, user.updatedAt) && - Objects.equals(this.type, user.type) && - Objects.equals(this.bio, user.bio) - ); - } - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return ( - a == b || - (a != null && - b != null && - a.isPresent() && - b.isPresent() && - Objects.deepEquals(a.get(), b.get())) - ); - } - - @Override - public int hashCode() { - return Objects.hash(id, login, email, name, status, avatarUrl, createdAt, updatedAt, type, bio); - } - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[] { a.get() }) : 31; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class UserDto {\n"); - sb.append(" id: ").append(toIndentedString(id)).append("\n"); - sb.append(" login: ").append(toIndentedString(login)).append("\n"); - sb.append(" email: ").append(toIndentedString(email)).append("\n"); - sb.append(" name: ").append(toIndentedString(name)).append("\n"); - sb.append(" status: ").append(toIndentedString(status)).append("\n"); - sb.append(" avatarUrl: ").append(toIndentedString(avatarUrl)).append("\n"); - sb.append(" createdAt: ").append(toIndentedString(createdAt)).append("\n"); - sb.append(" updatedAt: ").append(toIndentedString(updatedAt)).append("\n"); - sb.append(" type: ").append(toIndentedString(type)).append("\n"); - sb.append(" bio: ").append(toIndentedString(bio)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserStatusDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserStatusDto.java deleted file mode 100644 index 99b412dd83..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UserStatusDto.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.*; -import javax.annotation.Generated; -import javax.validation.constraints.*; - -/** The account status of a user */ -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -public enum UserStatusDto { - PENDING("pending"), - - APPROVED("approved"), - - DISABLED("disabled"), - - BLACKLIST("blacklist"); - - private String value; - - UserStatusDto(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } - - @Override - public String toString() { - return String.valueOf(value); - } - - @JsonCreator - public static UserStatusDto fromValue(String value) { - for (UserStatusDto b : UserStatusDto.values()) { - if (b.value.equals(value)) { - return b; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UsersPageDto.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UsersPageDto.java deleted file mode 100644 index c63d25e312..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/dto/UsersPageDto.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.sagebionetworks.challenge.model.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import javax.annotation.Generated; -import javax.validation.Valid; -import javax.validation.constraints.*; - -/** A page of users */ -@Schema(name = "UsersPage", description = "A page of users") -@JsonTypeName("UsersPage") -@Generated(value = "org.openapitools.codegen.languages.SpringCodegen") -@lombok.Builder -public class UsersPageDto { - - @JsonProperty("paging") - private PageMetadataPagingDto paging; - - @JsonProperty("totalResults") - private Integer totalResults; - - @JsonProperty("users") - @Valid - private List users = new ArrayList<>(); - - public UsersPageDto paging(PageMetadataPagingDto paging) { - this.paging = paging; - return this; - } - - /** - * Get paging - * - * @return paging - */ - @NotNull - @Valid - @Schema(name = "paging", required = true) - public PageMetadataPagingDto getPaging() { - return paging; - } - - public void setPaging(PageMetadataPagingDto paging) { - this.paging = paging; - } - - public UsersPageDto totalResults(Integer totalResults) { - this.totalResults = totalResults; - return this; - } - - /** - * Total number of results in the result set - * - * @return totalResults - */ - @NotNull - @Schema( - name = "totalResults", - description = "Total number of results in the result set", - required = true - ) - public Integer getTotalResults() { - return totalResults; - } - - public void setTotalResults(Integer totalResults) { - this.totalResults = totalResults; - } - - public UsersPageDto users(List users) { - this.users = users; - return this; - } - - public UsersPageDto addUsersItem(UserDto usersItem) { - this.users.add(usersItem); - return this; - } - - /** - * A list of users - * - * @return users - */ - @NotNull - @Valid - @Schema(name = "users", description = "A list of users", required = true) - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UsersPageDto usersPage = (UsersPageDto) o; - return ( - Objects.equals(this.paging, usersPage.paging) && - Objects.equals(this.totalResults, usersPage.totalResults) && - Objects.equals(this.users, usersPage.users) - ); - } - - @Override - public int hashCode() { - return Objects.hash(paging, totalResults, users); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class UsersPageDto {\n"); - sb.append(" paging: ").append(toIndentedString(paging)).append("\n"); - sb.append(" totalResults: ").append(toIndentedString(totalResults)).append("\n"); - sb.append(" users: ").append(toIndentedString(users)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java deleted file mode 100644 index 5980692808..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/entity/UserEntity.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.sagebionetworks.challenge.model.entity; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.sagebionetworks.challenge.model.dto.UserStatusDto; - -/** - * The User information saved to DB. - * - *

The following properties are saved in Keycloak: email, password (hash). - */ -@Entity -@Table(name = "challenge_user") -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class UserEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(nullable = false, updatable = false) - private Long id; - - @Column(nullable = false) - private String login; - - @Column(nullable = false) - private String authId; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private UserStatusDto status; -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java deleted file mode 100644 index 4e3d650660..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/mapper/UserMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.sagebionetworks.challenge.model.mapper; - -import org.sagebionetworks.challenge.model.dto.UserDto; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.util.model.mapper.BaseMapper; -import org.springframework.beans.BeanUtils; - -public class UserMapper extends BaseMapper { - - @Override - public UserEntity convertToEntity(UserDto dto, Object... args) { - UserEntity userEntity = new UserEntity(); - if (dto != null) { - BeanUtils.copyProperties(dto, userEntity); - } - return userEntity; - } - - @Override - public UserDto convertToDto(UserEntity entity, Object... args) { - UserDto user = new UserDto(); - if (entity != null) { - BeanUtils.copyProperties(entity, user); - } - return user; - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java deleted file mode 100644 index abdc24355d..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/repository/UserRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.sagebionetworks.challenge.model.repository; - -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserRepository extends JpaRepository {} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java deleted file mode 100644 index 8125d94553..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/AccountResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.sagebionetworks.challenge.model.rest.response; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AccountResponse { - - private String number; - private Integer id; - private String type; - private String status; -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java deleted file mode 100644 index 591aa491bf..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/model/rest/response/UserResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.sagebionetworks.challenge.model.rest.response; - -import java.util.List; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class UserResponse { - - private String firstName; - private String lastName; - private List challengeAccounts; - private Integer id; - private String email; -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java deleted file mode 100644 index db9f6e6374..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/KeycloakUserService.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import java.util.Optional; -import javax.ws.rs.core.Response; -import org.keycloak.admin.client.resource.UserResource; -import org.keycloak.representations.idm.UserRepresentation; -import org.sagebionetworks.challenge.configuration.KeycloakManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class KeycloakUserService { - - @Autowired - private KeycloakManager keycloakManager; - - public Integer createUser(UserRepresentation userRepresentation) { - Response response = keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .create(userRepresentation); - return response.getStatus(); - } - - public void updateUser(UserRepresentation userRepresentation) { - keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .get(userRepresentation.getId()) - .update(userRepresentation); - } - - public Optional getUserByUsername(String username) { - return keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .search(username) - .stream() - .filter(userRep -> username.equals(userRep.getUsername())) - .findFirst(); - } - - public UserRepresentation getUser(String authId) { - try { - UserResource userResource = keycloakManager - .getKeycloakInstanceWithRealm() - .users() - .get(authId); - return userResource.toRepresentation(); - } catch (Exception e) { - throw new RuntimeException("User not found under given ID"); - } - } -} diff --git a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java b/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java deleted file mode 100644 index 3f323fd3dc..0000000000 --- a/apps/openchallenges/user-service/src/main/java/org/sagebionetworks/challenge/service/UserService.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.sagebionetworks.challenge.service; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import lombok.extern.slf4j.Slf4j; -import org.keycloak.representations.idm.CredentialRepresentation; -import org.keycloak.representations.idm.UserRepresentation; -import org.sagebionetworks.challenge.exception.InvalidUserException; -import org.sagebionetworks.challenge.exception.UserNotFoundException; -import org.sagebionetworks.challenge.exception.UsernameAlreadyExistsException; -import org.sagebionetworks.challenge.model.dto.UserCreateRequestDto; -import org.sagebionetworks.challenge.model.dto.UserCreateResponseDto; -import org.sagebionetworks.challenge.model.dto.UserDto; -import org.sagebionetworks.challenge.model.dto.UserStatusDto; -import org.sagebionetworks.challenge.model.dto.UsersPageDto; -import org.sagebionetworks.challenge.model.entity.UserEntity; -import org.sagebionetworks.challenge.model.mapper.UserMapper; -import org.sagebionetworks.challenge.model.repository.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Slf4j -@Service -public class UserService { - - @Autowired - private KeycloakUserService keycloakUserService; - - @Autowired - private UserRepository userRepository; - - private UserMapper userMapper = new UserMapper(); - - @Transactional - public UserCreateResponseDto createUser(UserCreateRequestDto userCreateRequest) { - if (keycloakUserService.getUserByUsername(userCreateRequest.getLogin()).isPresent()) { - throw new UsernameAlreadyExistsException( - String.format("The username %s already exist.", userCreateRequest.getLogin()) - ); - } - - UserRepresentation userRepresentation = new UserRepresentation(); - userRepresentation.setEmail(userCreateRequest.getEmail()); - userRepresentation.setEmailVerified(false); - userRepresentation.setEnabled(true); - userRepresentation.setUsername(userCreateRequest.getLogin()); - - CredentialRepresentation credentialRepresentation = new CredentialRepresentation(); - credentialRepresentation.setValue(userCreateRequest.getPassword()); - credentialRepresentation.setTemporary(false); - userRepresentation.setCredentials(Collections.singletonList(credentialRepresentation)); - - Integer userCreationResponse = keycloakUserService.createUser(userRepresentation); - - if (userCreationResponse == 201) { - Optional representation = keycloakUserService.getUserByUsername( - userCreateRequest.getLogin() - ); - UserEntity user = new UserEntity(); - user.setAuthId(representation.get().getId()); - user.setStatus(UserStatusDto.PENDING); - UserEntity savedUser = userRepository.save(user); - return UserCreateResponseDto.builder().id(savedUser.getId()).build(); - } - - throw new InvalidUserException(null); - } - - @Transactional(readOnly = true) - public UsersPageDto listUsers(Integer pageNumber, Integer pageSize) { - Page userEntitiesPage = userRepository.findAll( - PageRequest.of(pageNumber, pageSize) - ); - List userEntities = userEntitiesPage.getContent(); - List users = new ArrayList<>(); - userEntities.forEach(userEntity -> { - UserRepresentation userRepresentation = keycloakUserService.getUser(userEntity.getAuthId()); - UserDto user = userMapper.convertToDto(userEntity); - user.email(userRepresentation.getEmail()); - user.login(userRepresentation.getUsername()); - users.add(user); - }); - return UsersPageDto.builder().users(users).totalResults(0).paging(null).build(); - } - - @Transactional(readOnly = true) - public UserDto getUser(Long userId) { - UserEntity userEntity = userRepository - .findById(userId) - .orElseThrow(() -> - new UserNotFoundException(String.format("The user with ID %s does not exist.", userId)) - ); - - UserRepresentation userRepresentation = keycloakUserService.getUser(userEntity.getAuthId()); - UserDto user = userMapper.convertToDto(userEntity); - user.setEmail(userRepresentation.getEmail()); - return user; - } - // // TODO Review this function - // public UserDto updateUser(Long id, UserUpdateRequestDto userUpdateRequest) { - // UserEntity userEntity = - // userRepository.findById(id).orElseThrow(EntityNotFoundException::new); - - // if (userUpdateRequest.getStatus() == UserStatusDto.APPROVED) { - // UserRepresentation userRepresentation = - // keycloakUserService.getUser(userEntity.getAuthId()); - // userRepresentation.setEnabled(true); - // userRepresentation.setEmailVerified(true); - // keycloakUserService.updateUser(userRepresentation); - // } - - // userEntity.setStatus(userUpdateRequest.getStatus()); - // return userMapper.convertToDto(userRepository.save(userEntity)); - // } -} diff --git a/apps/openchallenges/user-service/src/main/resources/application.yml b/apps/openchallenges/user-service/src/main/resources/application.yml deleted file mode 100644 index d97e8c295d..0000000000 --- a/apps/openchallenges/user-service/src/main/resources/application.yml +++ /dev/null @@ -1,69 +0,0 @@ -spring: - application: - name: openchallenges-user-service - datasource: - # url: jdbc:mysql://openchallenges-mariadb:3306/challenge - url: ${db.url} - # url: jdbc:h2:mem:challenge - username: user_service - password: changeme - jpa: - hibernate: - ddl-auto: update - jackson: - date-format: org.sagebionetworks.challenge.RFC3339DateFormat - serialization: - WRITE_DATES_AS_TIMESTAMPS: false - -server: - port: 8083 - -eureka: - client: - service-url: - defaultZone: ${service.registry.url} - instance: - preferIpAddress: true - -management: - info: - env: - enabled: true - endpoints: - web: - exposure: - include: info,health - -info: - application: - name: ${spring.application.name} - -keycloak: - realm: test - resource: openchallenges-user-service - credentials: - secret: GFRau7cQTxhx19CUmSdrqrqrZ1pshjjB - auth-server-url: ${keycloak.url} - ssl-required: external - use-resource-role-mappings: true - bearer-only: true - -app: - config: - keycloak: - server-url: ${keycloak.url} - realm: test - # TODO: Which client ID property is used? - client-id: challenge-api-client - clientId: challenge-api-client - client-secret: mg2DrRcxHx19PIITibdOnbNEbJUKjGKb -# springdoc: -# version: openapi_3_1 -# swagger-ui: -# path: /api/v1/api-docs/ui -# # url: /api/v1/api-docs/openapi.json -# api-docs: -# enabled: true -# path: /api/v1/api-docs/openapi.json -# packagesToScan: org.sagebionetworks.challenge.controller -# pathsToMatch: /** diff --git a/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql b/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql deleted file mode 100644 index d787b45073..0000000000 --- a/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174638__create_user_table.sql +++ /dev/null @@ -1,13 +0,0 @@ --- challenge.challenge_user definition - -CREATE TABLE `challenge_user` -( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `auth_id` varchar(255) DEFAULT NULL, - `status` varchar(255) DEFAULT NULL, - `login` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -); - --- KEY `FKk9w2ogq595jbe8r2due7vv3xr` (`account_id`), --- CONSTRAINT `FKk9w2ogq595jbe8r2due7vv3xr` FOREIGN KEY (`account_id`) REFERENCES `banking_core_account` (`id`) \ No newline at end of file diff --git a/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql b/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql deleted file mode 100644 index 3a74588223..0000000000 --- a/apps/openchallenges/user-service/src/main/resources/db/migration/V1.0.20210427174721__temp_data.sql +++ /dev/null @@ -1,7 +0,0 @@ -INSERT INTO challenge_user - (id, auth_id, status, login) -VALUES ('1', 'b9302586-876b-47e8-900c-2c09f161b888', 'PENDING', 'test'), - ('2', '3739ed0d-6da2-4d65-ba5a-223eb2dfe2b6', 'PENDING', 'tschaffter'), - ('3', '9f6abcb0-25aa-49d7-aa1b-5292c8f3e26a', 'PENDING', 'rrchai'), - ('4', '04b800f3-c431-4862-93c4-2bef14fc204d', 'PENDING', 'vpchung'), - ('5', '1212f921-6ab0-444f-a5ea-9dc154199a3c', 'PENDING', 'oakenshield'); \ No newline at end of file diff --git a/apps/openchallenges/user-service/src/main/resources/openapi.yaml b/apps/openchallenges/user-service/src/main/resources/openapi.yaml deleted file mode 100644 index 15a441e1bd..0000000000 --- a/apps/openchallenges/user-service/src/main/resources/openapi.yaml +++ /dev/null @@ -1,442 +0,0 @@ -openapi: 3.0.3 -info: - contact: - name: Support - url: https://github.com/Sage-Bionetworks/sage-monorepo - license: - name: Apache 2.0 - url: https://github.com/Sage-Bionetworks/sage-monorepo - title: Challenge User API - version: 0.6.0 - x-logo: - url: https://Sage-Bionetworks.github.io/rocc-schemas/logo.png -servers: - - url: http://localhost -tags: - - description: Operations about users - name: User -paths: - /users/register: - post: - description: Create a user with the specified account name - operationId: createUser - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UserCreateRequest' - required: true - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/UserCreateResponse' - description: Account created - '400': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: Invalid request - '409': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request conflicts with current state of the target resource - '500': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request cannot be fulfilled due to an unexpected server - error - summary: Create a user - tags: - - User - x-content-type: application/json - x-accepts: application/json - x-tags: - - tag: User - /users: - get: - description: Returns the users - operationId: listUsers - parameters: - - description: The page number - explode: true - in: query - name: pageNumber - required: false - schema: - default: 0 - format: int32 - minimum: 0 - type: integer - style: form - - description: The number of items in a single page - explode: true - in: query - name: pageSize - required: false - schema: - default: 100 - format: int32 - minimum: 10 - type: integer - style: form - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/UsersPage' - description: Success - '400': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: Invalid request - '500': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request cannot be fulfilled due to an unexpected server - error - summary: Get all users - tags: - - User - x-accepts: application/json - x-tags: - - tag: User - /users/{userId}: - delete: - description: Deletes the user specified - operationId: deleteUser - parameters: - - description: "The unique identifier of the user, either the user account ID\ - \ or login" - explode: false - in: path - name: userId - required: true - schema: - $ref: '#/components/schemas/AccountId' - style: simple - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/EmptyObject' - description: Deleted - '400': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The specified resource was not found - '500': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request cannot be fulfilled due to an unexpected server - error - summary: Delete a user - tags: - - User - x-accepts: application/json - x-tags: - - tag: User - get: - description: Returns the user specified - operationId: getUser - parameters: - - description: "The unique identifier of the user, either the user account ID\ - \ or login" - explode: false - in: path - name: userId - required: true - schema: - $ref: '#/components/schemas/AccountId' - style: simple - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/User' - description: A user - '404': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The specified resource was not found - '500': - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request cannot be fulfilled due to an unexpected server - error - summary: Get a user - tags: - - User - x-accepts: application/json - x-tags: - - tag: User -components: - parameters: - pageNumber: - description: The page number - explode: true - in: query - name: pageNumber - required: false - schema: - default: 0 - format: int32 - minimum: 0 - type: integer - style: form - pageSize: - description: The number of items in a single page - explode: true - in: query - name: pageSize - required: false - schema: - default: 100 - format: int32 - minimum: 10 - type: integer - style: form - userId: - description: "The unique identifier of the user, either the user account ID\ - \ or login" - explode: false - in: path - name: userId - required: true - schema: - $ref: '#/components/schemas/AccountId' - style: simple - responses: - BadRequest: - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: Invalid request - Conflict: - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request conflicts with current state of the target resource - InternalServerError: - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The request cannot be fulfilled due to an unexpected server error - NotFound: - content: - application/problem+json: - schema: - $ref: '#/components/schemas/BasicError' - description: The specified resource was not found - schemas: - Email: - description: An email address - example: john.smith@example.com - format: email - type: string - UserCreateRequest: - description: The information required to create a user account - example: - login: awesome-user - email: awesome-user@example.org - password: yourpassword - name: Awesome User - avatarUrl: https://example.com/awesome-avatar.png - bio: A great bio - properties: - login: - type: string - email: - description: An email address - example: john.smith@example.com - format: email - type: string - password: - format: password - type: string - name: - nullable: true - type: string - avatarUrl: - example: https://example.com/awesome-avatar.png - format: url - nullable: true - type: string - bio: - nullable: true - type: string - required: - - email - - login - - password - type: object - AccountId: - description: The unique identifier of an account - example: 1 - format: int64 - type: integer - UserCreateResponse: - description: The response returned after the creation of the user - example: - id: 507f1f77bcf86cd799439011 - properties: - id: - description: The unique identifier of an account - example: 1 - format: int64 - type: integer - required: - - id - type: object - x-java-class-annotations: - - '@lombok.Builder' - BasicError: - description: Problem details (tools.ietf.org/html/rfc7807) - properties: - title: - description: A human readable documentation for the problem type - type: string - status: - description: The HTTP status code - type: integer - detail: - description: A human readable explanation specific to this occurrence of - the problem - type: string - type: - description: An absolute URI that identifies the problem type - type: string - required: - - status - - title - type: object - x-java-class-annotations: - - '@lombok.Builder' - PageMetadata: - description: The metadata of a page - properties: - paging: - $ref: '#/components/schemas/PageMetadata_paging' - totalResults: - description: Total number of results in the result set - type: integer - required: - - paging - - totalResults - type: object - UserStatus: - description: The account status of a user - enum: - - pending - - approved - - disabled - - blacklist - example: pending - type: string - User: - description: A simple user - example: - login: awesome-user - email: awesome-user@example.org - name: Awesome User - status: approved - avatarUrl: https://example.com/awesome-avatar.png - bio: A great bio - createdAt: 2017-07-08T16:18:44-04:00 - updatedAt: 2017-07-08T16:18:44-04:00 - type: User - properties: - id: - description: The unique identifier of an account - example: 1 - format: int64 - type: integer - login: - type: string - email: - description: An email address - example: john.smith@example.com - format: email - type: string - name: - nullable: true - type: string - status: - $ref: '#/components/schemas/UserStatus' - avatarUrl: - example: https://example.com/awesome-avatar.png - format: url - nullable: true - type: string - createdAt: - format: date-time - type: string - updatedAt: - format: date-time - type: string - type: - example: User - type: string - bio: - nullable: true - type: string - required: - - createdAt - - email - - login - - type - - updatedAt - type: object - UsersPage: - allOf: - - $ref: '#/components/schemas/PageMetadata' - - $ref: '#/components/schemas/UsersPage_allOf' - description: A page of users - type: object - x-java-class-annotations: - - '@lombok.Builder' - EmptyObject: - description: Empty JSON object - type: object - PageMetadata_paging: - description: Links to navigate to different pages of results - properties: - next: - description: Link to the next page of results - format: url - type: string - type: object - UsersPage_allOf: - properties: - users: - description: A list of users - items: - $ref: '#/components/schemas/User' - type: array - required: - - users - type: object diff --git a/apps/openchallenges/user-service/src/test/java/org/sagebionetworks/challenge/OpenApiGeneratorApplicationTests.java b/apps/openchallenges/user-service/src/test/java/org/sagebionetworks/challenge/OpenApiGeneratorApplicationTests.java deleted file mode 100644 index 9719f3aefb..0000000000 --- a/apps/openchallenges/user-service/src/test/java/org/sagebionetworks/challenge/OpenApiGeneratorApplicationTests.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.sagebionetworks.challenge; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class OpenApiGeneratorApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/apps/openchallenges/user-service/templates/AUTHORS.md b/apps/openchallenges/user-service/templates/AUTHORS.md deleted file mode 100644 index 01c10d9f06..0000000000 --- a/apps/openchallenges/user-service/templates/AUTHORS.md +++ /dev/null @@ -1,11 +0,0 @@ -# Authors - -Ordered by first contribution. - -- [Thomas Schaffter](https://github.com/tschaffter) - - - - - diff --git a/apps/openchallenges/user-service/templates/config.yaml b/apps/openchallenges/user-service/templates/config.yaml deleted file mode 100644 index 8ecff11504..0000000000 --- a/apps/openchallenges/user-service/templates/config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -files: - AUTHORS.md: {} diff --git a/apps/openchallenges/user-service/templates/openapi2SpringBoot.mustache b/apps/openchallenges/user-service/templates/openapi2SpringBoot.mustache deleted file mode 100644 index 01725be2d2..0000000000 --- a/apps/openchallenges/user-service/templates/openapi2SpringBoot.mustache +++ /dev/null @@ -1,27 +0,0 @@ -package {{basePackage}}; - -{{#openApiNullable}} -import com.fasterxml.jackson.databind.Module; -import org.openapitools.jackson.nullable.JsonNullableModule; -{{/openApiNullable}} -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@ComponentScan(basePackages = {"{{basePackage}}", "{{apiPackage}}" , "{{configPackage}}"}) -public class OpenApiGeneratorApplication { - - public static void main(String[] args) { - SpringApplication.run(OpenApiGeneratorApplication.class, args); - } - -{{#openApiNullable}} - @Bean - public Module jsonNullableModule() { - return new JsonNullableModule(); - } -{{/openApiNullable}} - -} \ No newline at end of file diff --git a/apps/openchallenges/user-service/templates/pojo.mustache b/apps/openchallenges/user-service/templates/pojo.mustache deleted file mode 100644 index 03b58fe88a..0000000000 --- a/apps/openchallenges/user-service/templates/pojo.mustache +++ /dev/null @@ -1,254 +0,0 @@ -/** - * {{description}}{{^description}}{{classname}}{{/description}} - */ -{{>additionalModelTypeAnnotations}} -{{#description}} -{{#swagger1AnnotationLibrary}} -@ApiModel(description = "{{{description}}}") -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} -@Schema({{#name}}name = "{{name}}", {{/name}}description = "{{{description}}}") -{{/swagger2AnnotationLibrary}} -{{/description}} -{{#discriminator}} -{{>typeInfoAnnotation}} -{{/discriminator}} -{{#jackson}} -{{#isClassnameSanitized}} -@JsonTypeName("{{name}}") -{{/isClassnameSanitized}} -{{/jackson}} -{{#withXml}} -{{>xmlAnnotation}} -{{/withXml}} -{{>generatedAnnotation}} -{{#vendorExtensions.x-class-extra-annotation}} -{{{vendorExtensions.x-class-extra-annotation}}} -{{/vendorExtensions.x-class-extra-annotation}} -{{#vendorExtensions.x-java-class-annotations}} -{{{.}}} -{{/vendorExtensions.x-java-class-annotations}} -{{^vendorExtensions.x-java-class-annotations}} -// TODO Add x-java-class-annotations -{{/vendorExtensions.x-java-class-annotations}} -public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}} extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { -{{#serializableModel}} - - private static final long serialVersionUID = 1L; -{{/serializableModel}} - {{#vars}} - - {{#isEnum}} - {{^isContainer}} -{{>enumClass}} - {{/isContainer}} - {{#isContainer}} - {{#mostInnerItems}} -{{>enumClass}} - {{/mostInnerItems}} - {{/isContainer}} - {{/isEnum}} - {{#jackson}} - @JsonProperty("{{baseName}}") - {{#withXml}} - @JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace="{{.}}", {{/xmlNamespace}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") - {{/withXml}} - {{/jackson}} - {{#gson}} - @SerializedName("{{baseName}}") - {{/gson}} - {{#vendorExtensions.x-field-extra-annotation}} - {{{vendorExtensions.x-field-extra-annotation}}} - {{/vendorExtensions.x-field-extra-annotation}} - {{#isContainer}} - {{#useBeanValidation}}@Valid{{/useBeanValidation}} - {{#openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#isNullable}}JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}; - {{/openApiNullable}} - {{/isContainer}} - {{^isContainer}} - {{#isDate}} - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) - {{/isDate}} - {{#isDateTime}} - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - {{/isDateTime}} - {{#openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; - {{/openApiNullable}} - {{/isContainer}} - {{/vars}} - {{#vars}} - - {{! begin feature: fluent setter methods }} - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { - {{#openApiNullable}} - this.{{name}} = {{#isNullable}}JsonNullable.of({{name}}){{/isNullable}}{{^isNullable}}{{name}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - this.{{name}} = {{name}}; - {{/openApiNullable}} - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - {{#openApiNullable}} - {{^required}} - if (this.{{name}} == null{{#isNullable}} || !this.{{name}}.isPresent(){{/isNullable}}) { - this.{{name}} = {{#isNullable}}JsonNullable.of({{{defaultValue}}}){{/isNullable}}{{^isNullable}}{{{defaultValue}}}{{/isNullable}}; - } - {{/required}} - this.{{name}}{{#isNullable}}.get(){{/isNullable}}.add({{name}}Item); - {{/openApiNullable}} - {{^openApiNullable}} - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}; - } - this.{{name}}.add({{name}}Item); - {{/openApiNullable}} - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - {{^required}} - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}; - } - {{/required}} - this.{{name}}.put(key, {{name}}Item); - return this; - } - {{/isMap}} - {{! end feature: fluent setter methods }} - {{! begin feature: getter and setter }} - - /** - {{#description}} - * {{{.}}} - {{/description}} - {{^description}} - * Get {{name}} - {{/description}} - {{#minimum}} - * minimum: {{.}} - {{/minimum}} - {{#maximum}} - * maximum: {{.}} - {{/maximum}} - * @return {{name}} - */ - {{#vendorExtensions.x-extra-annotation}} - {{{vendorExtensions.x-extra-annotation}}} - {{/vendorExtensions.x-extra-annotation}} - {{#useBeanValidation}} - {{>beanValidation}} - {{/useBeanValidation}} - {{#swagger2AnnotationLibrary}} - @Schema(name = "{{{baseName}}}", {{#isReadOnly}}accessMode = Schema.AccessMode.READ_ONLY, {{/isReadOnly}}{{#example}}example = "{{{.}}}", {{/example}}{{#description}}description = "{{{.}}}", {{/description}}required = {{{required}}}) - {{/swagger2AnnotationLibrary}} - {{#swagger1AnnotationLibrary}} - @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}") - {{/swagger1AnnotationLibrary}} - public {{>nullableDataType}} {{getter}}() { - return {{name}}; - } - - {{#vendorExtensions.x-setter-extra-annotation}} - {{{vendorExtensions.x-setter-extra-annotation}}} - {{/vendorExtensions.x-setter-extra-annotation}} - public void {{setter}}({{>nullableDataType}} {{name}}) { - this.{{name}} = {{name}}; - } - {{! end feature: getter and setter }} - {{/vars}} - {{#parentVars}} - - {{! begin feature: fluent setter methods for inherited properties }} - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { - super.{{setter}}({{name}}); - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - super.add{{nameInCamelCase}}Item({{name}}Item); - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - super.put{{nameInCamelCase}}Item({{name}}Item); - return this; - } - {{/isMap}} - {{! end feature: fluent setter methods for inherited properties }} - {{/parentVars}} - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - }{{#hasVars}} - {{classname}} {{classVarName}} = ({{classname}}) o; - return {{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}equalsNullable(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}} && - {{/-last}}{{/vars}}{{#parent}} && - super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} - return true;{{/hasVars}} - } - {{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); - } - {{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public int hashCode() { - return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}); - } - {{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; - } - {{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class {{classname}} {\n"); - {{#parent}} - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - {{/parent}} - {{#vars}}sb.append(" {{name}}: ").append(toIndentedString({{name}})).append("\n"); - {{/vars}}sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/templates/v6.1.x/openapi2SpringBoot.mustache b/apps/openchallenges/user-service/templates/v6.1.x/openapi2SpringBoot.mustache deleted file mode 100644 index 01725be2d2..0000000000 --- a/apps/openchallenges/user-service/templates/v6.1.x/openapi2SpringBoot.mustache +++ /dev/null @@ -1,27 +0,0 @@ -package {{basePackage}}; - -{{#openApiNullable}} -import com.fasterxml.jackson.databind.Module; -import org.openapitools.jackson.nullable.JsonNullableModule; -{{/openApiNullable}} -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@ComponentScan(basePackages = {"{{basePackage}}", "{{apiPackage}}" , "{{configPackage}}"}) -public class OpenApiGeneratorApplication { - - public static void main(String[] args) { - SpringApplication.run(OpenApiGeneratorApplication.class, args); - } - -{{#openApiNullable}} - @Bean - public Module jsonNullableModule() { - return new JsonNullableModule(); - } -{{/openApiNullable}} - -} \ No newline at end of file diff --git a/apps/openchallenges/user-service/templates/v6.1.x/pojo.mustache b/apps/openchallenges/user-service/templates/v6.1.x/pojo.mustache deleted file mode 100644 index 48e6d0faf8..0000000000 --- a/apps/openchallenges/user-service/templates/v6.1.x/pojo.mustache +++ /dev/null @@ -1,248 +0,0 @@ -/** - * {{description}}{{^description}}{{classname}}{{/description}} - */ -{{>additionalModelTypeAnnotations}} -{{#description}} -{{#swagger1AnnotationLibrary}} -@ApiModel(description = "{{{description}}}") -{{/swagger1AnnotationLibrary}} -{{#swagger2AnnotationLibrary}} -@Schema({{#name}}name = "{{name}}", {{/name}}description = "{{{description}}}") -{{/swagger2AnnotationLibrary}} -{{/description}} -{{#discriminator}} -{{>typeInfoAnnotation}} -{{/discriminator}} -{{#jackson}} -{{#isClassnameSanitized}} -@JsonTypeName("{{name}}") -{{/isClassnameSanitized}} -{{/jackson}} -{{#withXml}} -{{>xmlAnnotation}} -{{/withXml}} -{{>generatedAnnotation}} -{{#vendorExtensions.x-class-extra-annotation}} -{{{vendorExtensions.x-class-extra-annotation}}} -{{/vendorExtensions.x-class-extra-annotation}} -public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}} extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { -{{#serializableModel}} - - private static final long serialVersionUID = 1L; -{{/serializableModel}} - {{#vars}} - - {{#isEnum}} - {{^isContainer}} -{{>enumClass}} - {{/isContainer}} - {{#isContainer}} - {{#mostInnerItems}} -{{>enumClass}} - {{/mostInnerItems}} - {{/isContainer}} - {{/isEnum}} - {{#jackson}} - @JsonProperty("{{baseName}}") - {{#withXml}} - @JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace="{{.}}", {{/xmlNamespace}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") - {{/withXml}} - {{/jackson}} - {{#gson}} - @SerializedName("{{baseName}}") - {{/gson}} - {{#vendorExtensions.x-field-extra-annotation}} - {{{vendorExtensions.x-field-extra-annotation}}} - {{/vendorExtensions.x-field-extra-annotation}} - {{#isContainer}} - {{#useBeanValidation}}@Valid{{/useBeanValidation}} - {{#openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#isNullable}}JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - private {{>nullableDataType}} {{name}} = {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}; - {{/openApiNullable}} - {{/isContainer}} - {{^isContainer}} - {{#isDate}} - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) - {{/isDate}} - {{#isDateTime}} - @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - {{/isDateTime}} - {{#openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; - {{/openApiNullable}} - {{/isContainer}} - {{/vars}} - {{#vars}} - - {{! begin feature: fluent setter methods }} - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { - {{#openApiNullable}} - this.{{name}} = {{#isNullable}}JsonNullable.of({{name}}){{/isNullable}}{{^isNullable}}{{name}}{{/isNullable}}; - {{/openApiNullable}} - {{^openApiNullable}} - this.{{name}} = {{name}}; - {{/openApiNullable}} - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - {{#openApiNullable}} - {{^required}} - if (this.{{name}} == null{{#isNullable}} || !this.{{name}}.isPresent(){{/isNullable}}) { - this.{{name}} = {{#isNullable}}JsonNullable.of({{{defaultValue}}}){{/isNullable}}{{^isNullable}}{{{defaultValue}}}{{/isNullable}}; - } - {{/required}} - this.{{name}}{{#isNullable}}.get(){{/isNullable}}.add({{name}}Item); - {{/openApiNullable}} - {{^openApiNullable}} - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}; - } - this.{{name}}.add({{name}}Item); - {{/openApiNullable}} - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - {{^required}} - if (this.{{name}} == null) { - this.{{name}} = {{{defaultValue}}}; - } - {{/required}} - this.{{name}}.put(key, {{name}}Item); - return this; - } - {{/isMap}} - {{! end feature: fluent setter methods }} - {{! begin feature: getter and setter }} - - /** - {{#description}} - * {{{.}}} - {{/description}} - {{^description}} - * Get {{name}} - {{/description}} - {{#minimum}} - * minimum: {{.}} - {{/minimum}} - {{#maximum}} - * maximum: {{.}} - {{/maximum}} - * @return {{name}} - */ - {{#vendorExtensions.x-extra-annotation}} - {{{vendorExtensions.x-extra-annotation}}} - {{/vendorExtensions.x-extra-annotation}} - {{#useBeanValidation}} - {{>beanValidation}} - {{/useBeanValidation}} - {{#swagger2AnnotationLibrary}} - @Schema(name = "{{{baseName}}}", {{#isReadOnly}}accessMode = Schema.AccessMode.READ_ONLY, {{/isReadOnly}}{{#example}}example = "{{{.}}}", {{/example}}{{#description}}description = "{{{.}}}", {{/description}}required = {{{required}}}) - {{/swagger2AnnotationLibrary}} - {{#swagger1AnnotationLibrary}} - @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}") - {{/swagger1AnnotationLibrary}} - public {{>nullableDataType}} {{getter}}() { - return {{name}}; - } - - {{#vendorExtensions.x-setter-extra-annotation}} - {{{vendorExtensions.x-setter-extra-annotation}}} - {{/vendorExtensions.x-setter-extra-annotation}} - public void {{setter}}({{>nullableDataType}} {{name}}) { - this.{{name}} = {{name}}; - } - {{! end feature: getter and setter }} - {{/vars}} - {{#parentVars}} - - {{! begin feature: fluent setter methods for inherited properties }} - public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { - super.{{setter}}({{name}}); - return this; - } - {{#isArray}} - - public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { - super.add{{nameInCamelCase}}Item({{name}}Item); - return this; - } - {{/isArray}} - {{#isMap}} - - public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { - super.put{{nameInCamelCase}}Item({{name}}Item); - return this; - } - {{/isMap}} - {{! end feature: fluent setter methods for inherited properties }} - {{/parentVars}} - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - }{{#hasVars}} - {{classname}} {{classVarName}} = ({{classname}}) o; - return {{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}equalsNullable(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}} && - {{/-last}}{{/vars}}{{#parent}} && - super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} - return true;{{/hasVars}} - } - {{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static boolean equalsNullable(JsonNullable a, JsonNullable b) { - return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); - } - {{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public int hashCode() { - return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}); - } - {{#vendorExtensions.x-jackson-optional-nullable-helpers}} - - private static int hashCodeNullable(JsonNullable a) { - if (a == null) { - return 1; - } - return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; - } - {{/vendorExtensions.x-jackson-optional-nullable-helpers}} - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class {{classname}} {\n"); - {{#parent}} - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - {{/parent}} - {{#vars}}sb.append(" {{name}}: ").append(toIndentedString({{name}})).append("\n"); - {{/vars}}sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/apps/openchallenges/user-service/tools/pull-templates.sh b/apps/openchallenges/user-service/tools/pull-templates.sh deleted file mode 100755 index 85040d8688..0000000000 --- a/apps/openchallenges/user-service/tools/pull-templates.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -openapiGeneratorVersion="6.1.x" -destinationFolder="${PWD}/templates/v${openapiGeneratorVersion}" - -mkdir -p "${destinationFolder}" - -curl -O --output-dir "${destinationFolder}" \ - "https://raw.githubusercontent.com/OpenAPITools/openapi-generator/${openapiGeneratorVersion}/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-boot/openapi2SpringBoot.mustache" -curl -O --output-dir "${destinationFolder}" \ - "https://raw.githubusercontent.com/OpenAPITools/openapi-generator/${openapiGeneratorVersion}/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache" \ No newline at end of file diff --git a/apps/openchallenges/user-service/users.http b/apps/openchallenges/user-service/users.http deleted file mode 100644 index 08e10ce4e5..0000000000 --- a/apps/openchallenges/user-service/users.http +++ /dev/null @@ -1,30 +0,0 @@ -@apiGatewayHost = http://openchallenges-api-gateway:8082 -@userServiceHost = http://openchallenges-user-service:8083 - -### Create a User - -POST {{userServiceHost}}/api/v1/users/register -Content-Type: application/json - -{ - "username": "test", - "email": "test@gmail.com", - "password": "changeme" -} - -### Login - -# @name login - -POST {{apiGatewayHost}}/api/v1/auth/login -Content-Type: application/json - -{ - "username": "test", - "password": "changeme" -} - -### List all the users - -GET {{apiGatewayHost}}/api/v1/users -Authorization: Bearer {{login.response.body.$.access_token}} diff --git a/libs/openchallenges/config/src/lib/app.config.ts b/libs/openchallenges/config/src/lib/app.config.ts index cc140ca4e7..c493bb6a77 100644 --- a/libs/openchallenges/config/src/lib/app.config.ts +++ b/libs/openchallenges/config/src/lib/app.config.ts @@ -16,7 +16,6 @@ export interface AppConfig { environment: Environment; googleTagManagerId: string; isPlatformServer: boolean; - keycloakRealm: string; privacyPolicyUrl: string; ssrApiUrl: string; termsOfUseUrl: string; diff --git a/package.json b/package.json index 5692b8621b..e27199aa11 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,6 @@ "express": "~4.18.2", "glob": "11.0.0", "json5": "2.2.3", - "keycloak-angular": "12.1.0", - "keycloak-js": "19.0.3", "lodash": "4.17.21", "mariadb": "3.3.1", "mongoose": "^8.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f809f60b72..1c6f83c7b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,12 +88,6 @@ importers: json5: specifier: 2.2.3 version: 2.2.3 - keycloak-angular: - specifier: 12.1.0 - version: 12.1.0(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(@angular/router@18.2.5(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(@angular/platform-browser@18.2.5(@angular/animations@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(rxjs@7.8.1))(keycloak-js@19.0.3) - keycloak-js: - specifier: 19.0.3 - version: 19.0.3 lodash: specifier: 4.17.21 version: 4.17.21 @@ -8266,9 +8260,6 @@ packages: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} - js-sha256@0.9.0: - resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8451,17 +8442,6 @@ packages: karma-source-map-support@1.4.0: resolution: {integrity: sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==} - keycloak-angular@12.1.0: - resolution: {integrity: sha512-ykEoEC4hRMlKLNLJd3kSGr0DHYX1NYeHcNj9NEaPLKwhbz95Bf5cwzYwoqn0oo07OqB3e9r5ZzgXsiQRKwMebg==} - peerDependencies: - '@angular/common': ^14 - '@angular/core': ^14 - '@angular/router': ^14 - keycloak-js: ^18 || ^19 - - keycloak-js@19.0.3: - resolution: {integrity: sha512-mzCBxrzfl+vB551Q7MB+T9+40IHU4i0a6g1eTatzeEGrQMis5m/BqvPC3kxTsI+/LxHbB9XYQE3u9SlWKDHQCw==} - keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} @@ -12212,8 +12192,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.7.0-dev.20241003: - resolution: {integrity: sha512-0aj8jZi+P+uw10g2eQRnJmaMooatg2nDyqy0YlTM3A8QtXBulWuNU/vvPOTiLWCNgaz5wvcodjrmCNsOYFeQag==} + typescript@5.7.0-dev.20241007: + resolution: {integrity: sha512-oIOWWuGS7GI51GWoNlP9O/eKmV1P+lKbIzVzg2i8Ul6n8vWHjQISb8MkPBxDj+alhYG2sl+HxtFHHG/LCgN/hg==} engines: {node: '>=14.17'} hasBin: true @@ -21299,7 +21279,7 @@ snapshots: dependencies: semver: 7.6.3 shelljs: 0.8.5 - typescript: 5.7.0-dev.20241003 + typescript: 5.7.0-dev.20241007 duplexer@0.1.2: {} @@ -23965,8 +23945,6 @@ snapshots: js-levenshtein@1.1.6: {} - js-sha256@0.9.0: {} - js-tokens@4.0.0: {} js-tokens@9.0.0: {} @@ -24239,19 +24217,6 @@ snapshots: dependencies: source-map-support: 0.5.21 - keycloak-angular@12.1.0(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(@angular/router@18.2.5(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(@angular/platform-browser@18.2.5(@angular/animations@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(rxjs@7.8.1))(keycloak-js@19.0.3): - dependencies: - '@angular/common': 18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1) - '@angular/core': 18.2.5(rxjs@7.8.1)(zone.js@0.14.4) - '@angular/router': 18.2.5(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(@angular/platform-browser@18.2.5(@angular/animations@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(@angular/common@18.2.5(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4))(rxjs@7.8.1))(@angular/core@18.2.5(rxjs@7.8.1)(zone.js@0.14.4)))(rxjs@7.8.1) - keycloak-js: 19.0.3 - tslib: 2.6.3 - - keycloak-js@19.0.3: - dependencies: - base64-js: 1.5.1 - js-sha256: 0.9.0 - keygrip@1.1.0: dependencies: tsscmp: 1.0.6 @@ -28532,7 +28497,7 @@ snapshots: typescript@5.5.4: {} - typescript@5.7.0-dev.20241003: {} + typescript@5.7.0-dev.20241007: {} ua-parser-js@1.0.38: {} diff --git a/tools/configure-hostnames.sh b/tools/configure-hostnames.sh index 23828901de..79fdb21065 100755 --- a/tools/configure-hostnames.sh +++ b/tools/configure-hostnames.sh @@ -11,24 +11,20 @@ declare -a hostnames=( "127.0.0.1 model-ad-mongo" "127.0.0.1 openchallenges-api-gateway" "127.0.0.1 openchallenges-app" - "127.0.0.1 openchallenges-auth-service" "127.0.0.1 openchallenges-challenge-service" "127.0.0.1 openchallenges-config-server" "127.0.0.1 openchallenges-core-service" "127.0.0.1 openchallenges-elasticsearch" "127.0.0.1 openchallenges-grafana" "127.0.0.1 openchallenges-image-service" - "127.0.0.1 openchallenges-keycloak" "127.0.0.1 openchallenges-mariadb" "127.0.0.1 openchallenges-mysqld-exporter" "127.0.0.1 openchallenges-opensearch" "127.0.0.1 openchallenges-organization-service" - "127.0.0.1 openchallenges-postgres" "127.0.0.1 openchallenges-prometheus" "127.0.0.1 openchallenges-schema-registry" "127.0.0.1 openchallenges-service-registry" "127.0.0.1 openchallenges-thumbor" - "127.0.0.1 openchallenges-user-service" "127.0.0.1 openchallenges-zipkin" "127.0.0.1 schematic-api" )