From 3f5ff7d45cf25b0dd48d69befb09a81c4db2cce3 Mon Sep 17 00:00:00 2001
From: Luke Sikina
Date: Thu, 18 Jan 2024 10:43:39 -0500
Subject: [PATCH 001/222] [ALS-5508] Dependabot dep bumps
- httpd
- Jackson
- deleted some commented out stuff
---
pic-sure-auth-services/pom.xml | 59 +++-------------------------------
1 file changed, 5 insertions(+), 54 deletions(-)
diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml
index ed466699f..b57dca54c 100644
--- a/pic-sure-auth-services/pom.xml
+++ b/pic-sure-auth-services/pom.xml
@@ -11,8 +11,8 @@
pic-sure-auth-services
war
-
- 2.10.0
+ 2.12.7
+ 2.12.7.1
2.3.0
@@ -32,7 +32,7 @@
org.apache.httpcomponents
httpclient
- 4.5.6
+ 4.5.13
io.jsonwebtoken
@@ -71,7 +71,7 @@
org.hibernate
hibernate-core
- 5.3.1.Final
+ 5.3.20.Final
provided
@@ -79,16 +79,6 @@
hibernate-envers
5.2.5.Final
-
-
-
-
-
-
-
-
-
-
com.fasterxml.jackson.core
jackson-core
@@ -97,23 +87,8 @@
com.fasterxml.jackson.core
jackson-databind
- ${jackson.version}
+ ${jackson.version.fourdigit}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
org.springframework
spring-web
@@ -199,30 +174,6 @@
swagger-jaxrs2-servlet-initializer
2.0.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
${project.artifactId}
From 0da626ce38c322c327716a13ea40a74c8764ecf6 Mon Sep 17 00:00:00 2001
From: Luke Sikina
Date: Thu, 17 Aug 2023 10:15:39 -0400
Subject: [PATCH 002/222] Label checker GH action
---
.github/workflows/label-checker.yml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 .github/workflows/label-checker.yml
diff --git a/.github/workflows/label-checker.yml b/.github/workflows/label-checker.yml
new file mode 100644
index 000000000..abfc09b3c
--- /dev/null
+++ b/.github/workflows/label-checker.yml
@@ -0,0 +1,21 @@
+name: Label Checker
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - reopened
+ - labeled
+ - unlabeled
+
+jobs:
+
+ check_labels:
+ name: Check labels
+ runs-on: ubuntu-latest
+ steps:
+ - uses: docker://agilepathway/pull-request-label-checker:latest
+ with:
+ one_of: breaking-change,enhancement,bug,documentation,ignore-for-release
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+
From bac5e6d153be6fb2b48f4ebbe5e064f976ed9939 Mon Sep 17 00:00:00 2001
From: Luke Sikina
Date: Tue, 19 Dec 2023 11:21:37 -0500
Subject: [PATCH 003/222] Add deps to label checker + release config
---
.github/release.yml | 20 ++++++++++++++++++++
.github/workflows/label-checker.yml | 2 +-
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 .github/release.yml
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 000000000..0db6cd02d
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,20 @@
+changelog:
+ exclude:
+ labels:
+ - ignore-for-release
+ categories:
+ - title: Breaking Changes
+ labels:
+ - breaking-change
+ - title: Features
+ labels:
+ - feature
+ - title: Enhancements
+ labels:
+ - enhancement
+ - title: Bug Fixes
+ labels:
+ - bug
+ - title: Other Changes
+ labels:
+ - "*"
diff --git a/.github/workflows/label-checker.yml b/.github/workflows/label-checker.yml
index abfc09b3c..2bd9019bf 100644
--- a/.github/workflows/label-checker.yml
+++ b/.github/workflows/label-checker.yml
@@ -16,6 +16,6 @@ jobs:
steps:
- uses: docker://agilepathway/pull-request-label-checker:latest
with:
- one_of: breaking-change,enhancement,bug,documentation,ignore-for-release
+ one_of: breaking-change,enhancement,bug,documentation,ignore-for-release,dependencies
repo_token: ${{ secrets.GITHUB_TOKEN }}
From 4a4d1c20732caa3812d818a97861d6f92d3a7c75 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Jan 2024 17:18:48 +0000
Subject: [PATCH 004/222] Bump com.jayway.jsonpath:json-path in
/pic-sure-auth-services
Bumps [com.jayway.jsonpath:json-path](https://github.com/jayway/JsonPath) from 2.4.0 to 2.9.0.
- [Release notes](https://github.com/jayway/JsonPath/releases)
- [Changelog](https://github.com/json-path/JsonPath/blob/master/changelog.md)
- [Commits](https://github.com/jayway/JsonPath/compare/json-path-2.4.0...json-path-2.9.0)
---
updated-dependencies:
- dependency-name: com.jayway.jsonpath:json-path
dependency-type: direct:production
...
Signed-off-by: dependabot[bot]
---
pic-sure-auth-services/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml
index b57dca54c..4a753e76c 100644
--- a/pic-sure-auth-services/pom.xml
+++ b/pic-sure-auth-services/pom.xml
@@ -119,7 +119,7 @@
com.jayway.jsonpath
json-path
- 2.4.0
+ 2.9.0
org.mockito
From f58525cce34666f21c5eaa86e60b0652bb5a5fe3 Mon Sep 17 00:00:00 2001
From: Gcolon021 <34667267+Gcolon021@users.noreply.github.com>
Date: Tue, 13 Feb 2024 10:18:19 -0500
Subject: [PATCH 005/222] [ALS-5612] Create stored procedure to create a user
(#154)
* Add a new stored procedure to enable user creation
* Specific database Auth
* Update CreateUserWithRole stored procedure in auth DB
The stored procedure, CreateUserWithRole, in the 'auth' database has been updated to improve user creation. It now checks for existing users and roles, and generates a new UUID if needed. Additionally, it associates new users with roles if they exist.
* Rename CreateUserWithRole stored procedure file
* Add general metadata parameter to CreateUserWithRole procedure
---
.../V4__ADD_CREATE_USER_STORED_PROCEDURE.sql | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
diff --git a/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql b/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
new file mode 100644
index 000000000..53c550fad
--- /dev/null
+++ b/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
@@ -0,0 +1,32 @@
+USE `auth`;
+
+DROP PROCEDURE IF EXISTS CreateUserWithRole;
+DELIMITER //
+CREATE PROCEDURE CreateUserWithRole (
+ IN user_email VARCHAR(255),
+ IN connection_id VARCHAR(255),
+ IN role_name VARCHAR(255),
+ IN user_general_metadata varchar(255)
+)
+BEGIN
+ -- Attempt to retrieve the UUIDs for the user and role based on the provided information
+SELECT @userUUID := uuid FROM auth.user WHERE email = user_email AND connectionId = connection_id;
+SELECT @roleUUID := uuid FROM auth.role WHERE name = role_name;
+
+-- If the user does not exist, create a new user entry
+IF @userUUID IS NULL THEN
+ -- Generate a new UUID for the user
+ SET @userUUID = UNHEX(REPLACE(UUID(), '-', ''));
+ -- Retrieve the UUID for the connection
+SELECT @connectionUUID := uuid FROM auth.connection WHERE id = connection_id;
+-- Insert the new user record into the user table
+INSERT INTO auth.user (uuid, general_metadata, acceptedTOS, connectionId, email, matched, subject, is_active, long_term_token)
+VALUES (@userUUID, user_general_metadata, CURRENT_TIMESTAMP, @connectionUUID, user_email, 0, NULL, 1, NULL);
+END IF;
+
+ -- If the role exists, associate the user with the role
+ IF @roleUUID IS NOT NULL THEN
+ INSERT INTO auth.user_role (user_id, role_id) VALUES (@userUUID, @roleUUID);
+END IF;
+END//
+DELIMITER ;
\ No newline at end of file
From 0ed9bf07f19de71aef7bc5307d79d006709e7343 Mon Sep 17 00:00:00 2001
From: Gcolon021 <34667267+Gcolon021@users.noreply.github.com>
Date: Wed, 14 Feb 2024 15:58:11 -0500
Subject: [PATCH 006/222] [ALS-5612] Updated stored procedure (#155)
* Add connectionSubPrefix to user creation stored procedure
* Refactor connection prefix manipulation in SQL procedure
* Update user creation stored procedure
The changes made address the process of user creation in the stored procedure. A new variable, @baseUUID, has been introduced for storing UUIDs during processing. Additionally, the preparation of @connectionSubPrefix has been adjusted to concatenate LONG_TERM_TOKEN with existing values instead of overriding them.
* Add PIC-SURE User role assignment in CreateUserWithRole procedure
Improved the CreateUserWithRole stored procedure in the auth-db. All new users are now automatically assigned the 'PIC-SURE User' role in addition to specific roles designated during account creation. This ensures all users have access to the base level of functionalities.
---
.../V4__ADD_CREATE_USER_STORED_PROCEDURE.sql | 31 +++++++++++++------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql b/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
index 53c550fad..af57999c5 100644
--- a/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
+++ b/pic-sure-auth-db/db/sql/V4__ADD_CREATE_USER_STORED_PROCEDURE.sql
@@ -2,7 +2,7 @@ USE `auth`;
DROP PROCEDURE IF EXISTS CreateUserWithRole;
DELIMITER //
-CREATE PROCEDURE CreateUserWithRole (
+CREATE PROCEDURE CreateUserWithRole(
IN user_email VARCHAR(255),
IN connection_id VARCHAR(255),
IN role_name VARCHAR(255),
@@ -10,23 +10,34 @@ CREATE PROCEDURE CreateUserWithRole (
)
BEGIN
-- Attempt to retrieve the UUIDs for the user and role based on the provided information
-SELECT @userUUID := uuid FROM auth.user WHERE email = user_email AND connectionId = connection_id;
-SELECT @roleUUID := uuid FROM auth.role WHERE name = role_name;
+ SELECT @userUUID := uuid FROM auth.user WHERE email = user_email AND connectionId = connection_id;
+ SELECT @roleUUID := uuid FROM auth.role WHERE name = role_name;
+ SELECT @picsureUserRoleId := uuid FROM auth.role WHERE name = 'PIC-SURE User';
-- If the user does not exist, create a new user entry
-IF @userUUID IS NULL THEN
+ IF @userUUID IS NULL THEN
+ set @baseUUID = UUID();
-- Generate a new UUID for the user
- SET @userUUID = UNHEX(REPLACE(UUID(), '-', ''));
+ SET @userUUID = UNHEX(REPLACE(@baseUUID, '-', ''));
-- Retrieve the UUID for the connection
-SELECT @connectionUUID := uuid FROM auth.connection WHERE id = connection_id;
+ SELECT @connectionUUID := uuid FROM auth.connection WHERE id = connection_id;
+ SELECT @connectionSubPrefix := subPrefix FROM auth.connection WHERE id = connection_id;
-- Insert the new user record into the user table
-INSERT INTO auth.user (uuid, general_metadata, acceptedTOS, connectionId, email, matched, subject, is_active, long_term_token)
-VALUES (@userUUID, user_general_metadata, CURRENT_TIMESTAMP, @connectionUUID, user_email, 0, NULL, 1, NULL);
-END IF;
+ INSERT INTO auth.user (uuid, general_metadata, acceptedTOS, connectionId, email, matched, subject, is_active,
+ long_term_token)
+ VALUES (@userUUID, user_general_metadata, CURRENT_TIMESTAMP, @connectionUUID, user_email, 0,
+ concat(@connectionSubPrefix, REPLACE(@baseUUID, '-', '')), 1, NULL);
+ END IF;
-- If the role exists, associate the user with the role
IF @roleUUID IS NOT NULL THEN
INSERT INTO auth.user_role (user_id, role_id) VALUES (@userUUID, @roleUUID);
-END IF;
+ END IF;
+
+ -- If the role is not PIC-SURE User, associate the user with the PIC-SURE User role as well
+ -- All users must have the PIC-SURE User role
+ IF @roleUUID IS NOT NULL AND @roleUUID != @picsureUserRoleId THEN
+ INSERT INTO auth.user_role (user_id, role_id) VALUES (@userUUID, @picsureUserRoleId);
+ END IF;
END//
DELIMITER ;
\ No newline at end of file
From 6403f7c21f529c8518bdf55f03e720caad361e26 Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Wed, 20 Mar 2024 16:37:55 -0400
Subject: [PATCH 007/222] Start major refactor of PSAMA
- Updated target java from 11 to 21
- Updated Spring dependencies to 3 or 6 based on the library
- Started re-organizing the code base so there is an appropriate separation of concerns. The means we will have a repositories, services, controllers, configuration, filters, and more separated appropriately.
- Fixed naming conventions and started to remove javax to jakarta or spring dependencies instead.
---
pic-sure-auth-services/Dockerfile | 2 +-
pic-sure-auth-services/bak/README.md | 1 +
.../META-INF => bak}/persistence.xml | 2 +-
.../standalone.xml | 0
pic-sure-auth-services/pom.xml | 236 ++----
.../hms/dbmi/avillach/auth/Application.java | 11 +
.../avillach/auth/JAXRSConfiguration.java | 20 +-
.../auth/data/entity/package-info.java | 4 -
.../UserMetadataMappingRepository.java | 50 --
.../auth/{data => }/entity/AccessRule.java | 2 +-
.../auth/{data => }/entity/Application.java | 2 +-
.../auth/{data => }/entity/Connection.java | 2 +-
.../auth/{data => }/entity/Privilege.java | 2 +-
.../avillach/auth/{data => }/entity/Role.java | 3 +-
.../{data => }/entity/TermsOfService.java | 2 +-
.../avillach/auth/{data => }/entity/User.java | 699 +++++++++---------
.../entity/UserMetadataMapping.java | 2 +-
.../avillach/auth/entity/package-info.java | 4 +
.../exceptions/NotAuthorizedException.java | 7 +
.../dbmi/avillach/auth/filter/JWTFilter.java | 206 ++++++
.../auth/{service => model}/AccessEmail.java | 6 +-
.../auth/model/response/PICSUREResponse.java | 83 +++
.../repository/AccessRuleRepository.java | 10 +-
.../repository/ApplicationRepository.java | 13 +-
.../repository/ConnectionRepository.java | 21 +-
.../repository/PrivilegeRepository.java | 12 +-
.../{data => }/repository/RoleRepository.java | 11 +-
.../repository/TermsOfServiceRepository.java | 14 +-
.../UserMetadataMappingRepository.java | 27 +
.../{data => }/repository/UserRepository.java | 28 +-
.../{data => }/repository/package-info.java | 2 +-
...Service.java => AccessRuleController.java} | 62 +-
.../auth/rest/ApplicationController.java | 86 +++
.../auth/rest/ApplicationService.java | 187 -----
.../avillach/auth/rest/AuthController.java | 55 ++
.../dbmi/avillach/auth/rest/AuthService.java | 54 --
.../auth/rest/ConnectionWebController.java | 83 +++
.../auth/rest/ConnectionWebService.java | 102 ---
.../auth/rest/PrivilegeController.java | 77 ++
.../avillach/auth/rest/PrivilegeService.java | 107 ---
.../{RoleService.java => RoleController.java} | 26 +-
...int.java => TermsOfSerivceController.java} | 18 +-
.../avillach/auth/rest/TokenController.java | 319 ++++++++
.../dbmi/avillach/auth/rest/TokenService.java | 325 --------
.../{UserService.java => UserController.java} | 302 ++++----
... => UserMetadataMappingWebController.java} | 25 +-
.../auth/security/AuthSecurityContext.java | 98 +--
.../avillach/auth/security/JWTFilter.java | 260 -------
.../auth/security/SecurityConfig.java | 39 +
.../service/OauthUserMatchingService.java | 134 ----
.../auth/service/PrivilegeService.java | 68 ++
.../service/UserMetadataMappingService.java | 57 --
.../service/auth/AuthenticationService.java | 158 ----
.../auth/AuthenticationService.java.orig | 143 ----
.../auth/service/auth/package-info.java | 4 -
.../auth/service/impl/ApplicationService.java | 142 ++++
.../service/impl/AuthenticationService.java | 150 ++++
.../{auth => impl}/AuthorizationService.java | 38 +-
.../service/{ => impl}/BaseEntityService.java | 43 +-
.../service/impl/ConnectionWebService.java | 51 ++
.../FENCEAuthenticationService.java | 81 +-
.../auth/service/{ => impl}/MailService.java | 9 +-
.../impl/OauthUserMatchingService.java | 126 ++++
.../auth/service/{ => impl}/TOSService.java | 55 +-
.../impl/UserMetadataMappingService.java | 57 ++
.../auth/service/impl/UserService.java | 67 ++
.../auth/service/impl/package-info.java | 1 +
.../dbmi/avillach/auth/utils/AuthUtils.java | 127 ----
.../hms/dbmi/avillach/auth/utils/JWTUtil.java | 95 ++-
...itional-spring-configuration-metadata.json | 118 +++
.../src/main/resources/application.properties | 50 ++
.../src/main/webapp/META-INF/context.xml | 2 -
.../src/main/webapp/WEB-INF/beans.xml | 6 -
.../dbmi/avillach/ApplicationServiceTest.java | 12 +-
.../avillach/Auth0MatchingServiceTest.java | 38 +-
.../avillach/AuthorizationServiceTest.java | 4 +-
.../AuthorizationServiceTestByUseCases.java | 4 +-
77 files changed, 2788 insertions(+), 2761 deletions(-)
create mode 100644 pic-sure-auth-services/bak/README.md
rename pic-sure-auth-services/{src/main/resources/META-INF => bak}/persistence.xml (92%)
rename pic-sure-auth-services/{src/main/resources/wildfly-configuration => bak}/standalone.xml (100%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/Application.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/package-info.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserMetadataMappingRepository.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/AccessRule.java (99%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/Application.java (98%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/Connection.java (96%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/Privilege.java (98%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/Role.java (92%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/TermsOfService.java (93%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/User.java (94%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/entity/UserMetadataMapping.java (95%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/package-info.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/exceptions/NotAuthorizedException.java
create mode 100755 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{service => model}/AccessEmail.java (87%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/response/PICSUREResponse.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/AccessRuleRepository.java (59%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/ApplicationRepository.java (52%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/ConnectionRepository.java (65%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/PrivilegeRepository.java (53%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/RoleRepository.java (57%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/TermsOfServiceRepository.java (71%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/UserRepository.java (86%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/{data => }/repository/package-info.java (54%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/{AccessRuleService.java => AccessRuleController.java} (71%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeService.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/{RoleService.java => RoleController.java} (86%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/{TermsOfServiceEndpoint.java => TermsOfSerivceController.java} (75%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenService.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/{UserService.java => UserController.java} (67%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/{UserMetadataMappingWebService.java => UserMetadataMappingWebController.java} (77%)
delete mode 100755 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/JWTFilter.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/OauthUserMatchingService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/UserMetadataMappingService.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java.orig
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/package-info.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ApplicationService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/{auth => impl}/AuthorizationService.java (95%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/{ => impl}/BaseEntityService.java (85%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ConnectionWebService.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/{auth => impl}/FENCEAuthenticationService.java (89%)
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/{ => impl}/MailService.java (94%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/OauthUserMatchingService.java
rename pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/{ => impl}/TOSService.java (50%)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/package-info.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/AuthUtils.java
create mode 100644 pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
create mode 100644 pic-sure-auth-services/src/main/resources/application.properties
delete mode 100644 pic-sure-auth-services/src/main/webapp/META-INF/context.xml
delete mode 100644 pic-sure-auth-services/src/main/webapp/WEB-INF/beans.xml
diff --git a/pic-sure-auth-services/Dockerfile b/pic-sure-auth-services/Dockerfile
index edf2df048..41b42c7d8 100644
--- a/pic-sure-auth-services/Dockerfile
+++ b/pic-sure-auth-services/Dockerfile
@@ -30,7 +30,7 @@ RUN wildfly/bin/jboss-cli.sh --command="module add --name=com.sql.mysql \
--resources=/modules/mysql-connector-java-5.1.38.jar --dependencies=javax.api"
# Copy standalone.xml
-COPY src/main/resources/wildfly-configuration/standalone.xml wildfly/standalone/configuration/
+COPY bak/standalone.xml wildfly/standalone/configuration/
# Copy war file
COPY target/pic-sure-auth-services.war wildfly/standalone/deployments/pic-sure-auth-services.war
diff --git a/pic-sure-auth-services/bak/README.md b/pic-sure-auth-services/bak/README.md
new file mode 100644
index 000000000..0b4e9d4ce
--- /dev/null
+++ b/pic-sure-auth-services/bak/README.md
@@ -0,0 +1 @@
+# This is a temporary location to keep files that I intend to delete, but want to keep around for a while just in case.
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/resources/META-INF/persistence.xml b/pic-sure-auth-services/bak/persistence.xml
similarity index 92%
rename from pic-sure-auth-services/src/main/resources/META-INF/persistence.xml
rename to pic-sure-auth-services/bak/persistence.xml
index 4955d4789..bee8e1af6 100644
--- a/pic-sure-auth-services/src/main/resources/META-INF/persistence.xml
+++ b/pic-sure-auth-services/bak/persistence.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
java:jboss/datasources/AuthDS
- edu.harvard.hms.dbmi.avillach.auth.data.entity.User
+ edu.harvard.hms.dbmi.avillach.auth.entity.User
diff --git a/pic-sure-auth-services/src/main/resources/wildfly-configuration/standalone.xml b/pic-sure-auth-services/bak/standalone.xml
similarity index 100%
rename from pic-sure-auth-services/src/main/resources/wildfly-configuration/standalone.xml
rename to pic-sure-auth-services/bak/standalone.xml
diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml
index 4a753e76c..8d5f6a954 100644
--- a/pic-sure-auth-services/pom.xml
+++ b/pic-sure-auth-services/pom.xml
@@ -1,6 +1,6 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
pic-sure-auth-microapp
@@ -12,88 +12,99 @@
war
2.12.7
- 2.12.7.1
2.3.0
-
-
- edu.harvard.hms.dbmi.avillach
- pic-sure-util
- 2.1.0-SNAPSHOT
-
-
+
edu.harvard.hms.dbmi.avillach
pic-sure-api-data
2.1.0-SNAPSHOT
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.slf4j
+ slf4j-jdk14
+
+
+
+
- org.apache.httpcomponents
- httpclient
- 4.5.13
+ org.springframework.boot
+ spring-boot
+ 3.2.3
+
- io.jsonwebtoken
- jjwt
- 0.9.0
-
-
- javax
- javaee-api
- 8.0
- provided
-
-
- org.hibernate.javax.persistence
- hibernate-jpa-2.1-api
- 1.0.2.Final
- provided
+ org.springframework
+ spring-web
+ 6.1.5
+
- org.hibernate
- hibernate-annotations
- 3.5.6-Final
- provided
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.2.3
- org.hibernate.javax.persistence
- hibernate-jpa-2.0-api
+ org.springframework.boot
+ spring-boot-starter-logging
+
- com.auth0
- auth0
- 1.8.0
+ org.springframework.boot
+ spring-boot-autoconfigure
+ 3.2.3
- org.hibernate
- hibernate-core
- 5.3.20.Final
- provided
+ org.springframework.boot
+ spring-boot-starter-security
+ 3.2.3
+
+
+
- org.hibernate
- hibernate-envers
- 5.2.5.Final
+ com.mysql
+ mysql-connector-j
+ 8.3.0
- com.fasterxml.jackson.core
- jackson-core
- ${jackson.version}
+ io.jsonwebtoken
+ jjwt
+ 0.9.0
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version.fourdigit}
+ com.auth0
+ auth0
+ 1.8.0
- org.springframework
- spring-web
- 4.1.9.RELEASE
+ org.hibernate
+ hibernate-core
+ 5.3.20.Final
+ provided
+
junit
junit
@@ -101,32 +112,21 @@
test
-
- javax.xml.bind
- jaxb-api
- ${xml.bind.version}
-
-
- com.sun.xml.bind
- jaxb-core
- ${xml.bind.version}
-
-
- com.sun.xml.bind
- jaxb-impl
- ${xml.bind.version}
-
+
com.jayway.jsonpath
json-path
2.9.0
-
- org.mockito
- mockito-core
- 2.23.4
- test
-
+
+
+ org.mockito
+ mockito-core
+ 2.23.4
+ test
+
+
+
com.github.spullara.mustache.java
compiler
@@ -174,6 +174,11 @@
swagger-jaxrs2-servlet-initializer
2.0.0
+
+ edu.harvard.hms.dbmi.avillach.picsure.auth.microapp
+ pic-sure-auth-microapp
+ 1.0-SNAPSHOT
+
${project.artifactId}
@@ -182,9 +187,9 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.0
+ 3.11.0
- 11
+ 21
@@ -195,34 +200,6 @@
false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -271,59 +248,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/Application.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/Application.java
new file mode 100644
index 000000000..bd6737731
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/Application.java
@@ -0,0 +1,11 @@
+package edu.harvard.hms.dbmi.avillach.auth;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class);
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/JAXRSConfiguration.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/JAXRSConfiguration.java
index a89c0e3fd..a4845dd95 100755
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/JAXRSConfiguration.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/JAXRSConfiguration.java
@@ -2,16 +2,14 @@
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.MapType;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
import edu.harvard.hms.dbmi.avillach.auth.rest.TokenService;
import io.swagger.jaxrs.config.BeanConfig;
-import org.apache.commons.io.IOUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
@@ -28,13 +26,7 @@
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.SecurityContext;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.util.*;
-import javax.net.ssl.*;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/package-info.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/package-info.java
deleted file mode 100644
index 718deb3b9..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains the entity objects for PSAMA.
- */
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserMetadataMappingRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserMetadataMappingRepository.java
deleted file mode 100644
index 0b939d5db..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserMetadataMappingRepository.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
-
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.UserMetadataMapping;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Join;
-import javax.persistence.criteria.Root;
-import javax.transaction.Transactional;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Provides operations for the UserMetadataMapping entity to interact with a database.
- * @see UserMetadataMapping
- */
-@Transactional
-@ApplicationScoped
-public class UserMetadataMappingRepository extends BaseRepository {
-
- @Inject
- UserMetadataMappingRepository userMetadataMappingRepository;
-
- private Logger logger = LoggerFactory.getLogger(UserMetadataMappingRepository.class);
-
- protected UserMetadataMappingRepository() {
- super(UserMetadataMapping.class);
- }
-
- public List findByConnection(Connection connection) {
- return userMetadataMappingRepository.getByColumn("connection", connection);
-
-// CriteriaBuilder cb = cb();
-// CriteriaQuery query = cb.createQuery(UserMetadataMapping.class);
-// Root queryRoot = query.from(UserMetadataMapping.class);
-// query.select(queryRoot);
-// Join connectionJoin = queryRoot.join("connection");
-// return em.createQuery(query
-// .where(
-// cb.equal(connectionJoin.get("uuid"), connectionId)))
-// .getResultList();
- }
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/AccessRule.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
similarity index 99%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/AccessRule.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
index ac3e4098c..bbb6a98fa 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/AccessRule.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Application.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
similarity index 98%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Application.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
index 615291691..36981cfd3 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Application.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Connection.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
similarity index 96%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Connection.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
index e069dfa85..4c7e92a50 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Connection.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Privilege.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
similarity index 98%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Privilege.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
index c5ce1f65d..7076d1b9d 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Privilege.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Role.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
similarity index 92%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Role.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
index 54320b61b..529e74657 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/Role.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
@@ -1,6 +1,5 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/TermsOfService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
similarity index 93%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/TermsOfService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
index 5ff8d2be1..f7615b68f 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/TermsOfService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/User.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
similarity index 94%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/User.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
index 87a750595..66765f564 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/User.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
@@ -1,349 +1,350 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
-import org.hibernate.annotations.Type;
-
-import javax.persistence.*;
-import java.io.Serializable;
-import java.security.Principal;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Defines a model of User behavior.
- */
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
-@Entity(name = "user")
-public class User extends BaseEntity implements Serializable, Principal {
-
- @Column(unique = true)
- private String subject;
-
- @ManyToMany(fetch = FetchType.EAGER)
- @JoinTable(name = "user_role",
- joinColumns = {@JoinColumn(name = "user_id", nullable = false, updatable = false)},
- inverseJoinColumns = {@JoinColumn(name = "role_id", nullable = false, updatable = false)})
- private Set roles;
-
- private String email;
-
-
- /**
- * NOTICE
- *
- * When you update or create a user,
- * please use connection.id as the input. The UserService is specifically using connection.id.
- *
- *
- *
- * Note: This is because of the checkAssociation() method in UserService.
- *
- * @see edu.harvard.hms.dbmi.avillach.auth.rest.UserService
- */
- @ManyToOne
- @JoinColumn(name = "connectionId")
- private Connection connection;
-
- private boolean matched;
-
- private Date acceptedTOS;
-
- @Column(name = "auth0_metadata")
- @Type(type = "text")
- private String auth0metadata;
-
- @Column(name = "general_metadata")
- @Type(type = "text")
- private String generalMetadata;
-
- @Column(name = "is_active")
- private boolean active = true;
-
- @Column(name = "long_term_token")
- private String token;
-
- public String getSubject() {
- return subject;
- }
-
- public User setSubject(String subject) {
- this.subject = subject;
- return this;
- }
-
- public Set getRoles() {
- return roles;
- }
-
- public User setRoles(Set roles) {
- this.roles = roles;
- return this;
- }
-
- /**
- * return all privileges in the roles as a set
- * @return
- */
- @JsonIgnore
- public Set getTotalPrivilege(){
- if (roles == null)
- return null;
-
- Set privileges = new HashSet<>();
- roles.stream().forEach(r -> privileges.addAll(r.getPrivileges()));
- return privileges;
- }
-
- /**
- * return all privileges in the roles as a set
- * @return
- */
- @JsonIgnore
- public Set getTotalAccessRule(){
- if (roles == null)
- return null;
-
- Set accessRules = new HashSet<>();
- roles.stream().
- forEach(r -> r.getPrivileges().stream().
- forEach(p -> accessRules.addAll(p.getAccessRules())));
- return accessRules;
- }
-
- /**
- * return all privilege name in each role as a set.
- *
- * @return
- */
- @JsonIgnore
- public Set getPrivilegeNameSet(){
- Set totalPrivilegeSet = getTotalPrivilege();
-
- if (totalPrivilegeSet == null)
- return null;
-
- Set nameSet = new HashSet<>();
- totalPrivilegeSet.stream().forEach(p -> nameSet.add(p.getName()));
- return nameSet;
- }
-
- /**
- * return privilege names in each role as a set based on Application given.
- *
- * @return
- */
- @JsonIgnore
- public Set getPrivilegeNameSetByApplication(Application application){
- Set totalPrivilegeSet = getTotalPrivilege();
-
- if (totalPrivilegeSet == null)
- return null;
-
- Set nameSet = new HashSet<>();
- if (application == null)
- return nameSet;
-
- for (Privilege appPrivilege : application.getPrivileges()) {
- for (Privilege userPrivilege : totalPrivilegeSet) {
- if (appPrivilege.equals(userPrivilege))
- nameSet.add(userPrivilege.getName());
- }
- }
- return nameSet;
- }
-
- /**
- * return privileges in each role as a set based on Application given.
- *
- * @return
- */
- @JsonIgnore
- public Set getPrivilegesByApplication(Application application){
- if (application == null || application.getUuid() == null){
- return getTotalPrivilege();
- }
-
- if (roles == null)
- return null;
-
- Set privileges = new HashSet<>();
- roles.stream().
- forEach(r -> privileges.addAll(r.getPrivileges()
- .stream()
- .filter(p -> application.getUuid()
- .equals((p.getApplication()==null)?
- null:
- p.getApplication().getUuid()))
- .collect(Collectors.toSet())));
- return privileges;
- }
-
- @JsonIgnore
- public String getPrivilegeString(){
- Set totalPrivilegeSet = getTotalPrivilege();
-
- if (totalPrivilegeSet == null)
- return null;
-
- return totalPrivilegeSet.stream().map(p -> p.getName()).collect(Collectors.joining(","));
- }
-
- @JsonIgnore
- public String getRoleString(){
- return (roles==null)?null:roles.stream().map(r -> r.name)
- .collect(Collectors.joining(","));
- }
-
- public String getAuth0metadata() {
- return auth0metadata;
- }
-
- public User setAuth0metadata(String auth0metadata) {
- this.auth0metadata = auth0metadata;
- return this;
- }
-
- public String getGeneralMetadata() {
- return generalMetadata;
- }
-
- public User setGeneralMetadata(String generalMetadata) {
- this.generalMetadata = generalMetadata;
- return this;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public Connection getConnection() {
- return connection;
- }
-
- public User setConnection(Connection connection) {
- this.connection = connection;
- return this;
- }
-
- public boolean isMatched() {
- return matched;
- }
-
- public void setMatched(boolean matched) {
- this.matched = matched;
- }
-
- public Date getAcceptedTOS() {
- return acceptedTOS;
- }
-
- public void setAcceptedTOS(Date acceptedTOS) {
- this.acceptedTOS = acceptedTOS;
- }
-
- @JsonIgnore
- @Override
- public String getName() {
- return this.subject;
- }
-
- public boolean isActive() {
- return active;
- }
-
- public void setActive(boolean active) {
- this.active = active;
- }
-
- public String getToken() {
- return token;
- }
-
- public void setToken(String token) {
- this.token = token;
- }
-
- /**
- * Inner class defining limited user attributes returned from the User endpoint.
- */
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- public static class UserForDisplay {
- String uuid;
- String email;
- Set privileges;
- String token;
- Set queryScopes;
- private boolean acceptedTOS;
-
- public UserForDisplay() {
- }
-
- public String getEmail() {
- return email;
- }
-
- public UserForDisplay setEmail(String email) {
- this.email = email;
- return this;
- }
-
- public Set getPrivileges() {
- return privileges;
- }
-
- public UserForDisplay setPrivileges(Set privileges) {
- this.privileges = privileges;
- return this;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public UserForDisplay setUuid(String uuid) {
- this.uuid = uuid;
- return this;
- }
-
- public String getToken() {
- return token;
- }
-
- public UserForDisplay setToken(String token) {
- this.token = token;
- return this;
- }
-
- public Set getQueryScopes() {
- return queryScopes;
- }
-
- public void setQueryScopes(Set queryScopes) {
- this.queryScopes = queryScopes;
- }
-
- public boolean getAcceptedTOS() {
- return acceptedTOS;
- }
-
- public UserForDisplay setAcceptedTOS(boolean acceptedTOS) {
- this.acceptedTOS = acceptedTOS;
- return this;
- }
- }
-
- public String toString() {
- if(uuid == null) {
- return "No UUID assigned___ " + subject + " ___ " + email + " ___ " + generalMetadata + " ___ " + auth0metadata + " ___ {" + ((connection==null)?null:connection.toString()) + "}";
- }
- return uuid.toString() + " ___ " + subject + " ___ " + email + " ___ " + generalMetadata + " ___ " + auth0metadata + " ___ {" + ((connection==null)?null:connection.toString()) + "}";
- }
-}
+package edu.harvard.hms.dbmi.avillach.auth.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
+import edu.harvard.hms.dbmi.avillach.auth.rest.UserController;
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Defines a model of User behavior.
+ */
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+@Entity(name = "user")
+public class User extends BaseEntity implements Serializable, Principal {
+
+ @Column(unique = true)
+ private String subject;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "user_role",
+ joinColumns = {@JoinColumn(name = "user_id", nullable = false, updatable = false)},
+ inverseJoinColumns = {@JoinColumn(name = "role_id", nullable = false, updatable = false)})
+ private Set roles;
+
+ private String email;
+
+
+ /**
+ * NOTICE
+ *
+ * When you update or create a user,
+ * please use connection.id as the input. The UserService is specifically using connection.id.
+ *
+ *
+ *
+ * Note: This is because of the checkAssociation() method in UserService.
+ *
+ * @see UserController
+ */
+ @ManyToOne
+ @JoinColumn(name = "connectionId")
+ private Connection connection;
+
+ private boolean matched;
+
+ private Date acceptedTOS;
+
+ @Column(name = "auth0_metadata")
+ @Type(type = "text")
+ private String auth0metadata;
+
+ @Column(name = "general_metadata")
+ @Type(type = "text")
+ private String generalMetadata;
+
+ @Column(name = "is_active")
+ private boolean active = true;
+
+ @Column(name = "long_term_token")
+ private String token;
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public User setSubject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+
+ public Set getRoles() {
+ return roles;
+ }
+
+ public User setRoles(Set roles) {
+ this.roles = roles;
+ return this;
+ }
+
+ /**
+ * return all privileges in the roles as a set
+ * @return
+ */
+ @JsonIgnore
+ public Set getTotalPrivilege(){
+ if (roles == null)
+ return null;
+
+ Set privileges = new HashSet<>();
+ roles.stream().forEach(r -> privileges.addAll(r.getPrivileges()));
+ return privileges;
+ }
+
+ /**
+ * return all privileges in the roles as a set
+ * @return
+ */
+ @JsonIgnore
+ public Set getTotalAccessRule(){
+ if (roles == null)
+ return null;
+
+ Set accessRules = new HashSet<>();
+ roles.stream().
+ forEach(r -> r.getPrivileges().stream().
+ forEach(p -> accessRules.addAll(p.getAccessRules())));
+ return accessRules;
+ }
+
+ /**
+ * return all privilege name in each role as a set.
+ *
+ * @return
+ */
+ @JsonIgnore
+ public Set getPrivilegeNameSet(){
+ Set totalPrivilegeSet = getTotalPrivilege();
+
+ if (totalPrivilegeSet == null)
+ return null;
+
+ Set nameSet = new HashSet<>();
+ totalPrivilegeSet.stream().forEach(p -> nameSet.add(p.getName()));
+ return nameSet;
+ }
+
+ /**
+ * return privilege names in each role as a set based on Application given.
+ *
+ * @return
+ */
+ @JsonIgnore
+ public Set getPrivilegeNameSetByApplication(Application application){
+ Set totalPrivilegeSet = getTotalPrivilege();
+
+ if (totalPrivilegeSet == null)
+ return null;
+
+ Set nameSet = new HashSet<>();
+ if (application == null)
+ return nameSet;
+
+ for (Privilege appPrivilege : application.getPrivileges()) {
+ for (Privilege userPrivilege : totalPrivilegeSet) {
+ if (appPrivilege.equals(userPrivilege))
+ nameSet.add(userPrivilege.getName());
+ }
+ }
+ return nameSet;
+ }
+
+ /**
+ * return privileges in each role as a set based on Application given.
+ *
+ * @return
+ */
+ @JsonIgnore
+ public Set getPrivilegesByApplication(Application application){
+ if (application == null || application.getUuid() == null){
+ return getTotalPrivilege();
+ }
+
+ if (roles == null)
+ return null;
+
+ Set privileges = new HashSet<>();
+ roles.stream().
+ forEach(r -> privileges.addAll(r.getPrivileges()
+ .stream()
+ .filter(p -> application.getUuid()
+ .equals((p.getApplication()==null)?
+ null:
+ p.getApplication().getUuid()))
+ .collect(Collectors.toSet())));
+ return privileges;
+ }
+
+ @JsonIgnore
+ public String getPrivilegeString(){
+ Set totalPrivilegeSet = getTotalPrivilege();
+
+ if (totalPrivilegeSet == null)
+ return null;
+
+ return totalPrivilegeSet.stream().map(p -> p.getName()).collect(Collectors.joining(","));
+ }
+
+ @JsonIgnore
+ public String getRoleString(){
+ return (roles==null)?null:roles.stream().map(r -> r.name)
+ .collect(Collectors.joining(","));
+ }
+
+ public String getAuth0metadata() {
+ return auth0metadata;
+ }
+
+ public User setAuth0metadata(String auth0metadata) {
+ this.auth0metadata = auth0metadata;
+ return this;
+ }
+
+ public String getGeneralMetadata() {
+ return generalMetadata;
+ }
+
+ public User setGeneralMetadata(String generalMetadata) {
+ this.generalMetadata = generalMetadata;
+ return this;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public User setConnection(Connection connection) {
+ this.connection = connection;
+ return this;
+ }
+
+ public boolean isMatched() {
+ return matched;
+ }
+
+ public void setMatched(boolean matched) {
+ this.matched = matched;
+ }
+
+ public Date getAcceptedTOS() {
+ return acceptedTOS;
+ }
+
+ public void setAcceptedTOS(Date acceptedTOS) {
+ this.acceptedTOS = acceptedTOS;
+ }
+
+ @JsonIgnore
+ @Override
+ public String getName() {
+ return this.subject;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ /**
+ * Inner class defining limited user attributes returned from the User endpoint.
+ */
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ public static class UserForDisplay {
+ String uuid;
+ String email;
+ Set privileges;
+ String token;
+ Set queryScopes;
+ private boolean acceptedTOS;
+
+ public UserForDisplay() {
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public UserForDisplay setEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Set getPrivileges() {
+ return privileges;
+ }
+
+ public UserForDisplay setPrivileges(Set privileges) {
+ this.privileges = privileges;
+ return this;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public UserForDisplay setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public UserForDisplay setToken(String token) {
+ this.token = token;
+ return this;
+ }
+
+ public Set getQueryScopes() {
+ return queryScopes;
+ }
+
+ public void setQueryScopes(Set queryScopes) {
+ this.queryScopes = queryScopes;
+ }
+
+ public boolean getAcceptedTOS() {
+ return acceptedTOS;
+ }
+
+ public UserForDisplay setAcceptedTOS(boolean acceptedTOS) {
+ this.acceptedTOS = acceptedTOS;
+ return this;
+ }
+ }
+
+ public String toString() {
+ if(uuid == null) {
+ return "No UUID assigned___ " + subject + " ___ " + email + " ___ " + generalMetadata + " ___ " + auth0metadata + " ___ {" + ((connection==null)?null:connection.toString()) + "}";
+ }
+ return uuid.toString() + " ___ " + subject + " ___ " + email + " ___ " + generalMetadata + " ___ " + auth0metadata + " ___ {" + ((connection==null)?null:connection.toString()) + "}";
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/UserMetadataMapping.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
similarity index 95%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/UserMetadataMapping.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
index c7a5a170f..5e44ffdfb 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/entity/UserMetadataMapping.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.entity;
+package edu.harvard.hms.dbmi.avillach.auth.entity;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/package-info.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/package-info.java
new file mode 100644
index 000000000..a3ef54e20
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the entity objects for PSAMA.
+ */
+package edu.harvard.hms.dbmi.avillach.auth.entity;
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/exceptions/NotAuthorizedException.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/exceptions/NotAuthorizedException.java
new file mode 100644
index 000000000..050b6de05
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/exceptions/NotAuthorizedException.java
@@ -0,0 +1,7 @@
+package edu.harvard.hms.dbmi.avillach.auth.exceptions;
+
+public class NotAuthorizedException extends RuntimeException {
+ public NotAuthorizedException(String message) {
+ super(message);
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
new file mode 100755
index 000000000..00eef0b01
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
@@ -0,0 +1,206 @@
+package edu.harvard.hms.dbmi.avillach.auth.filter;
+
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ApplicationRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
+import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil.parseToken;
+
+/**
+ * The main gate for PSAMA that filters all incoming requests against PSAMA.
+ * Design Logic
+ *
+ * - All incoming requests pass through this filter.
+ * - To pass this filter, the incoming request needs a valid bearer token in its HTTP Authorization Header
+ * to represent a valid identity behind the token.
+ * - In some cases, the incoming request doesn't need to hold a token. For example, when the request is to the
authentication
+ * endpoint, swagger.json
, or swagger.html
.
+ *
+ */
+
+@Component
+public class JWTFilter extends OncePerRequestFilter {
+
+ private final static Logger logger = LoggerFactory.getLogger(JWTFilter.class);
+
+ private final UserRepository userRepo;
+
+ private final ApplicationRepository applicationRepo;
+
+ private final TOSService tosService;
+
+ @Value("${application.user.id.claim}")
+ private String USER_CLAIM_ID;
+
+ @Autowired
+ public JWTFilter(UserRepository userRepo, ApplicationRepository applicationRepo, TOSService tosService) {
+ this.userRepo = userRepo;
+ this.applicationRepo = applicationRepo;
+ this.tosService = tosService;
+ }
+
+ /**
+ * Filter implementation that performs authentication and authorization checks based on the provided request headers.
+ * The filter checks for the presence of the "Authorization" header and validates the token.
+ * It sets the appropriate security context based on the type of token (long term token or PSAMA application token) and
+ * performs the necessary checks to ensure that the user or application is authorized to access the requested resource.
+ * This filter is called by the configured security filter chain in the SecurityConfig class.
+ *
+ * @param request the HttpServletRequest object
+ * @param response the HttpServletResponse object
+ * @param filterChain the FilterChain object
+ * @throws IOException if an I/O error occurs during the execution of the filter
+ */
+ @Override
+ // Ends that are allowed are handled by the configured security filter chain in the SecurityConfig class
+ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws IOException {
+ // Get headers from the request
+ String authorizationHeader = request.getHeader("Authorization");
+
+ if (!StringUtils.isNotBlank(authorizationHeader)) {
+ // If the header is not present, then the request is not authorized
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "No authorization header found.");
+ } else {
+ // If the header is present, we need to check the token
+ String token = authorizationHeader.substring(6).trim();
+ logger.debug(" token: {}", token);
+
+ // Parse the token
+ Jws jws = parseToken(token); // TODO: We shouldn't be implementing a method that should be in the JWTUtils class
+ String userId = jws.getBody().get(this.USER_CLAIM_ID, String.class); // TODO: Update when we remove the JAXRSConfiguration class
+
+ if (userId.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
+ // For profile information, we do indeed allow long term token
+ // to be a valid token.
+ if (request.getRequestURI().startsWith("/user/me")) {
+ // Get the subject claim, remove the LONG_TERM_TOKEN_PREFIX, and use that String value to
+ // look up the existing user.
+ String realClaimsSubject = jws.getBody().getSubject().substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
+
+ setSecurityContextForUser(request, response, realClaimsSubject);
+ } else {
+ logger.error("the long term token with subject, {}, cannot access to PSAMA.", userId);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Long term tokens cannot be used to access to PSAMA.");
+ }
+
+ }
+
+ if (authorizationHeader.startsWith(AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX)) {
+ logger.info("User Authentication Starts with {}", AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX);
+
+ // Check if user is attempting to access the correct introspect endpoint. If not reject the request
+ // log an error indicating the user's token may be being used by a malicious actor.
+ if (!request.getRequestURI().endsWith("token/inspect")) {
+ logger.error(userId + " attempted to perform request " + request.getRequestURI() + " token may be compromised.");
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User is deactivated");
+ }
+
+ // Authenticate as Application
+ Application authenticatedApplication = applicationRepo.getById(UUID.fromString(userId.split("\\|")[1]));
+ if (authenticatedApplication == null) {
+ logger.error("Cannot find an application by userId: " + userId);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your token doesn't contain valid identical information, please contact admin.");
+ return;
+ }
+
+ if (!authenticatedApplication.getToken().equals(token)) {
+ logger.error("filter() incoming application token - " + token +
+ " - is not the same as record, might because the token has been refreshed. Subject: " + userId);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your token has been inactivated, please contact admin to grab you the latest one.");
+ }
+
+ // This is the application token that is being used to authenticate the user by other applications
+ // Set the security context for the application
+ setSecurityContextForApplication(request, authenticatedApplication);
+ } else {
+ logger.debug("UserID: {} is not a long term token and not a PSAMA application token.", userId);
+ // Authenticate as User
+ setSecurityContextForUser(request, response, jws.getBody().getSubject());
+ }
+ }
+
+ }
+
+ private void setSecurityContextForApplication(HttpServletRequest request, Application authenticatedApplication) {
+ logger.info("Setting security context for application: {}", authenticatedApplication.getName());
+ request.setAttribute("authenticatedApplication", authenticatedApplication);
+ }
+
+ // TODO: Implement the ApplicationException thrown in this method
+
+ /**
+ * Sets the security context for the given user.
+ * This method is responsible for validating the user claims, checking if the user is active,
+ * ensuring that the user has accepted the terms of service (if enabled), validating user roles and privileges,
+ * and setting the user object as an attribute in the request.
+ *
+ * @param request the HttpServletRequest object
+ * @param response the HttpServletResponse object
+ * @param realClaimsSubject the subject of the user's claims in the JWT token
+ */
+ private void setSecurityContextForUser(HttpServletRequest request, HttpServletResponse response, String realClaimsSubject) {
+ logger.info("Setting security context for user: {}", realClaimsSubject);
+
+ User authenticatedUser = userRepo.findBySubject(realClaimsSubject);
+
+ if (authenticatedUser == null) {
+ logger.error("Cannot validate user claims, based on information stored in the JWT token.");
+ throw new IllegalArgumentException("Cannot validate user claims, based on information stored in the JWT token.");
+ }
+
+ if (!authenticatedUser.isActive()) {
+ logger.warn("User with ID: " + authenticatedUser.getUuid() + " is deactivated.");
+ throw new NotAuthorizedException("User is deactivated");
+ }
+
+ if (JAXRSConfiguration.tosEnabled.startsWith("true") && tosService.getLatest() != null && !tosService.hasUserAcceptedLatest(authenticatedUser.getSubject())) {
+ //If user has not accepted terms of service and is attempted to get information other than the terms of service, don't authenticate
+ try {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "User must accept terms of service");
+ } catch (IOException e) {
+ logger.error("Failed to send response.", e);
+ }
+ }
+
+ // Get the user's roles
+ Set userRoles = authenticatedUser.getRoles();
+
+ // Check if the user has any roles and privileges associated with them
+ if (userRoles == null || userRoles.isEmpty() || userRoles.stream().noneMatch(role -> role.getPrivileges() != null && !role.getPrivileges().isEmpty())) {
+ logger.error("User doesn't have any roles or privileges.");
+ try {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User doesn't have any roles or privileges.");
+ } catch (IOException e) {
+ logger.error("Failed to send response.", e);
+ }
+ }
+
+ // TODO: Spring is generally expecting ROLE_ prefix for roles. We may need to add this prefix to all the user roles.
+ // We don't want to add this to the database, because it may break backward compatibility for the UI.
+ request.setAttribute("authenticatedUser", authenticatedUser);
+ }
+
+}
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AccessEmail.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
similarity index 87%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AccessEmail.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
index 082e27504..90b19ebd9 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AccessEmail.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
@@ -1,8 +1,8 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
+package edu.harvard.hms.dbmi.avillach.auth.model;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import java.util.Set;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/response/PICSUREResponse.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/response/PICSUREResponse.java
new file mode 100644
index 000000000..d5244b2fc
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/response/PICSUREResponse.java
@@ -0,0 +1,83 @@
+package edu.harvard.hms.dbmi.avillach.auth.model.response;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import java.util.HashMap;
+import java.util.Map;
+
+public class PICSUREResponse {
+
+ private static final MediaType DEFAULT_MEDIA_TYPE = MediaType.APPLICATION_JSON;
+ private static final HttpStatus DEFAULT_RESPONSE_ERROR_CODE = HttpStatus.INTERNAL_SERVER_ERROR;
+
+ public static ResponseEntity> success() {
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ public static ResponseEntity> success(Object content) {
+ return new ResponseEntity<>(content, HttpStatus.OK);
+ }
+
+ public static ResponseEntity> success(String message, Object content) {
+ Map response = new HashMap<>();
+ response.put("message", message);
+ response.put("content", content);
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
+
+ public static ResponseEntity> error(Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, content);
+ }
+
+ public static ResponseEntity> error(String message, Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, message, content);
+ }
+
+ public static ResponseEntity> error(HttpStatus status, Object content) {
+ if (status == null) {
+ status = DEFAULT_RESPONSE_ERROR_CODE;
+ }
+ return new ResponseEntity<>(content, status);
+ }
+
+ public static ResponseEntity> error(HttpStatus status, String message, Object content) {
+ Map response = new HashMap<>();
+ response.put("message", message);
+ response.put("content", content);
+ return new ResponseEntity<>(response, status);
+ }
+
+ public static ResponseEntity> applicationError(Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, "Application error", content);
+ }
+
+ public static ResponseEntity> applicationError(String message, Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, message, content);
+ }
+
+ public static ResponseEntity> riError(Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, "RI error", content);
+ }
+
+ public static ResponseEntity> riError(String message, Object content) {
+ return error(DEFAULT_RESPONSE_ERROR_CODE, message, content);
+ }
+
+ public static ResponseEntity> protocolError(Object content) {
+ return error(HttpStatus.BAD_REQUEST, content);
+ }
+
+ public static ResponseEntity> protocolError(String message, Object content) {
+ return error(HttpStatus.BAD_REQUEST, message, content);
+ }
+
+ public static ResponseEntity> unauthorizedError(Object content) {
+ return error(HttpStatus.UNAUTHORIZED, "Unauthorized", content);
+ }
+
+ public static ResponseEntity> unauthorizedError(String message, Object content) {
+ return error(HttpStatus.UNAUTHORIZED, message, content);
+ }
+}
+
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/AccessRuleRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
similarity index 59%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/AccessRuleRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
index ebe62bf66..2d9f9ec47 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/AccessRuleRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
@@ -1,10 +1,9 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.AccessRule;
+import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
@@ -12,8 +11,7 @@
*
* @see AccessRule
*/
-@ApplicationScoped
-@Transactional
+@Repository
public class AccessRuleRepository extends BaseRepository {
protected AccessRuleRepository() {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ApplicationRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
similarity index 52%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ApplicationRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
index 56f7fb4c3..ceb0f2960 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ApplicationRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
@@ -1,10 +1,9 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
@@ -12,8 +11,10 @@
*
* @see Application
*/
-@ApplicationScoped
-@Transactional
+
+// TODO: A repository class is not the right place to annotate with transactional. Verify?
+// TODO: Is there a reason why we would scope this to the application?
+@Repository
public class ApplicationRepository extends BaseRepository {
protected ApplicationRepository() {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ConnectionRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
similarity index 65%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ConnectionRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
index 81238472b..3369bd3f8 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/ConnectionRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
@@ -1,28 +1,23 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
* Provides operations for the Connection entity to interact with a database.
+ *
* @see Connection
*/
-@Transactional
-@ApplicationScoped
+@Repository
public class ConnectionRepository extends BaseRepository {
- private Logger logger = LoggerFactory.getLogger(ConnectionRepository.class);
-
protected ConnectionRepository() {
super(Connection.class);
}
@@ -34,10 +29,10 @@ public Connection findConnectionById(String connectionId) {
CriteriaBuilder cb = cb();
try {
return em.createQuery(query
- .where(
- eq(cb, queryRoot, "id", connectionId)))
+ .where(
+ eq(cb, queryRoot, "id", connectionId)))
.getSingleResult();
- } catch (NoResultException e){
+ } catch (NoResultException e) {
return null;
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/PrivilegeRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
similarity index 53%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/PrivilegeRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
index ac22d0dc5..7d02a05ff 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/PrivilegeRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
@@ -1,19 +1,17 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
* Provides operations for the Privilege entity to interact with a database.
* @see Privilege
*/
-@ApplicationScoped
-@Transactional
+
+@Repository
public class PrivilegeRepository extends BaseRepository {
protected PrivilegeRepository() {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/RoleRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
similarity index 57%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/RoleRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
index 63aa7f92d..d2cb6ae1e 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/RoleRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
@@ -1,18 +1,17 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
* Provides operations for the Role entity to interact with a database.
* @see Role
*/
-@ApplicationScoped
-@Transactional
+
+@Repository
public class RoleRepository extends BaseRepository {
protected RoleRepository() {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/TermsOfServiceRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
similarity index 71%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/TermsOfServiceRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
index 60913eab1..eb4ef256e 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/TermsOfServiceRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
@@ -1,27 +1,21 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.TermsOfService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
-import javax.transaction.Transactional;
import java.util.UUID;
/**
* Provides operations for the TermsOfService entity to interact with a database.
* @see TermsOfService
*/
-@Transactional
-@ApplicationScoped
+@Repository
public class TermsOfServiceRepository extends BaseRepository {
- private Logger logger = LoggerFactory.getLogger(TermsOfServiceRepository.class);
-
protected TermsOfServiceRepository() {
super(TermsOfService.class);
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
new file mode 100644
index 000000000..fa8d10cc9
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
@@ -0,0 +1,27 @@
+package edu.harvard.hms.dbmi.avillach.auth.repository;
+
+import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Provides operations for the UserMetadataMapping entity to interact with a database.
+ * @see UserMetadataMapping
+ */
+
+@Repository
+public class UserMetadataMappingRepository extends BaseRepository {
+
+ protected UserMetadataMappingRepository() {
+ super(UserMetadataMapping.class);
+ }
+
+ public List findByConnection(Connection connection) {
+ return getByColumn("connection", connection);
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
similarity index 86%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserRepository.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
index a627ccd5f..257939dc3 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/UserRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
@@ -1,18 +1,20 @@
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
+package edu.harvard.hms.dbmi.avillach.auth.repository;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.TermsOfService;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Repository;
-import javax.enterprise.context.ApplicationScoped;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
-import javax.persistence.criteria.*;
-import javax.transaction.Transactional;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Subquery;
import java.util.Date;
import java.util.List;
import java.util.Set;
@@ -23,11 +25,11 @@
* Provides operations for the User entity to interact with a database.
* @see User
*/
-@Transactional
-@ApplicationScoped
+
+@Repository
public class UserRepository extends BaseRepository {
- private Logger logger = LoggerFactory.getLogger(UserRepository.class);
+ private final static Logger logger = LoggerFactory.getLogger(UserRepository.class);
protected UserRepository() {
super(User.class);
@@ -99,10 +101,6 @@ public User findOrCreate(User inputUser) {
private User createUser(User inputUser) {
String subject = inputUser.getSubject();
-// if (subject == null && userId == null){
-// logger.error("createUser() cannot create user when both subject and userId are null");
-// return null;
-// }
logger.debug("createUser() creating user, subject: " + subject + " ......");
em().persist(inputUser);
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/package-info.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/package-info.java
similarity index 54%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/package-info.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/package-info.java
index 1b40cbce9..6ab62fffc 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/data/repository/package-info.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/package-info.java
@@ -1,4 +1,4 @@
/**
* Defines which entity objects should persist in the database.
*/
-package edu.harvard.hms.dbmi.avillach.auth.data.repository;
\ No newline at end of file
+package edu.harvard.hms.dbmi.avillach.auth.repository;
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
similarity index 71%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
index 9633b306c..314e03b57 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
@@ -1,23 +1,16 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.AccessRule;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.AccessRuleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import io.swagger.annotations.Api;
+import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
+import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
import javax.transaction.Transactional;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
import java.util.List;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
@@ -26,38 +19,37 @@
/**
* Endpoint for service handling business logic for access rules.
* Note: Only users with the super admin role can access this endpoint.
+ *
+ * Path: /accessRule
*/
-@Api
-@Path("/accessRule")
-public class AccessRuleService extends BaseEntityService {
- Logger logger = LoggerFactory.getLogger(AccessRuleService.class);
+@Controller("/accessRule")
+public class AccessRuleController extends BaseEntityService {
- @Inject
- AccessRuleRepository accessRuleRepo;
+ private final AccessRuleRepository accessRuleRepo;
- @Context
- SecurityContext securityContext;
+// @Context
+// private SecurityContext securityContext;
- public AccessRuleService() {
+ @Autowired
+ public AccessRuleController(AccessRuleRepository accessRuleRepo) {
super(AccessRule.class);
+ this.accessRuleRepo = accessRuleRepo;
}
@ApiOperation(value = "GET information of one AccessRule with the UUID, requires ADMIN or SUPER_ADMIN role")
- @GET
- @RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("/{accessRuleId}")
- public Response getAccessRuleById(
+ @Secured(value = {ADMIN, SUPER_ADMIN})
+ @GetMapping(value = "/{accessRuleId}")
+ public ResponseEntity> getAccessRuleById(
@ApiParam(value="The UUID of the accessRule to fetch information about")
@PathParam("accessRuleId") String accessRuleId) {
return getEntityById(accessRuleId,accessRuleRepo);
}
@ApiOperation(value = "GET a list of existing AccessRules, requires ADMIN or SUPER_ADMIN role")
- @GET
- @RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("")
- public Response getAccessRuleAll() {
+ @Secured({ADMIN, SUPER_ADMIN})
+ @GetMapping("")
+ public ResponseEntity> getAccessRuleAll() {
return getEntityAll(accessRuleRepo);
}
@@ -66,7 +58,7 @@ public Response getAccessRuleAll() {
@RolesAllowed(SUPER_ADMIN)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response addAccessRule(
+ public ResponseEntity> addAccessRule(
@ApiParam(required = true, value = "A list of AccessRule in JSON format")
List accessRules){
accessRules.stream().forEach(accessRule -> {
@@ -90,7 +82,7 @@ public Response addAccessRule(
@RolesAllowed(SUPER_ADMIN)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response updateAccessRule(
+ public ResponseEntity> updateAccessRule(
@ApiParam(required = true, value = "A list of AccessRule with fields to be updated in JSON format")
List accessRules){
return updateEntity(accessRules, accessRuleRepo);
@@ -101,7 +93,7 @@ public Response updateAccessRule(
@DELETE
@RolesAllowed(SUPER_ADMIN)
@Path("/{accessRuleId}")
- public Response removeById(
+ public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid accessRule Id")
@PathParam("accessRuleId") final String accessRuleId) {
return removeEntityById(accessRuleId, accessRuleRepo);
@@ -112,7 +104,7 @@ public Response removeById(
@RolesAllowed(SUPER_ADMIN)
@Path("/allTypes")
@Produces(MediaType.APPLICATION_JSON)
- public Response getAllTypes(){
+ public ResponseEntity> getAllTypes(){
return PICSUREResponse.success(AccessRule.TypeNaming.getTypeNameMap());
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
new file mode 100644
index 000000000..a190ef2b0
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
@@ -0,0 +1,86 @@
+package edu.harvard.hms.dbmi.avillach.auth.rest;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.ApplicationService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import jakarta.annotation.security.RolesAllowed;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
+
+/**
+ * Endpoint for registering and administering applications.
+ *
+ * Note: Only users with the super admin role can access this endpoint.
+ */
+@Api
+@Controller
+@RequestMapping(value = "/application")
+public class ApplicationController {
+
+ private final ApplicationService applicationService;
+
+ @Autowired
+ public ApplicationController(ApplicationService applicationService) {
+ this.applicationService = applicationService;
+ }
+
+ @ApiOperation(value = "GET information of one Application with the UUID, no role restrictions")
+ @GetMapping(value = "/{applicationId}")
+ public ResponseEntity> getApplicationById(
+ @ApiParam(required = true, value = "The UUID of the application to fetch information about")
+ @PathVariable("applicationId") String applicationId) {
+ return applicationService.getEntityById(applicationId);
+ }
+
+ @ApiOperation(value = "GET a list of existing Applications, no role restrictions")
+ @GetMapping
+ public ResponseEntity> getApplicationAll() {
+ return applicationService.getEntityAll();
+ }
+
+ @ApiOperation(value = "POST a list of Applications, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @PostMapping(value = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> addApplication(
+ @ApiParam(required = true, value = "A list of AccessRule in JSON format")
+ List applications) {
+ return applicationService.addNewApplications(applications);
+ }
+
+ @ApiOperation(value = "Update a list of Applications, will only update the fields listed, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @PutMapping(value = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> updateApplication(
+ @ApiParam(required = true, value = "A list of AccessRule with fields to be updated in JSON format")
+ List applications) {
+ return applicationService.updateApplications(applications);
+ }
+
+ @ApiOperation(value = "Refresh a token of an application by application Id, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @GetMapping(value = "/refreshToken/{applicationId}")
+ public ResponseEntity> refreshApplicationToken(
+ @ApiParam(required = true, value = "A valid application Id")
+ @PathVariable("applicationId") String applicationId) {
+ return applicationService.refreshApplicationToken(applicationId);
+ }
+
+ @ApiOperation(value = "DELETE an Application by Id only if the application is not associated by others, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @DeleteMapping(value = "/{applicationId}")
+ public ResponseEntity> removeById(
+ @ApiParam(required = true, value = "A valid accessRule Id")
+ @PathVariable("applicationId") final String applicationId) {
+ return applicationService.deleteApplicationById(applicationId);
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationService.java
deleted file mode 100644
index f47031d91..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationService.java
+++ /dev/null
@@ -1,187 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.rest;
-
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ApplicationRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.transaction.Transactional;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import java.util.*;
-
-import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
-
-/**
- * Endpoint for registering and administering applications.
- *
- * Note: Only users with the super admin role can access this endpoint.
- */
-@Api
-@Path("/application")
-public class ApplicationService extends BaseEntityService {
-
- private static final long ONE_YEAR = 1000L * 60 * 60 * 24 * 365;
-
- Logger logger = LoggerFactory.getLogger(ApplicationService.class);
-
- @Inject
- ApplicationRepository applicationRepo;
-
- @Inject
- PrivilegeRepository privilegeRepo;
-
- @Context
- SecurityContext securityContext;
-
- public ApplicationService() {
- super(Application.class);
- }
-
- @ApiOperation(value = "GET information of one Application with the UUID, no role restrictions")
- @GET
- @Path("/{applicationId}")
- public Response getApplicationById(
- @ApiParam(required = true, value="The UUID of the application to fetch information about")
- @PathParam("applicationId") String applicationId) {
- return getEntityById(applicationId,applicationRepo);
- }
-
- @ApiOperation(value = "GET a list of existing Applications, no role restrictions")
- @GET
- @Path("")
- public Response getApplicationAll() {
- return getEntityAll(applicationRepo);
- }
-
- @ApiOperation(value = "POST a list of Applications, requires SUPER_ADMIN role")
- @Transactional
- @POST
- @RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
- public Response addApplication(
- @ApiParam(required = true, value = "A list of AccessRule in JSON format")
- List applications){
- checkAssociation(applications);
- List appEntities = addOrUpdate(applications, true, applicationRepo);
- for(Application application : appEntities) {
- try{
- application.setToken(
- generateApplicationToken(application)
- );
- } catch(Exception e) {
- logger.error("", e);
- }
- }
-
- return updateEntity(appEntities, applicationRepo);
- }
-
- @ApiOperation(value = "Update a list of Applications, will only update the fields listed, requires SUPER_ADMIN role")
- @PUT
- @RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
- public Response updateApplication(
- @ApiParam(required = true, value = "A list of AccessRule with fields to be updated in JSON format")
- List applications){
- checkAssociation(applications);
- return updateEntity(applications, applicationRepo);
- }
-
- @ApiOperation(value = "Refresh a token of an application by application Id, requires SUPER_ADMIN role")
- @GET
- @RolesAllowed({SUPER_ADMIN})
- @Path("/refreshToken/{applicationId}")
- public Response refreshApplicationToken(
- @ApiParam(required = true, value = "A valid application Id")
- @PathParam("applicationId") String applicationId){
- Application application = applicationRepo.getById(UUID.fromString(applicationId));
-
- if (application == null){
- logger.error("refreshApplicationToken() cannot find the application by applicationId: " + applicationId);
- throw new ProtocolException("Cannot find application by the given applicationId: " + applicationId);
- }
-
- String newToken = generateApplicationToken(application);
- if (newToken != null){
- application.setToken(
- newToken
- );
-
- applicationRepo.merge(application);
- } else {
- logger.error("refreshApplicationToken() token is null for application: " + applicationId);
- throw new ApplicationException("Inner problem, please contact admin");
- }
-
- return PICSUREResponse.success(Map.of("token", newToken));
-
- }
-
- @ApiOperation(value = "DELETE an Application by Id only if the application is not associated by others, requires SUPER_ADMIN role")
- @Transactional
- @DELETE
- @RolesAllowed({SUPER_ADMIN})
- @Path("/{applicationId}")
- public Response removeById(
- @ApiParam(required = true, value = "A valid accessRule Id")
- @PathParam("applicationId") final String applicationId) {
- Application application = applicationRepo.getById(UUID.fromString(applicationId));
- return removeEntityById(applicationId, applicationRepo);
- }
-
- private void checkAssociation(List applications){
- for (Application application: applications){
- if (application.getPrivileges() != null) {
- Set privileges = new HashSet<>();
- application.getPrivileges().stream().forEach(p -> {
- Privilege privilege = privilegeRepo.getById(p.getUuid());
- if (privilege != null){
- privilege.setApplication(application);
- privileges.add(privilege);
- } else {
- logger.error("Didn't find privilege by uuid: " + p.getUuid());
- }
- });
- application.setPrivileges(privileges);
-
- }
- }
-
- }
-
- public String generateApplicationToken(Application application){
- if (application == null || application.getUuid() == null) {
- logger.error("generateApplicationToken() application is null or uuid is missing to generate the application token");
- throw new ApplicationException("Cannot generate application token, please contact admin");
- }
-
- return JWTUtil.createJwtToken(
- JAXRSConfiguration.clientSecret, null, null,
- new HashMap<>(
- Map.of(
- "user_id", AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX + "|" + application.getName()
- )
- ),
- AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX + "|" + application.getUuid().toString(), 365L * 1000 * 60 * 60 * 24);
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
new file mode 100644
index 000000000..cf891eca5
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
@@ -0,0 +1,55 @@
+package edu.harvard.hms.dbmi.avillach.auth.rest;
+
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthenticationService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthorizationService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.FENCEAuthenticationService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.Map;
+
+
+/**
+ * The authentication endpoint for PSAMA.
+ */
+@Api
+@Controller
+@RequestMapping("/")
+public class AuthController {
+
+ private final static Logger logger = LoggerFactory.getLogger(AuthController.class.getName());
+
+ public final AuthorizationService authorizationService;
+
+ public final AuthenticationService authenticationService;
+ public final FENCEAuthenticationService fenceAuthenticationService;
+
+ @Autowired
+ public AuthController(AuthorizationService authorizationService, AuthenticationService authenticationService, FENCEAuthenticationService fenceAuthenticationService) {
+ this.authorizationService = authorizationService;
+ this.authenticationService = authenticationService;
+ this.fenceAuthenticationService = fenceAuthenticationService;
+ }
+
+ @ApiOperation(value = "The authentication endpoint for retrieving a valid user token")
+ @PostMapping(path = "/authentication", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> authentication(@ApiParam(required = true, value = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") Map authRequest) {
+ logger.debug("authentication() starting...");
+ if (JAXRSConfiguration.idp_provider.equalsIgnoreCase("fence")) {
+ logger.debug("authentication() FENCE authentication");
+ return fenceAuthenticationService.getFENCEProfile(authRequest);
+ } else {
+ logger.debug("authentication() default authentication");
+ return authenticationService.getToken(authRequest);
+ }
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthService.java
deleted file mode 100644
index 5bcb58d76..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthService.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.rest;
-
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.service.auth.AuthenticationService;
-import edu.harvard.hms.dbmi.avillach.auth.service.auth.AuthorizationService;
-import edu.harvard.hms.dbmi.avillach.auth.service.auth.FENCEAuthenticationService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Response;
-import java.util.Map;
-
-/**
- * The authentication endpoint for PSAMA.
- */
-@Api
-@Path("/")
-@Consumes("application/json")
-@Produces("application/json")
-public class AuthService {
-
- private Logger logger = LoggerFactory.getLogger(this.getClass());
-
- @Inject
- AuthorizationService authorizationService;
-
- @Inject
- AuthenticationService authenticationService;
-
- @Inject
- FENCEAuthenticationService fenceAuthenticationService;
-
- @ApiOperation(value = "The authentication endpoint for retrieving a valid user token")
- @POST
- @Path("/authentication")
- public Response authentication(@ApiParam(required = true, value = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") Map authRequest) {
- logger.debug("authentication() starting...");
- if (JAXRSConfiguration.idp_provider.equalsIgnoreCase("fence")) {
- logger.debug("authentication() FENCE authentication");
- return fenceAuthenticationService.getFENCEProfile(authRequest);
- } else {
- logger.debug("authentication() default authentication");
- return authenticationService.getToken(authRequest);
- }
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
new file mode 100644
index 000000000..923f98556
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
@@ -0,0 +1,83 @@
+package edu.harvard.hms.dbmi.avillach.auth.rest;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.ConnectionWebService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
+
+/**
+ * Endpoint for service handling business logic for connections to PSAMA.
+ * Note: Only users with the super admin role can access this endpoint.
+ */
+@Api
+@Controller
+@RequestMapping("/connection")
+public class ConnectionWebController { // TODO: This isn't a service class, it's a controller. We should refactor this so it doesn't extend BaseEntityService
+
+
+ private final ConnectionWebService connectionWebService;
+
+ @Autowired
+ public ConnectionWebController(ConnectionWebService connectionWebSerivce) {
+ this.connectionWebService = connectionWebSerivce;
+ }
+
+ @ApiOperation(value = "GET information of one Connection with the UUID, requires ADMIN or SUPER_ADMIN role")
+ @GetMapping(path ="/{connectionId}", produces = "application/json")
+ @Secured({SUPER_ADMIN, ADMIN})
+ public ResponseEntity> getConnectionById(
+ @ApiParam(required = true, value="The UUID of the Connection to fetch information about")
+ @PathVariable("connectionId") String connectionId) {
+ return connectionWebService.getEntityById(connectionId);
+ }
+
+ @ApiOperation(value = "GET a list of existing Connection, requires SUPER_ADMIN or ADMIN role")
+ @GetMapping(path ="/", produces = "application/json")
+ @Secured({SUPER_ADMIN, ADMIN})
+ public ResponseEntity> getAllConnections() {
+ return connectionWebService.getEntityAll();
+ }
+
+ @ApiOperation(value = "POST a list of Connections, requires SUPER_ADMIN role")
+ @Transactional
+ @Secured({SUPER_ADMIN})
+ @PostMapping(produces = "application/json", consumes = "application/json")
+ public ResponseEntity> addConnection(
+ @ApiParam(required = true, value = "A list of Connections in JSON format")
+ List connections){
+ return connectionWebService.addEntity(connections);
+ }
+
+ @ApiOperation(value = "Update a list of Connections, will only update the fields listed, requires SUPER_ADMIN role")
+ @Secured({SUPER_ADMIN})
+ @PutMapping(produces = "application/json", consumes = "application/json")
+ public ResponseEntity> updateConnection(
+ @ApiParam(required = true, value = "A list of Connection with fields to be updated in JSON format")
+ List connections){
+ return connectionWebService.updateEntity(connections);
+ }
+
+ @ApiOperation(value = "DELETE an Connection by Id only if the Connection is not associated by others, requires SUPER_ADMIN role")
+ @Transactional
+ @Secured({SUPER_ADMIN})
+ @DeleteMapping(path ="/{connectionId}", produces = "application/json")
+ public ResponseEntity> removeById(
+ @ApiParam(required = true, value = "A valid connection Id")
+ @PathVariable("connectionId") final String connectionId) {
+ return connectionWebService.removeEntityById(connectionId);
+ }
+
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebService.java
deleted file mode 100644
index 325534686..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebService.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.rest;
-
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.transaction.Transactional;
-import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.util.List;
-
-import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
-import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
-
-/**
- * Endpoint for service handling business logic for connections to PSAMA.
- * Note: Only users with the super admin role can access this endpoint.
- */
-@Api
-@Path("connection")
-public class ConnectionWebService extends BaseEntityService {
-
- public ConnectionWebService() {
- super(Connection.class);
- }
-
- @Inject
- ConnectionRepository connectionRepo;
-
- @ApiOperation(value = "GET information of one Connection with the UUID, requires ADMIN or SUPER_ADMIN role")
- @Path("{connectionId}")
- @GET
- @Produces("application/json")
- @RolesAllowed({SUPER_ADMIN, ADMIN})
- public Response getConnectionById(
- @ApiParam(required = true, value="The UUID of the Connection to fetch information about")
- @PathParam("connectionId") String connectionId) {
- return getEntityById(connectionId,connectionRepo);
- }
-
- @ApiOperation(value = "GET a list of existing Connection, requires SUPER_ADMIN or ADMIN role")
- @GET
- @Produces("application/json")
- @RolesAllowed({SUPER_ADMIN, ADMIN})
- public Response getAllConnections() {
- return getEntityAll(connectionRepo);
- }
-
- @ApiOperation(value = "POST a list of Connections, requires SUPER_ADMIN role")
- @Transactional
- @POST
- @Consumes(MediaType.APPLICATION_JSON)
- @RolesAllowed({SUPER_ADMIN})
- @Path("/")
- public Response addConnection(
- @ApiParam(required = true, value = "A list of Connections in JSON format")
- List connections){
- return addEntity(connections);
- }
-
- @ApiOperation(value = "Update a list of Connections, will only update the fields listed, requires SUPER_ADMIN role")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- @RolesAllowed({SUPER_ADMIN})
- @Path("/")
- public Response updateConnection(
- @ApiParam(required = true, value = "A list of Connection with fields to be updated in JSON format")
- List connections){
- return updateEntity(connections, connectionRepo);
- }
-
- @ApiOperation(value = "DELETE an Connection by Id only if the Connection is not associated by others, requires SUPER_ADMIN role")
- @Transactional
- @DELETE
- @RolesAllowed({SUPER_ADMIN})
- @Path("/{connectionId}")
- public Response removeById(
- @ApiParam(required = true, value = "A valid connection Id")
- @PathParam("connectionId") final String connectionId) {
- return removeEntityById(connectionId, connectionRepo);
- }
-
- private Response addEntity(List connections){
- for (Connection c : connections){
- if (c.getSubPrefix() == null || c.getRequiredFields() == null || c.getLabel() == null || c.getId() == null){
- return PICSUREResponse.protocolError("Id, Label, Subprefix, and RequiredFields cannot be null");
- }
- Connection conn = connectionRepo.findConnectionById(c.getId());
- if (conn != null){
- return PICSUREResponse.protocolError("Id must be unique, a connection with id " + c.getId() + " already exists in the database");
- }
- }
- return addEntity(connections, connectionRepo);
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
new file mode 100644
index 000000000..5f1aa82bf
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
@@ -0,0 +1,77 @@
+package edu.harvard.hms.dbmi.avillach.auth.rest;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.service.PrivilegeService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import jakarta.annotation.security.RolesAllowed;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
+
+/**
+ * Endpoint for service handling business logic for privileges.
+ *
Note: Only users with the super admin role can access this endpoint.
+ */
+@Api
+@RestController
+@RequestMapping("/privilege")
+public class PrivilegeController {
+
+ private final PrivilegeService privilegeService;
+
+ @Autowired
+ public PrivilegeController(PrivilegeService privilegeService) {
+ this.privilegeService = privilegeService;
+ }
+
+ @ApiOperation(value = "GET information of one Privilege with the UUID, requires ADMIN or SUPER_ADMIN role")
+ @RolesAllowed({ADMIN, SUPER_ADMIN})
+ @GetMapping(path = "/{privilegeId}", produces = "application/json")
+ public ResponseEntity> getPrivilegeById(
+ @ApiParam(value="The UUID of the privilege to fetch information about")
+ @PathVariable("privilegeId") String privilegeId) {
+ return this.privilegeService.getEntityById(privilegeId);
+ }
+
+ @ApiOperation(value = "GET a list of existing privileges, requires ADMIN or SUPER_ADMIN role")
+ @RolesAllowed({ADMIN, SUPER_ADMIN})
+ @GetMapping(path = "/", produces = "application/json")
+ public ResponseEntity> getPrivilegeAll() {
+ return this.privilegeService.getEntityAll();
+ }
+
+ @ApiOperation(value = "POST a list of privileges, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @PostMapping(path = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> addPrivilege(
+ @ApiParam(required = true, value = "A list of privileges in JSON format")
+ List privileges){
+ return this.privilegeService.addEntity(privileges);
+ }
+
+ @ApiOperation(value = "Update a list of privileges, will only update the fields listed, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @PutMapping(path = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> updatePrivilege(
+ @ApiParam(required = true, value = "A list of privilege with fields to be updated in JSON format")
+ List privileges){
+ return this.privilegeService.updateEntity(privileges);
+ }
+
+ @ApiOperation(value = "DELETE an privilege by Id only if the privilege is not associated by others, requires SUPER_ADMIN role")
+ @RolesAllowed({SUPER_ADMIN})
+ @DeleteMapping(path = "/{privilegeId}", produces = "application/json")
+ public ResponseEntity> removeById(
+ @ApiParam(required = true, value = "A valid privilege Id")
+ @PathVariable("privilegeId") final String privilegeId) {
+ return this.privilegeService.deletePrivilegeByPrivilegeId(privilegeId);
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeService.java
deleted file mode 100644
index e5274d56c..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeService.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.rest;
-
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.transaction.Transactional;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import java.util.List;
-import java.util.UUID;
-
-import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
-import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
-
-/**
- * Endpoint for service handling business logic for privileges.
- *
Note: Only users with the super admin role can access this endpoint.
- */
-@Api
-@Path("/privilege")
-public class PrivilegeService extends BaseEntityService {
-
- Logger logger = LoggerFactory.getLogger(PrivilegeService.class);
-
- @Inject
- PrivilegeRepository privilegeRepo;
-
- @Context
- SecurityContext securityContext;
-
- public PrivilegeService() {
- super(Privilege.class);
- }
-
- @ApiOperation(value = "GET information of one Privilege with the UUID, requires ADMIN or SUPER_ADMIN role")
- @GET
- @RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("/{privilegeId}")
- public Response getPrivilegeById(
- @ApiParam(value="The UUID of the privilege to fetch information about")
- @PathParam("privilegeId") String privilegeId) {
- return getEntityById(privilegeId,privilegeRepo);
- }
-
- @ApiOperation(value = "GET a list of existing privileges, requires ADMIN or SUPER_ADMIN role")
- @GET
- @RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("")
- public Response getPrivilegeAll() {
- return getEntityAll(privilegeRepo);
- }
-
- @ApiOperation(value = "POST a list of privileges, requires SUPER_ADMIN role")
- @POST
- @RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
- public Response addPrivilege(
- @ApiParam(required = true, value = "A list of privileges in JSON format")
- List privileges){
- return addEntity(privileges, privilegeRepo);
- }
-
- @ApiOperation(value = "Update a list of privileges, will only update the fields listed, requires SUPER_ADMIN role")
- @PUT
- @RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
- public Response updatePrivilege(
- @ApiParam(required = true, value = "A list of privilege with fields to be updated in JSON format")
- List privileges){
- return updateEntity(privileges, privilegeRepo);
- }
-
- @ApiOperation(value = "DELETE an privilege by Id only if the privilege is not associated by others, requires SUPER_ADMIN role")
- @Transactional
- @DELETE
- @RolesAllowed({SUPER_ADMIN})
- @Path("/{privilegeId}")
- public Response removeById(
- @ApiParam(required = true, value = "A valid privilege Id")
- @PathParam("privilegeId") final String privilegeId) {
- Privilege privilege = privilegeRepo.getById(UUID.fromString(privilegeId));
- if (ADMIN.equals(privilege.getName())){
- logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext)
- + ", is trying to remove the system admin privilege: " + ADMIN);
- return PICSUREResponse.protocolError("System Admin privilege cannot be removed - uuid: " + privilege.getUuid().toString()
- + ", name: " + privilege.getName());
- }
-
- return removeEntityById(privilegeId, privilegeRepo);
- }
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
similarity index 86%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
index c58cfdf04..ae8378cfa 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
@@ -2,11 +2,11 @@
import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -35,9 +35,9 @@
*/
@Api
@Path("/role")
-public class RoleService extends BaseEntityService {
+public class RoleController extends BaseEntityService {
- Logger logger = LoggerFactory.getLogger(RoleService.class);
+ Logger logger = LoggerFactory.getLogger(RoleController.class);
@Context
SecurityContext securityContext;
@@ -48,7 +48,7 @@ public class RoleService extends BaseEntityService {
@Inject
PrivilegeRepository privilegeRepo;
- public RoleService() {
+ public RoleController() {
super(Role.class);
}
@@ -56,7 +56,7 @@ public RoleService() {
@GET
@Path("/{roleId}")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- public Response getRoleById(
+ public ResponseEntity> getRoleById(
@ApiParam(value="The UUID of the Role to fetch information about")
@PathParam("roleId") String roleId) {
return getEntityById(roleId,roleRepo);
@@ -66,7 +66,7 @@ public Response getRoleById(
@GET
@Path("")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- public Response getRoleAll() {
+ public ResponseEntity> getRoleAll() {
return getEntityAll(roleRepo);
}
@@ -76,7 +76,7 @@ public Response getRoleAll() {
@RolesAllowed({SUPER_ADMIN})
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response addRole(
+ public ResponseEntity> addRole(
@ApiParam(required = true, value = "A list of Roles in JSON format")
List roles){
checkPrivilegeAssociation(roles);
@@ -89,7 +89,7 @@ public Response addRole(
@RolesAllowed({SUPER_ADMIN})
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response updateRole(
+ public ResponseEntity> updateRole(
@ApiParam(required = true, value = "A list of Roles with fields to be updated in JSON format")
List roles){
checkPrivilegeAssociation(roles);
@@ -101,7 +101,7 @@ public Response updateRole(
@DELETE
@RolesAllowed({SUPER_ADMIN})
@Path("/{roleId}")
- public Response removeById(
+ public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid Role Id")
@PathParam("roleId") final String roleId) {
Role role = roleRepo.getById(UUID.fromString(roleId));
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfServiceEndpoint.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
similarity index 75%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfServiceEndpoint.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
index 40aeb1e86..f4125a8e8 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfServiceEndpoint.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
@@ -1,8 +1,8 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.TermsOfService;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import edu.harvard.hms.dbmi.avillach.auth.service.TOSService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@@ -20,9 +20,9 @@
* Endpoint for creating and updating terms of service entities. Records when a user accepts a term of service.
*/
@Path("tos")
-public class TermsOfServiceEndpoint extends BaseEntityService {
+public class TermsOfSerivceController extends BaseEntityService {
- public TermsOfServiceEndpoint() {
+ public TermsOfSerivceController() {
super(TermsOfService.class);
}
@@ -33,7 +33,7 @@ public TermsOfServiceEndpoint() {
@Path("/latest")
@GET
@Produces("text/html")
- public Response getLatestTermsOfService(){
+ public ResponseEntity> getLatestTermsOfService(){
return Response.ok(tosService.getLatest()).build();
}
@@ -42,7 +42,7 @@ public Response getLatestTermsOfService(){
@RolesAllowed({AuthNaming.AuthRoleNaming.ADMIN, SUPER_ADMIN})
@Consumes("text/html")
@Produces("application/json")
- public Response updateTermsOfService(
+ public ResponseEntity> updateTermsOfService(
@ApiParam(required = true, value = "A html page for updating") String html){
return Response.status(201).entity(tosService.updateTermsOfService(html)).build();
}
@@ -51,7 +51,7 @@ public Response updateTermsOfService(
@Path("/")
@GET
@Produces("text/plain")
- public Response hasUserAcceptedTOS(@Context SecurityContext securityContext){
+ public ResponseEntity> hasUserAcceptedTOS(@Context SecurityContext securityContext){
String userSubject = securityContext.getUserPrincipal().getName();
return Response.ok(tosService.hasUserAcceptedLatest(userSubject)).build();
}
@@ -60,7 +60,7 @@ public Response hasUserAcceptedTOS(@Context SecurityContext securityContext){
@Path("/accept")
@POST
@Produces("application/json")
- public Response acceptTermsOfService(@Context SecurityContext securityContext){
+ public ResponseEntity> acceptTermsOfService(@Context SecurityContext securityContext){
String userSubject = securityContext.getUserPrincipal().getName();
tosService.acceptTermsOfService(userSubject);
return Response.ok().build();
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
new file mode 100644
index 000000000..f3da366a1
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
@@ -0,0 +1,319 @@
+package edu.harvard.hms.dbmi.avillach.auth.rest;
+
+import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
+import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthorizationService;
+import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
+import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+
+import java.security.Principal;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil.parseToken;
+
+/**
+ * Token introspection endpoint called by an application to validate a user's token and permissions by request.
+ *
+ * Here, a registered application asks if the user behind a token is allowed to perform certain activities by
+ * showing this endpoint the token and where the user wants to go.
+ * To accomplish this, this endpoint validates the incoming token, then checks if the user behind the token
+ * is authorized to access the URLs they queried and send data along with them. The AuthorizationService class handles authorization
+ * {@link AuthorizationService} at the access rule level, but this endpoint handles token validation and pre-check at the privilege level.
+ */
+@Api
+@Controller("/token")
+public class TokenController {
+
+ private Logger logger = LoggerFactory.getLogger(TokenController.class);
+
+ private final UserRepository userRepo;
+
+ private final AuthorizationService authorizationService;
+
+ @Context
+ SecurityContext securityContext;
+
+ @Autowired
+ public TokenController(UserRepository userRepo, AuthorizationService authorizationService) {
+ this.userRepo = userRepo;
+ this.authorizationService = authorizationService;
+ }
+
+ @ApiOperation(value = "Token introspection endpoint for user to retrieve a valid token")
+ @PostMapping("/inspect")
+ @Consumes("application/json")
+ public ResponseEntity> inspectToken(
+ @ApiParam(required = true, value = "A JSON object that at least" +
+ " include a user the token for validation")
+ Map inputMap) {
+ logger.info("TokenInspect starting...");
+ TokenInspection tokenInspection = _inspectToken(inputMap);
+ if (tokenInspection.message != null)
+ tokenInspection.responseMap.put("message", tokenInspection.message);
+
+ logger.info("Finished token introspection.");
+ return PICSUREResponse.success(tokenInspection.responseMap);
+ }
+
+ /**
+ * This endpoint currently is only for a user token to be refreshed.
+ * Application token won't work here.
+ *
+ * @return
+ */
+ @ApiOperation(value = "To refresh current user's token if the user is an active user")
+ @GET
+ @Path("/refresh")
+ public ResponseEntity> refreshToken(@RequestHeader("Authorization") String authorizationHeader) {
+ logger.debug("RefreshToken starting...");
+
+ // still need to check if the user is in the database or not,
+ // just in case something changes in the middle
+ Principal principal = securityContext.getUserPrincipal();
+ if (!(principal instanceof User)) {
+ logger.error("refreshToken() Security context didn't have a user stored.");
+ }
+ User user = (User) principal;
+
+ if (user.getUuid() == null) {
+ logger.error("refreshToken() Stored user doesn't have a uuid.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ user = userRepo.getById(user.getUuid());
+ if (user == null) {
+ logger.error("refreshToken() When retrieving current user, it returned null, the user might be removed from database");
+ throw new NotAuthorizedException("User doesn't exist anymore");
+ }
+
+ if (!user.isActive()) {
+ logger.error("refreshToken() The user has just been deactivated.");
+ throw new NotAuthorizedException("User has been deactivated.");
+ }
+
+ String subject = user.getSubject();
+ if (subject == null || subject.isEmpty()) {
+ logger.error("refreshToken() subject doesn't exist in the user.");
+ }
+
+ // parse origin token
+ Jws jws;
+ try {
+ String token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader).orElseThrow(() -> new NotAuthorizedException("Token not found"));
+ jws = parseToken(token);
+
+ } catch (NotAuthorizedException ex) {
+ return PICSUREResponse.protocolError("Cannot parse original token");
+ }
+
+ Claims claims = jws.getBody();
+
+ // just check if the subject is along with the database record,
+ // just in case something has changed in middle
+ if (!subject.equals(claims.getSubject())) {
+ logger.error("refreshToken() user subject is not the same as the subject of the input token");
+ return PICSUREResponse.applicationError("Inner application error, try again or contact admin.");
+ }
+
+ Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
+ String refreshedToken = JWTUtil.createJwtToken(JAXRSConfiguration.clientSecret,
+ claims.getId(),
+ claims.getIssuer(),
+ claims,
+ subject,
+ JAXRSConfiguration.tokenExpirationTime);
+
+ logger.debug("Finished RefreshToken and new token has been generated.");
+ return PICSUREResponse.success(Map.of(
+ "token", refreshedToken,
+ "expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString()
+ ));
+ }
+
+
+ /**
+ * @param inputMap
+ * @return
+ */
+ private TokenInspection _inspectToken(Map inputMap) {
+ logger.debug("_inspectToken, the incoming token map is: {}", inputMap.entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + " - " + entry.getValue())
+ .collect(Collectors.joining(", ")));
+
+ TokenInspection tokenInspection = new TokenInspection();
+
+ String token = (String) inputMap.get("token");
+ if (token == null || token.isEmpty()) {
+ logger.error("Token - " + token + " is blank");
+ tokenInspection.message = "Token not found";
+ return tokenInspection;
+ }
+
+ // parse the token based on client secret
+ // don't need to check if jws is null or not, since parse function has already checked
+ Jws jws;
+ try {
+ jws = parseToken(token);
+
+ /*
+ * token has been verified, now we remove it from inputMap, so further logs will not be able to log
+ * the token accidentally!
+ */
+ inputMap.remove("token");
+ } catch (NotAuthorizedException ex) {
+ // only when the token is for sure invalid, we can dump it into the log.
+ logger.error("_inspectToken() the token - " + token + " - is invalid with exception: " + ex.getMessage());
+ tokenInspection.message = ex.getMessage();
+ return tokenInspection;
+ }
+
+
+ Application application;
+
+ try {
+ application = (Application) securityContext.getUserPrincipal();
+ } catch (ClassCastException ex) {
+ logger.error(securityContext.getUserPrincipal().getName()
+ + " - " + securityContext.getUserPrincipal().getClass().getSimpleName() +
+ " - is trying to use token introspection endpoint" +
+ ", but it is not an application");
+ throw new ApplicationException("The application token does not associate with an application but "
+ + securityContext.getUserPrincipal().getClass().getSimpleName());
+ }
+
+ // application null check should be finished when application token goes through the JWTFilter authentication process,
+ // here we just double check it to prevent a null application object goes further.
+ if (application == null) {
+ logger.error("_inspectToken() There is no application in securityContext, which shall not be.");
+ throw new ApplicationException("Inner application error, please ask admin to check the log.");
+ }
+
+ String subject = jws.getBody().getSubject();
+
+ // get the user based on subject field in token
+ User user;
+
+ // check if the token is the special LONG_TERM_TOKEN,
+ // the differences between this special token and normal token is
+ // one user only has one long_term_token stored in database,
+ // this token needs to be exactly the same as the database one.
+ // If the token refreshed, the old one will be invalid. But normal
+ // token will not invalid the old ones if refreshed.
+ boolean isLongTermToken = false;
+ if (subject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
+ subject = subject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
+ isLongTermToken = true;
+ }
+
+ user = userRepo.getUniqueResultByColumn("subject", subject);
+ logger.info("_inspectToken() user with subject - " + subject + " - exists in database");
+ if (user == null) {
+ logger.error("_inspectToken() could not find user with subject " + subject);
+ tokenInspection.message = "user doesn't exist";
+ return tokenInspection;
+ }
+
+
+ //Essentially we want to return jws.getBody() with an additional active: true field
+ //only under certain circumstances, the token will return active
+ boolean isAuthorizationPassed = false;
+ String errorMsg = null;
+
+ // long term token needs to be the same as the token in the database user table, if
+ // not the token might has been compromised, which will not go through the authorization check
+ boolean isLongTermTokenCompromised = false;
+ if (isLongTermToken && !token.equals(user.getToken())) {
+ // in long_term_token mode, the token needs to be exactly the same as the token in user table
+ isLongTermTokenCompromised = true;
+ logger.error("_inspectToken User " + user.getUuid() + "|" + user.getSubject()
+ + "is sending a long term token that is not matching the record in database user table.");
+ errorMsg = "Cannot find matched long term token, your token might have been refreshed.";
+ }
+
+ // we go through the authorization layer check only if we need to in order to improve the performance
+ // the logic here, if the token associated with a user, we will start the authorization check.
+ // If the current application has at least one privilege, the user must have one privilege associated to the application
+ // pass the accessRule check if there is any accessRules associated with.
+ if (application.getPrivileges() == null || application.getPrivileges().isEmpty()) {
+ // if no privileges associated
+ isAuthorizationPassed = true;
+ //we still want to log this, though.
+ logger.info("ACCESS_LOG ___ " + user.getUuid() + "," + user.getEmail() + "," + user.getName() +
+ " ___ has been granted access to execute query ___ " + inputMap.get("request") + " ___ in application ___ " + application.getName()
+ + " ___ NO APP PRIVILEGES DEFINED");
+ } else if (!isLongTermTokenCompromised
+ && user.getRoles() != null
+ // The protocol between applications and PSAMA is application will
+ // attach everything that needs to be verified in request field of inputMap
+ // besides token. So here we should attach everything in request.
+ && authorizationService.isAuthorized(application, inputMap.get("request"), user)) {
+ isAuthorizationPassed = true;
+ } else {
+ // if isLongTermTokenCompromised flag is true,
+ // the error message has already been set previously
+ if (!isLongTermTokenCompromised)
+ errorMsg = "User doesn't have enough privileges.";
+ }
+
+ if (isAuthorizationPassed) {
+ tokenInspection.responseMap.put("active", true);
+ ArrayList roles = new ArrayList();
+ for (Privilege p : user.getTotalPrivilege()) {
+ roles.add(p.getName());
+ }
+ tokenInspection.responseMap.put("roles", String.join(",", roles));
+ } else {
+ if (errorMsg != null)
+ tokenInspection.message = errorMsg;
+ return tokenInspection;
+ }
+
+ tokenInspection.responseMap.putAll(jws.getBody());
+
+ // attach all privileges associated with the application to the responseMap
+ tokenInspection.responseMap.put("privileges", user.getPrivilegeNameSetByApplication(application));
+
+
+ logger.info("_inspectToken() Successfully inspect and return response map: "
+ + tokenInspection.responseMap.entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + " - " + entry.getValue())
+ .collect(Collectors.joining(", ")));
+ return tokenInspection;
+ }
+
+ /**
+ * inner used token introspection class with active:false included
+ */
+ private class TokenInspection {
+ Map responseMap = new HashMap<>();
+ String message = null;
+
+ public TokenInspection() {
+ responseMap.put("active", false);
+ }
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenService.java
deleted file mode 100644
index fdbf75b58..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenService.java
+++ /dev/null
@@ -1,325 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.rest;
-
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ApplicationRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.auth.AuthorizationService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jws;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import java.security.Principal;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * Token introspection endpoint called by an application to validate a user's token and permissions by request.
- *
- * Here, a registered application asks if the user behind a token is allowed to perform certain activities by
- * showing this endpoint the token and where the user wants to go.
- * To accomplish this, this endpoint validates the incoming token, then checks if the user behind the token
- * is authorized to access the URLs they queried and send data along with them. The AuthorizationService class handles authorization
- * {@link AuthorizationService} at the access rule level, but this endpoint handles token validation and pre-check at the privilege level.
- */
-@Api
-@Path("/token")
-public class TokenService {
-
- private Logger logger = LoggerFactory.getLogger(TokenService.class);
-
- @Inject
- UserRepository userRepo;
-
- @Inject
- ApplicationRepository applicationRepo;
-
- @Inject
- AuthorizationService authorizationService;
-
- @Context
- SecurityContext securityContext;
-
- @ApiOperation(value = "Token introspection endpoint for user to retrieve a valid token")
- @POST
- @Path("/inspect")
- @Consumes("application/json")
- public Response inspectToken(
- @ApiParam(required = true, value = "A JSON object that at least" +
- " include a user the token for validation")
- Map inputMap){
- logger.info("TokenInspect starting...");
- TokenInspection tokenInspection = _inspectToken(inputMap);
- if (tokenInspection.message != null)
- tokenInspection.responseMap.put("message", tokenInspection.message);
-
- logger.info("Finished token introspection.");
- return PICSUREResponse.success(tokenInspection.responseMap);
- }
-
- /**
- * This endpoint currently is only for a user token to be refreshed.
- * Application token won't work here.
- *
- * @return
- */
- @ApiOperation(value = "To refresh current user's token if the user is an active user")
- @GET
- @Path("/refresh")
- public Response refreshToken(@Context HttpHeaders httpHeaders){
- logger.debug("RefreshToken starting...");
-
- // still need to check if the user is in the database or not,
- // just in case something changes in the middle
- Principal principal = securityContext.getUserPrincipal();
- if (!(principal instanceof User)){
- logger.error("refreshToken() Security context didn't have a user stored.");
- }
- User user = (User) principal;
-
- if (user.getUuid() == null){
- logger.error("refreshToken() Stored user doesn't have a uuid.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- user = userRepo.getById(user.getUuid());
- if (user == null){
- logger.error("refreshToken() When retrieving current user, it returned null, the user might be removed from database");
- throw new NotAuthorizedException("User doesn't exist anymore");
- }
-
- if (!user.isActive()){
- logger.error("refreshToken() The user has just been deactivated.");
- throw new NotAuthorizedException("User has been deactivated.");
- }
-
- String subject = user.getSubject();
- if (subject == null || subject.isEmpty()){
- logger.error("refreshToken() subject doesn't exist in the user.");
- }
-
- // parse origin token
- Jws jws;
- try {
- jws = AuthUtils.parseToken(JAXRSConfiguration.clientSecret,
- // the original token should be able to grab from header, otherwise, it should be stopped
- // at JWTFilter level
- httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION)
- .substring(6)
- .trim());
- } catch (NotAuthorizedException ex) {
- return PICSUREResponse.protocolError("Cannot parse original token");
- }
-
- Claims claims = jws.getBody();
-
- // just check if the subject is along with the database record,
- // just in case something has changed in middle
- if (!subject.equals(claims.getSubject())){
- logger.error("refreshToken() user subject is not the same as the subject of the input token");
- return PICSUREResponse.applicationError("Inner application error, try again or contact admin.");
- }
-
- Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
- String refreshedToken = JWTUtil.createJwtToken(JAXRSConfiguration.clientSecret,
- claims.getId(),
- claims.getIssuer(),
- claims,
- subject,
- JAXRSConfiguration.tokenExpirationTime);
-
- logger.debug("Finished RefreshToken and new token has been generated.");
- return PICSUREResponse.success(Map.of(
- "token", refreshedToken,
- "expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString()
- ));
- }
-
-
-
- /**
- * @param inputMap
- * @return
- */
- private TokenInspection _inspectToken(Map inputMap){
- logger.debug("_inspectToken, the incoming token map is: {}", inputMap.entrySet()
- .stream()
- .map(entry -> entry.getKey() + " - " + entry.getValue())
- .collect(Collectors.joining(", ")));
-
- TokenInspection tokenInspection = new TokenInspection();
-
- String token = (String)inputMap.get("token");
-// logger.debug("getting token: " + token); <- in any cases, token should not be shown in log if the token could be a valid token.
- if (token == null || token.isEmpty()){
- logger.error("Token - "+ token + " is blank");
- tokenInspection.message = "Token not found";
- return tokenInspection;
- }
-
- // parse the token based on client secret
- // don't need to check if jws is null or not, since parse function has already checked
- Jws jws;
- try {
- jws = AuthUtils.parseToken(JAXRSConfiguration.clientSecret, token);
-
- /**
- * token has been verified, now we remove it from inputMap, so further logs will not be able to log
- * the token accidentally!
- */
- inputMap.remove("token");
- } catch (NotAuthorizedException ex) {
- // only when the token is for sure invalid, we can dump it into the log.
- logger.error("_inspectToken() the token - " + token + " - is invalid with exception: " + ex.getChallenges());
- tokenInspection.message = ex.getChallenges().toString();
- return tokenInspection;
- }
-
-
- Application application;
-
- try {
- application = (Application) securityContext.getUserPrincipal();
- } catch (ClassCastException ex){
- logger.error(securityContext.getUserPrincipal().getName()
- + " - " + securityContext.getUserPrincipal().getClass().getSimpleName() +
- " - is trying to use token introspection endpoint" +
- ", but it is not an application");
- throw new ApplicationException("The application token does not associate with an application but "
- + securityContext.getUserPrincipal().getClass().getSimpleName());
- }
-
- // application null check should be finished when application token goes through the JWTFilter authentication process,
- // here we just double check it to prevent a null application object goes further.
- if (application == null){
- logger.error("_inspectToken() There is no application in securityContext, which shall not be.");
- throw new ApplicationException("Inner application error, please ask admin to check the log.");
- }
-
- String subject = jws.getBody().getSubject();
-
- // get the user based on subject field in token
- User user;
-
- // check if the token is the special LONG_TERM_TOKEN,
- // the differences between this special token and normal token is
- // one user only has one long_term_token stored in database,
- // this token needs to be exactly the same as the database one.
- // If the token refreshed, the old one will be invalid. But normal
- // token will not invalid the old ones if refreshed.
- boolean isLongTermToken = false;
- if (subject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
- subject = subject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length()+1);
- isLongTermToken = true;
- }
-
- user = userRepo.getUniqueResultByColumn("subject", subject);
- logger.info("_inspectToken() user with subject - " + subject + " - exists in database");
- if (user == null) {
- logger.error("_inspectToken() could not find user with subject " + subject);
- tokenInspection.message = "user doesn't exist";
- return tokenInspection;
- }
-
-
-
- //Essentially we want to return jws.getBody() with an additional active: true field
- //only under certain circumstances, the token will return active
- boolean isAuthorizationPassed = false;
- String errorMsg = null;
-
- // long term token needs to be the same as the token in the database user table, if
- // not the token might has been compromised, which will not go through the authorization check
- boolean isLongTermTokenCompromised = false;
- if (isLongTermToken && !token.equals(user.getToken())) {
- // in long_term_token mode, the token needs to be exactly the same as the token in user table
- isLongTermTokenCompromised = true;
- logger.error("_inspectToken User " + user.getUuid() + "|" + user.getSubject()
- + "is sending a long term token that is not matching the record in database user table.");
- errorMsg = "Cannot find matched long term token, your token might have been refreshed.";
- }
-
- // we go through the authorization layer check only if we need to in order to improve the performance
- // the logic here, if the token associated with a user, we will start the authorization check.
- // If the current application has at least one privilege, the user must have one privilege associated to the application
- // pass the accessRule check if there is any accessRules associated with.
- if (application.getPrivileges() == null || application.getPrivileges().isEmpty()){
- // if no privileges associated
- isAuthorizationPassed = true;
- //we still want to log this, though.
- logger.info("ACCESS_LOG ___ " + user.getUuid() + "," + user.getEmail() + "," + user.getName() +
- " ___ has been granted access to execute query ___ " + inputMap.get("request") + " ___ in application ___ " + application.getName()
- + " ___ NO APP PRIVILEGES DEFINED");
- } else if ( !isLongTermTokenCompromised
- && user.getRoles() != null
- // The protocol between applications and PSAMA is application will
- // attach everything that needs to be verified in request field of inputMap
- // besides token. So here we should attach everything in request.
- && authorizationService.isAuthorized(application, inputMap.get("request"), user)) {
- isAuthorizationPassed = true;
- } else {
- // if isLongTermTokenCompromised flag is true,
- // the error message has already been set previously
- if (!isLongTermTokenCompromised)
- errorMsg = "User doesn't have enough privileges.";
- }
-
- if (isAuthorizationPassed){
- tokenInspection.responseMap.put("active", true);
- ArrayList roles = new ArrayList();
- for(Privilege p : user.getTotalPrivilege()) {
- roles.add(p.getName());
- }
- tokenInspection.responseMap.put("roles", String.join(",", roles));
- } else {
- if (errorMsg != null )
- tokenInspection.message = errorMsg;
- return tokenInspection;
- }
-
- tokenInspection.responseMap.putAll(jws.getBody());
-
- // attach all privileges associated with the application to the responseMap
- tokenInspection.responseMap.put("privileges", user.getPrivilegeNameSetByApplication(application));
-
-
- logger.info("_inspectToken() Successfully inspect and return response map: "
- + tokenInspection.responseMap.entrySet()
- .stream()
- .map(entry -> entry.getKey() + " - " + entry.getValue())
- .collect(Collectors.joining(", ")));
- return tokenInspection;
- }
-
- /**
- * inner used token introspection class with active:false included
- */
- private class TokenInspection {
- Map responseMap = new HashMap<>();
- String message = null;
-
- public TokenInspection() {
- responseMap.put("active", false);
- }
- }
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
similarity index 67%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
index eb9304b1f..532079cf1 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
@@ -6,14 +6,14 @@
import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponseOKwithMsgAndContent;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.*;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.*;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import edu.harvard.hms.dbmi.avillach.auth.service.MailService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.*;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ApplicationRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.MailService;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
import edu.harvard.hms.dbmi.avillach.auth.utils.JsonUtils;
import io.jsonwebtoken.Claims;
@@ -21,17 +21,17 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestHeader;
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.transaction.Transactional;
import javax.validation.constraints.NotNull;
-import javax.ws.rs.*;
-import javax.ws.rs.core.*;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@@ -44,56 +44,48 @@
* Endpoint for service handling business logic for users.
*/
@Api
-@Path("/user")
-public class UserService extends BaseEntityService {
-
- Logger logger = LoggerFactory.getLogger(UserService.class);
+@Controller("/user")
+public class UserController extends BaseEntityService {
- @Context
- SecurityContext securityContext;
+ Logger logger = LoggerFactory.getLogger(UserController.class);
- @Inject
- UserRepository userRepo;
+// @Context
+// SecurityContext securityContext;
- @Inject
- RoleRepository roleRepo;
+ private final RoleRepository roleRepo;
- @Inject
- PrivilegeRepository privilegeRepo;
+ private final ConnectionRepository connectionRepo;
- @Inject
- ConnectionRepository connectionRepo;
+ private final ApplicationRepository applicationRepo;
- @Inject
- ApplicationRepository applicationRepo;
+ private final AuthUtils authUtil;
- @Inject
- AccessRuleRepository accessruleRepo;
-
- @Inject
- AuthUtils authUtil;
-
- private MailService mailService = new MailService();
+ private MailService mailService;
- public UserService() {
+ @Autowired
+ public UserController(RoleRepository roleRepo, ConnectionRepository connectionRepo, ApplicationRepository applicationRepo, AuthUtils authUtil) {
super(User.class);
+ this.roleRepo = roleRepo;
+ this.connectionRepo = connectionRepo;
+ this.applicationRepo = applicationRepo;
+ this.authUtil = authUtil;
}
@ApiOperation(value = "GET information of one user with the UUID, requires ADMIN or SUPER_ADMIN roles")
@GET
@RolesAllowed({ADMIN, SUPER_ADMIN})
@Path("/{userId}")
- public Response getUserById(
- @ApiParam(required = true, value="The UUID of the user to fetch information about")
+ public ResponseEntity> getUserById(
+ @ApiParam(required = true, value = "The UUID of the user to fetch information about")
@PathParam("userId") String userId) {
- return getEntityById(userId,userRepo);
+ return getEntityById(userId, userRepo);
}
@ApiOperation(value = "GET a list of existing users, requires ADMIN or SUPER_ADMIN roles")
@GET
@RolesAllowed({ADMIN, SUPER_ADMIN})
@Path("")
- public Response getUserAll() {
+ public ResponseEntity> getUserAll() {
return getEntityAll(userRepo);
}
@@ -103,44 +95,46 @@ public Response getUserAll() {
@RolesAllowed({ADMIN})
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response addUser(
+ public ResponseEntity> addUser(
@ApiParam(required = true, value = "A list of user in JSON format")
- List users){
- User currentUser = (User)securityContext.getUserPrincipal();
- if (currentUser == null || currentUser.getUuid() == null){
+ List users) {
+ User currentUser = (User) securityContext.getUserPrincipal();
+ if (currentUser == null || currentUser.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
-
+
checkAssociation(users);
boolean allowAdd = true;
- for(User user : users) {
- logger.debug("Adding User " + user);
- if (!allowUpdateSuperAdminRole(currentUser, user, null)){
+ for (User user : users) {
+ logger.debug("Adding User " + user);
+ if (!allowUpdateSuperAdminRole(currentUser, user, null)) {
allowAdd = false;
break;
}
- if(user.getEmail() == null) {
- try {
- HashMap metadata = new HashMap(new ObjectMapper().readValue(user.getGeneralMetadata(), Map.class));
- List emailKeys = metadata.keySet().stream().filter((key)->{return key.toLowerCase().contains("email");}).collect(Collectors.toList());
- if(emailKeys.size()>0) {
- user.setEmail(metadata.get(emailKeys.get(0)));
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+ if (user.getEmail() == null) {
+ try {
+ HashMap metadata = new HashMap(new ObjectMapper().readValue(user.getGeneralMetadata(), Map.class));
+ List emailKeys = metadata.keySet().stream().filter((key) -> {
+ return key.toLowerCase().contains("email");
+ }).collect(Collectors.toList());
+ if (emailKeys.size() > 0) {
+ user.setEmail(metadata.get(emailKeys.get(0)));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
- if (allowAdd){
- Response updateResponse = addEntity(users, userRepo);
+ if (allowAdd) {
+ Response updateResponse = addEntity(users, userRepo);
sendUserUpdateEmailsFromResponse(updateResponse);
return updateResponse;
} else {
- logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles ["+ currentUser.getRoleString() + "] - is not allowed to grant "
+ logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant "
+ AuthNaming.AuthRoleNaming.SUPER_ADMIN + " role when adding a user.");
throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to add a user with a " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege associated.");
}
@@ -152,9 +146,9 @@ public Response addUser(
@RolesAllowed({ADMIN})
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
- public Response updateUser(List users){
- User currentUser = (User)securityContext.getUserPrincipal();
- if (currentUser == null || currentUser.getUuid() == null){
+ public ResponseEntity> updateUser(List users) {
+ User currentUser = (User) securityContext.getUserPrincipal();
+ if (currentUser == null || currentUser.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
@@ -165,7 +159,7 @@ public Response updateUser(List users){
for (User user : users) {
User originalUser = userRepo.getById(user.getUuid());
- if (allowUpdateSuperAdminRole(currentUser, user, originalUser)){
+ if (allowUpdateSuperAdminRole(currentUser, user, originalUser)) {
continue;
} else {
allowUpdate = false;
@@ -173,54 +167,53 @@ public Response updateUser(List users){
}
}
- if (allowUpdate){
+ if (allowUpdate) {
Response updateResponse = updateEntity(users, userRepo);
sendUserUpdateEmailsFromResponse(updateResponse);
return updateResponse;
- }
- else {
- logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles ["+ currentUser.getRoleString() + "] - is not allowed to grant or remove "
+ } else {
+ logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant or remove "
+ AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to update a user with changes associated to " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
}
}
private void sendUserUpdateEmailsFromResponse(Response updateResponse) {
- logger.debug("Sending email");
- try {
- Object entity = updateResponse.getEntity();
- if(entity != null && entity instanceof PICSUREResponseOKwithMsgAndContent) {
- PICSUREResponseOKwithMsgAndContent okResponse = (PICSUREResponseOKwithMsgAndContent)entity;
- List addedUsers = (List) okResponse.getContent();
- String message = okResponse.getMessage();
- for(User user : addedUsers) {
- try {
- mailService.sendUsersAccessEmail(user);
- } catch (MessagingException e) {
- logger.error("Failed to send email! " + e.getLocalizedMessage());
- logger.debug("Exception Trace: ", e);
- okResponse.setMessage(message + " WARN - could not send email to user " + user.getEmail() + " see logs for more info");
- }
- }
- }
- } catch (Exception e) {
- logger.error("Failed to send email - unhandled exception: ", e);
- }
- logger.debug("finished email sending method");
- }
-
- /**
+ logger.debug("Sending email");
+ try {
+ Object entity = updateResponse.getEntity();
+ if (entity != null && entity instanceof PICSUREResponseOKwithMsgAndContent) {
+ PICSUREResponseOKwithMsgAndContent okResponse = (PICSUREResponseOKwithMsgAndContent) entity;
+ List addedUsers = (List) okResponse.getContent();
+ String message = okResponse.getMessage();
+ for (User user : addedUsers) {
+ try {
+ mailService.sendUsersAccessEmail(user);
+ } catch (MessagingException e) {
+ logger.error("Failed to send email! " + e.getLocalizedMessage());
+ logger.debug("Exception Trace: ", e);
+ okResponse.setMessage(message + " WARN - could not send email to user " + user.getEmail() + " see logs for more info");
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Failed to send email - unhandled exception: ", e);
+ }
+ logger.debug("finished email sending method");
+ }
+
+ /**
* This check is to prevent non-super-admin user to create/remove a super admin role
* against a user(include themselves). Only super admin user could perform such actions.
*
*
- * if operations not related to super admin role updates, this will return true.
+ * if operations not related to super admin role updates, this will return true.
*
- *
+ *
* The logic here is checking the state of the super admin role in the input and output users,
* if the state is changed, check if the user is a super admin to determine if the user could perform the action.
*
- * @param currentUser the user trying to perform the action
+ * @param currentUser the user trying to perform the action
* @param inputUser
* @param originalUser there could be no original user when adding a new user
* @return
@@ -228,11 +221,11 @@ private void sendUserUpdateEmailsFromResponse(Response updateResponse) {
private boolean allowUpdateSuperAdminRole(
@NotNull User currentUser,
@NotNull User inputUser,
- User originalUser){
+ User originalUser) {
// if current user is a super admin, this check will return true
for (Role role : currentUser.getRoles()) {
- for (Privilege privilege : role.getPrivileges()){
+ for (Privilege privilege : role.getPrivileges()) {
if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
return true;
}
@@ -242,19 +235,19 @@ private boolean allowUpdateSuperAdminRole(
boolean inputUserHasSuperAdmin = false;
boolean originalUserHasSuperAdmin = false;
- for (Role role : inputUser.getRoles()){
- for (Privilege privilege : role.getPrivileges()){
- if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)){
+ for (Role role : inputUser.getRoles()) {
+ for (Privilege privilege : role.getPrivileges()) {
+ if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
inputUserHasSuperAdmin = true;
break;
}
}
}
- if (originalUser != null){
- for (Role role : originalUser.getRoles()){
- for (Privilege privilege : role.getPrivileges()){
- if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)){
+ if (originalUser != null) {
+ for (Role role : originalUser.getRoles()) {
+ for (Privilege privilege : role.getPrivileges()) {
+ if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
originalUserHasSuperAdmin = true;
break;
}
@@ -283,18 +276,18 @@ private boolean allowUpdateSuperAdminRole(
@Transactional
@GET
@Path("/me")
- public Response getCurrentUser(
- @Context HttpHeaders httpHeaders,
+ public ResponseEntity> getCurrentUser(
+ @RequestHeader("Authorization") String authorizationHeader,
@ApiParam(required = false, value = "Attribute that represents if a long term token will attach to the response")
- @QueryParam("hasToken") Boolean hasToken){
+ @QueryParam("hasToken") Boolean hasToken) {
User user = (User) securityContext.getUserPrincipal();
- if (user == null || user.getUuid() == null){
+ if (user == null || user.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
user = userRepo.getById(user.getUuid());
- if (user == null){
+ if (user == null) {
logger.error("When retrieving current user, it returned null");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
@@ -309,13 +302,13 @@ public Response getCurrentUser(
// We are expecting the queryScope string as plain string. If it is a JSON, we could change the
// code to use JsonUtils.mergeTemplateMap(Map, Map)
Set privileges = user.getTotalPrivilege();
- if (privileges != null && !privileges.isEmpty()){
+ if (privileges != null && !privileges.isEmpty()) {
Set scopes = new TreeSet<>();
- privileges.stream().filter(privilege -> privilege.getQueryScope() !=null).forEach(privilege -> {
+ privileges.stream().filter(privilege -> privilege.getQueryScope() != null).forEach(privilege -> {
try {
Arrays.stream(objectMapper.readValue(privilege.getQueryScope(), String[].class))
- .filter(x -> x!=null)
- .forEach(scopeList-> scopes.addAll(Arrays.asList(scopeList)));
+ .filter(x -> x != null)
+ .forEach(scopeList -> scopes.addAll(Arrays.asList(scopeList)));
} catch (IOException e) {
logger.error("Parsing issue for privilege " + privilege.getUuid() + " queryScope", e);
}
@@ -323,12 +316,12 @@ public Response getCurrentUser(
userForDisplay.setQueryScopes(scopes);
}
- if (hasToken!=null){
+ if (hasToken != null) {
- if (user.getToken() != null && !user.getToken().isEmpty()){
+ if (user.getToken() != null && !user.getToken().isEmpty()) {
userForDisplay.setToken(user.getToken());
} else {
- user.setToken(generateUserLongTermToken(httpHeaders));
+ user.setToken(generateUserLongTermToken(authorizationHeader));
userRepo.merge(user);
userForDisplay.setToken(user.getToken());
}
@@ -341,30 +334,30 @@ public Response getCurrentUser(
@Transactional
@GET
@Path("/me/queryTemplate/{applicationId}")
- public Response getQueryTemplate(
+ public ResponseEntity> getQueryTemplate(
@ApiParam(value = "Application Id for the returning queryTemplate")
- @PathParam("applicationId") String applicationId){
+ @PathParam("applicationId") String applicationId) {
- if (applicationId == null || applicationId.trim().isEmpty()){
+ if (applicationId == null || applicationId.trim().isEmpty()) {
logger.error("getQueryTemplate() input application UUID is null or empty.");
throw new ProtocolException("Input application UUID is incorrect.");
}
User user = (User) securityContext.getUserPrincipal();
- if (user == null || user.getUuid() == null){
+ if (user == null || user.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
user = userRepo.getById(user.getUuid());
- if (user == null){
+ if (user == null) {
logger.error("When retrieving current user, it returned null");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
Application application = applicationRepo.getById(UUID.fromString(applicationId));
- if (application == null){
+ if (application == null) {
logger.error("getQueryTemplate() cannot find corresponding application by UUID: " + applicationId);
throw new ProtocolException("Cannot find application by input UUID: " + applicationId);
}
@@ -378,7 +371,7 @@ public Response getQueryTemplate(
@Transactional
@GET
@Path("/me/queryTemplate")
- public Response getQueryTemplate(){
+ public ResponseEntity> getQueryTemplate() {
return getQueryTemplate(JAXRSConfiguration.defaultApplicationUUID);
}
@@ -386,16 +379,16 @@ public Response getQueryTemplate(){
private String mergeTemplate(User user, Application application) {
String resultJSON = null;
Map mergedTemplateMap = null;
- for (Privilege privilege : user.getPrivilegesByApplication(application)){
+ for (Privilege privilege : user.getPrivilegesByApplication(application)) {
String template = privilege.getQueryTemplate();
- logger.debug("mergeTemplate() processing template:"+template);
- if (template == null || template.trim().isEmpty()){
+ logger.debug("mergeTemplate() processing template:" + template);
+ if (template == null || template.trim().isEmpty()) {
continue;
}
Map templateMap = null;
try {
templateMap = objectMapper.readValue(template, Map.class);
- } catch (IOException ex){
+ } catch (IOException ex) {
logger.error("mergeTemplate() cannot convert stored queryTemplate using Jackson, the queryTemplate is: " + template);
throw new ApplicationException("Inner application error, please contact admin.");
}
@@ -404,7 +397,7 @@ private String mergeTemplate(User user, Application application) {
continue;
}
- if (mergedTemplateMap == null){
+ if (mergedTemplateMap == null) {
mergedTemplateMap = templateMap;
continue;
}
@@ -437,18 +430,18 @@ private String mergeTemplate(User user, Application application) {
@Transactional
@GET
@Path("/me/refresh_long_term_token")
- public Response refreshUserToken(
- @Context HttpHeaders httpHeaders,
+ public ResponseEntity> refreshUserToken(
+ @RequestHeader HttpHeaders httpHeaders,
@ApiParam(required = false, value = "A flag represents if the long term token will be returned or not")
- @QueryParam("hasToken") Boolean hasToken){
+ @QueryParam("hasToken") Boolean hasToken) {
User user = (User) securityContext.getUserPrincipal();
- if (user == null || user.getUuid() == null){
+ if (user == null || user.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
user = userRepo.getById(user.getUuid());
- if (user == null){
+ if (user == null) {
logger.error("When retrieving current user, it returned null");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
}
@@ -465,31 +458,31 @@ public Response refreshUserToken(
* Logic here is, retrieve the subject of the user from httpHeader. Then generate a long term one
* with LONG_TERM_TOKEN_PREFIX| in front of the subject to be able to distinguish with regular ones, since
* long term token only generated for accessing certain things to, in some degrees, decrease the insecurity.
- * @param httpHeaders
- * @return
- * @throws ProtocolException
+ *
+ * @param authorizationHeader the authorization header
+ * @return the long term token
+ * @throws IllegalArgumentException if the authorization header is not presented
*/
- private String generateUserLongTermToken(HttpHeaders httpHeaders) throws ProtocolException{
- Jws jws;
- try {
- jws = AuthUtils.parseToken(clientSecret,
- // the original token should be able to grab from header, otherwise, it should be stopped
- // at JWTFilter level
- httpHeaders.getHeaderString(HttpHeaders.AUTHORIZATION)
- .substring(6)
- .trim());
- } catch (NotAuthorizedException ex) {
- throw new ProtocolException("Cannot parse token in header");
+ private String generateUserLongTermToken(String authorizationHeader) {
+ if (!StringUtils.isNotBlank(authorizationHeader)) {
+ throw new IllegalArgumentException("Authorization header is not presented.");
+ }
+
+ Optional token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader);
+ if (token.isEmpty()) {
+ throw new IllegalArgumentException("Token is not presented in the authorization header.");
}
+ Jws jws = JWTUtil.parseToken(token.get());
+
Claims claims = jws.getBody();
String tokenSubject = claims.getSubject();
- if (tokenSubject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX+"|")) {
+ if (tokenSubject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX + "|")) {
// considering the subject already contains a "|"
// to prevent infinitely adding the long term token prefix
// we will grab the real subject here
- tokenSubject = tokenSubject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length()+1);
+ tokenSubject = tokenSubject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
}
return JWTUtil.createJwtToken(clientSecret,
@@ -509,16 +502,15 @@ private String generateUserLongTermToken(HttpHeaders httpHeaders) throws Protoco
* @param users
* @return
*/
- private void checkAssociation(List users) throws ProtocolException{
-
- for (User user: users){
- if (user.getRoles() != null){
+ private void checkAssociation(List users) {
+ for (User user : users) {
+ if (user.getRoles() != null) {
Set roles = new HashSet<>();
user.getRoles().stream().forEach(t -> roleRepo.addObjectToSet(roles, roleRepo, t));
user.setRoles(roles);
}
- if (user.getConnection() != null){
+ if (user.getConnection() != null) {
Connection connection = connectionRepo.getUniqueResultByColumn("id", user.getConnection().getId());
user.setConnection(connection);
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
similarity index 77%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
index d4ae09cc0..6c05afaf9 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
@@ -1,13 +1,14 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.UserMetadataMapping;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserMetadataMappingRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.BaseEntityService;
-import edu.harvard.hms.dbmi.avillach.auth.service.UserMetadataMappingService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserMetadataMappingRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserMetadataMappingService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import org.springframework.http.ResponseEntity;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
@@ -26,9 +27,9 @@
*/
@Api
@Path("mapping")
-public class UserMetadataMappingWebService extends BaseEntityService{
+public class UserMetadataMappingWebController extends BaseEntityService{
- public UserMetadataMappingWebService() {
+ public UserMetadataMappingWebController() {
super(UserMetadataMapping.class);
}
@@ -46,7 +47,7 @@ public UserMetadataMappingWebService() {
@Produces("application/json")
@RolesAllowed({ADMIN, SUPER_ADMIN})
@Path("{connectionId}")
- public Response getMappingsForConnection(@PathParam("connectionId") String connection) {
+ public ResponseEntity> getMappingsForConnection(@PathParam("connectionId") String connection) {
return Response.ok(mappingService.
getAllMappingsForConnection(connectionRepo
.getUniqueResultByColumn("id", connection)))
@@ -57,7 +58,7 @@ public Response getMappingsForConnection(@PathParam("connectionId") String conne
@GET
@Produces("application/json")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- public Response getAllMappings() {
+ public ResponseEntity> getAllMappings() {
return Response.ok(mappingService.getAllMappings()).build();
}
@@ -67,7 +68,7 @@ public Response getAllMappings() {
@Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed({SUPER_ADMIN})
@Path("/")
- public Response addMapping(
+ public ResponseEntity>Entity> addMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping in JSON format")
List mappings) {
return mappingService.addMappings(mappings);
@@ -78,7 +79,7 @@ public Response addMapping(
@Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed({SUPER_ADMIN})
@Path("/")
- public Response updateMapping(
+ public ResponseEntity> updateMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping with fields to be updated in JSON format")
List mappings) {
return updateEntity(mappings, mappingRepo);
@@ -89,7 +90,7 @@ public Response updateMapping(
@DELETE
@RolesAllowed({SUPER_ADMIN})
@Path("/{mappingId}")
- public Response removeById(
+ public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid UserMetadataMapping Id")
@PathParam("mappingId") final String mappingId) {
return removeEntityById(mappingId, mappingRepo);
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
index 5a6245a26..31fb8584f 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
@@ -1,49 +1,49 @@
-package edu.harvard.hms.dbmi.avillach.auth.security;
-
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-
-import javax.ws.rs.core.SecurityContext;
-import java.security.Principal;
-
-/**
- * Implements the SecurityContext interface for JWTFilter to use.
- */
-public class AuthSecurityContext implements SecurityContext {
-
- private User user;
- private Application application;
- private String scheme;
-
- public AuthSecurityContext(User user, String scheme) {
- this.user = user;
- this.scheme = scheme;
- }
-
- public AuthSecurityContext(Application application, String scheme) {
- this.application = application;
- this.scheme = scheme;
- }
-
- @Override
- public Principal getUserPrincipal() {
- return this.user == null ? this.application : this.user;
- }
-
- @Override
- public boolean isUserInRole(String role) {
- if (user.getRoles() != null)
- return user.getPrivilegeNameSet().contains(role);
- return false;
- }
-
- @Override
- public boolean isSecure() {
- return "https".equals(this.scheme);
- }
-
- @Override
- public String getAuthenticationScheme() {
- return SecurityContext.DIGEST_AUTH;
- }
-}
+//package edu.harvard.hms.dbmi.avillach.auth.security;
+//
+//import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
+//import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+//
+//import javax.ws.rs.core.SecurityContext;
+//import java.security.Principal;
+//
+///**
+// * Implements the SecurityContext interface for JWTFilter to use.
+// */
+//public class AuthSecurityContext implements SecurityContext {
+//
+// private User user;
+// private Application application;
+// private String scheme;
+//
+// public AuthSecurityContext(User user, String scheme) {
+// this.user = user;
+// this.scheme = scheme;
+// }
+//
+// public AuthSecurityContext(Application application, String scheme) {
+// this.application = application;
+// this.scheme = scheme;
+// }
+//
+// @Override
+// public Principal getUserPrincipal() {
+// return this.user == null ? this.application : this.user;
+// }
+//
+// @Override
+// public boolean isUserInRole(String role) {
+// if (user.getRoles() != null)
+// return user.getPrivilegeNameSet().contains(role);
+// return false;
+// }
+//
+// @Override
+// public boolean isSecure() {
+// return "https".equals(this.scheme);
+// }
+//
+// @Override
+// public String getAuthenticationScheme() {
+// return SecurityContext.DIGEST_AUTH;
+// }
+//}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/JWTFilter.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/JWTFilter.java
deleted file mode 100755
index 89e99ce6d..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/JWTFilter.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.security;
-
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ApplicationRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.TOSService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jws;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.ws.rs.NotAuthorizedException;
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.ResourceInfo;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-import javax.ws.rs.ext.Provider;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * The main gate for PSAMA that filters all incoming requests against PSAMA.
- * Design Logic
- *
- * - All incoming requests pass through this filter.
- * - To pass this filter, the incoming request needs a valid bearer token in its HTTP Authorization Header
- * to represent a valid identity behind the token.
- * - In some cases, the incoming request doesn't need to hold a token. For example, when the request is to the
authentication
- * endpoint, swagger.json
, or swagger.html
.
- *
- */
-@Provider
-public class JWTFilter implements ContainerRequestFilter {
-
- @Context
- private UriInfo uriInfo;
-
- Logger logger = LoggerFactory.getLogger(JWTFilter.class);
-
- @Context
- ResourceInfo resourceInfo;
-
- @Inject
- UserRepository userRepo;
-
- @Inject
- ApplicationRepository applicationRepo;
-
- @Inject
- TOSService tosService;
-
- @Override
- public void filter(ContainerRequestContext requestContext) throws IOException {
- logger.debug("starting...");
- logger.debug("For path:{} and requested URI: {}", uriInfo.getPath(), uriInfo.getRequestUri());
-
- /**
- * skip the filter in certain cases
- */
- if (uriInfo.getPath().endsWith("authentication")
- || uriInfo.getPath().endsWith("/swagger.yaml")
- || uriInfo.getPath().endsWith("/swagger.json")) {
- return;
- }
-
- try {
- String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
- if (authorizationHeader == null || authorizationHeader.isEmpty()) {
- throw new NotAuthorizedException("No authorization header found.");
- }
- String token = authorizationHeader.substring(6).trim();
- logger.debug(" token: {}", token);
-
- String userForLogging = null;
- Jws jws = parseToken(token);
- String userId = jws.getBody().get(JAXRSConfiguration.userIdClaim, String.class);
-
- if(userId.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
- // For profile information, we do indeed allow long term token
- // to be a valid token.
- if (uriInfo.getPath().startsWith("/user/me")) {
- // Get the subject claim, remove the LONG_TERM_TOKEN_PREFIX, and use that String value to
- // look up the existing user.
- String realClaimsSubject = jws.getBody().getSubject().substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length()+1);
- setSecurityContextForUser(requestContext, realClaimsSubject);
- } else {
- logger.error("the long term token with subject, {}, cannot access to PSAMA.", userId);
- throw new NotAuthorizedException("Long term tokens cannot be used to access to PSAMA.");
- }
- /**
- * authenticate if a long term token is presented.
- * PSAMA activities should not be able to execute under this token.
- * Normal admin should access psama through psamaui, the token will be expired
- * in a certain time.
- * If super admin want to access the psama through APIs, the token should
- * be grabbed from psamaui as well to prevent the token leakage.
- */
-
-
- } else if(userId.startsWith(AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX)) {
- logger.debug(" userId starts with PSAMA_APPLICATION_TOKEN_PREFIX");
-
- /**
- * authenticate as Application, we might need to extract this blob out to an separate function
- */
-
- if( ! uriInfo.getPath().endsWith("token/inspect")) {
- logger.error(userId + " attempted to perform request " + uriInfo.getPath() + " token may be compromised.");
- throw new NotAuthorizedException("User is deactivated");
- }
- Application authenticatedApplication = applicationRepo.getById(UUID.fromString(userId.split("\\|")[1]));
- if (authenticatedApplication == null){
- logger.error("Cannot find an application by userId: " + userId);
- throw new NotAuthorizedException("Your token doesn't contain valid identical information, please contact admin.");
- }
-
- if (!authenticatedApplication.getToken().equals(token)) {
- logger.error("filter() incoming application token - " + token +
- " - is not the same as record, might because the token has been refreshed. Subject: " + userId);
- throw new NotAuthorizedException("Your token has been inactivated, please contact admin to grab you the latest one.");
- }
-
- requestContext.setSecurityContext(new AuthSecurityContext(authenticatedApplication,
- uriInfo.getRequestUri().getScheme()));
- } else {
- logger.debug(" userId is not longterm and not psamaapp either.");
- /**
- * authenticate as User
- */
- setSecurityContextForUser(requestContext, jws.getBody().getSubject());
-
- }
-
- } catch (NotAuthorizedException e) {
- // the detail of this exception should be logged right before the exception thrown out
- // logger.error("User - " + userForLogging + " - is not authorized. " + e.getChallenges());
- // we should show different response based on role
- requestContext.abortWith(PICSUREResponse.unauthorizedError("User is not authorized. " + e.getChallenges()));
- } catch (ApplicationException e){
- // we should show different response based on role
- e.printStackTrace();
- requestContext.abortWith(PICSUREResponse.applicationError(e.getContent()));
- } catch (Exception e) {
- e.printStackTrace();
- requestContext.abortWith(PICSUREResponse.applicationError(e.getMessage()));
- }
- logger.debug("finished.");
- }
-
- /**
- * Validate the user information, stored in the token, that was passed in the HTTPRequest header.
- *
- * @param requestContext The request that has the information
- * @param token The token to be parsed
- * @param jws The claims stored in the token
- * @return
- */
- private void setSecurityContextForUser(ContainerRequestContext requestContext, String claimsSubject) throws NotAuthorizedException {
- logger.debug("starting...");
- String userForLogging;
-
- // Find the user when checking the claims
- User authenticatedUser = userRepo.findBySubject(claimsSubject);
-
- if (authenticatedUser == null) {
- logger.error("Cannot validate user claims, based on information stored in the JWT token.");
- throw new NotAuthorizedException("Cannot validate user claims, based on information stored in the JWT token.");
- }
- // Check whether user is active
- if (!authenticatedUser.isActive()) {
- logger.warn("User with ID: " + authenticatedUser.getUuid() + " is deactivated.");
- throw new NotAuthorizedException("User is deactivated");
- }
-
- /**
- * This TOSService code will hit to the database to retrieve a user once again
- */
- if (!uriInfo.getPath().contains("/tos")){
- if (JAXRSConfiguration.tosEnabled.startsWith("true") && tosService.getLatest() != null && !tosService.hasUserAcceptedLatest(authenticatedUser.getSubject())){
- //If user has not accepted terms of service and is attempted to get information other than the terms of service, don't authenticate
- requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("User must accept terms of service").build());
- return;
- }
- }
-
- // currently only user id will be logged, in the future, it might contain roles and other information,
- // like xxxuser|roles|otherInfo
- userForLogging = authenticatedUser.getSubject();
- // check authorization of the authenticated user
- checkRoles(authenticatedUser, resourceInfo
- .getResourceMethod().isAnnotationPresent(RolesAllowed.class)
- ? resourceInfo.getResourceMethod().getAnnotation(RolesAllowed.class).value()
- : new String[]{});
-
- logger.info("User - {} - has just passed all the authentication and authorization layers.", userForLogging);
-
- requestContext.setSecurityContext(new AuthSecurityContext(authenticatedUser,
- uriInfo.getRequestUri().getScheme()));
- }
-
- /**
- * check if user contains the input list of roles
- *
- * @param authenticatedUser
- * @param rolesAllowed
- * @return
- */
- private boolean checkRoles(User authenticatedUser, String[] rolesAllowed) throws NotAuthorizedException{
-
- String logMsg = "The roles of the user - id: " + authenticatedUser.getSubject() + " - "; //doesn't match the required restrictions";
- boolean b = true;
- if (rolesAllowed.length < 1) {
- return true;
- }
-
-
- if (authenticatedUser.getRoles() == null) {
- logger.error(logMsg + "is null.");
- throw new NotAuthorizedException("user doesn't have an assigned role.");
- }
-
- Set privilegeNameSet = authenticatedUser.getPrivilegeNameSet();
- if (privilegeNameSet.isEmpty()){
- logger.error(logMsg + "doesn't have privileges associated.");
- throw new NotAuthorizedException("user doesn't have roles or privileges, please contact admin.");
- }
-
- boolean isAuthorized = false;
- for (String role : rolesAllowed) {
- if(privilegeNameSet.contains(role.trim())) {
- isAuthorized = true;
- break;
- }
- }
- if(!isAuthorized) {
- logger.error(logMsg + "doesn't match the required role/privilege restrictions, privileges from user: "
- + authenticatedUser.getPrivilegeString() + ", priviliges required: " + Arrays.toString(rolesAllowed));
- throw new NotAuthorizedException("doesn't match the required role restrictions.");
- }
- return b;
- }
-
- private Jws parseToken(String token) {
- Jws jws = AuthUtils.parseToken(JAXRSConfiguration.clientSecret, token);
- return jws;
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
new file mode 100644
index 000000000..110755bb5
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
@@ -0,0 +1,39 @@
+package edu.harvard.hms.dbmi.avillach.auth.security;
+
+import edu.harvard.hms.dbmi.avillach.auth.filter.JWTFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.web.SecurityFilterChain;
+
+import static org.springframework.security.config.Customizer.withDefaults;
+
+@Configuration
+public class SecurityConfig {
+
+ private final JWTFilter jwtFilter;
+
+ @Autowired
+ public SecurityConfig(JWTFilter jwtFilter) {
+ this.jwtFilter = jwtFilter;
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests((authz) ->
+ authz.anyRequest().authenticated()
+ )
+ .addFilter()
+ .httpBasic(withDefaults());
+
+ return http.build();
+ }
+
+ @Bean
+ public WebSecurityCustomizer webSecurityCustomizer() {
+ return (web) -> web.ignoring().requestMatchers("/actuator/health", "/actuator/info", "/authentication/**", "/swagger.yaml", "/swagger.json");
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/OauthUserMatchingService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/OauthUserMatchingService.java
deleted file mode 100644
index a56946fc8..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/OauthUserMatchingService.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.jayway.jsonpath.*;
-
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.UserMetadataMapping;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.rest.UserService;
-
-/**
- * Matches users created by admins with user profiles created by a 3rd party Oauth provider.
- */
-public class OauthUserMatchingService {
-
- @Inject
- UserRepository userRepo;
-
- @Inject
- UserService userService;
-
- @Inject
- UserMetadataMappingService mappingService;
-
- @Inject
- ConnectionRepository connectionRepo;
-
- private Logger logger = LoggerFactory.getLogger(OauthUserMatchingService.class);
-
- private ObjectMapper mapper = new ObjectMapper();
-
- /*public String mockAuthAPIUserInfo(String accessToken) {
- Map map = Map.of("ldap-connector-access-token",
- "{ \"name\": \"Guy,Some\", \"family_name\": \"Guy\", \"given_name\": \"Some\", \"nickname\": \"CH000000000\", \"groups\": [], \"emails\": [ \"foo@childrens.harvard.edu\" ], \"dn\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"distinguishedName\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"organizationUnits\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"email\": \"foo@childrens.harvard.edu\", \"updated_at\": \"2018-10-04T18:28:23.371Z\", \"picture\": \"https://s.gravatar.com/avatar/blablablablablablablablablablablabla?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fsp.png\", \"user_id\": \"ad|ldap-connector|blablablablablablablablablablablabla\", \"identities\": [ { \"user_id\": \"ldap-connector|blablablablablablablablablablablabla\", \"provider\": \"ad\", \"connection\": \"ldap-connector\", \"isSocial\": false } ], \"created_at\": \"2018-01-26T14:06:50.413Z\", \"username\": \"CH0000000\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.32\", \"last_login\": \"2018-10-04T18:28:23.091Z\", \"logins_count\": 399, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "github-access-token", " { \"email\": \"blablabla@gmail.com\", \"name\": \"Some Girl\", \"picture\": \"https://avatars3.githubusercontent.com/u/0000000000?v=4\", \"nickname\": \"blablabla\", \"gravatar_id\": \"\", \"url\": \"https://api.github.com/users/blablabla\", \"html_url\": \"https://github.com/blablabla\", \"followers_url\": \"https://api.github.com/users/blablabla/followers\", \"following_url\": \"https://api.github.com/users/blablabla/following{/other_user}\", \"gists_url\": \"https://api.github.com/users/blablabla/gists{/gist_id}\", \"starred_url\": \"https://api.github.com/users/blablabla/starred{/owner}{/repo}\", \"subscriptions_url\": \"https://api.github.com/users/blablabla/subscriptions\", \"organizations_url\": \"https://api.github.com/users/blablabla/orgs\", \"repos_url\": \"https://api.github.com/users/blablabla/repos\", \"events_url\": \"https://api.github.com/users/blablabla/events{/privacy}\", \"received_events_url\": \"https://api.github.com/users/blablabla/received_events\", \"type\": \"User\", \"site_admin\": false, \"location\": \"Nowhere, USA\", \"hireable\": true, \"public_repos\": 8, \"public_gists\": 0, \"followers\": 3, \"following\": 1, \"updated_at\": \"2018-09-20T18:47:43.703Z\", \"emails\": [ { \"email\": \"blablabla@gmail.com\", \"primary\": true, \"verified\": true, \"visibility\": \"public\" }, { \"email\": \"blablabla@users.noreply.github.com\", \"primary\": false, \"verified\": true, \"visibility\": null } ], \"email_verified\": true, \"user_id\": \"github|0000000\", \"identities\": [ { \"provider\": \"github\", \"user_id\": 000000000, \"connection\": \"github\", \"isSocial\": true } ], \"created_at\": \"2016-10-22T22:38:20.437Z\", \"blog\": \"\", \"node_id\": \"blablabla=\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.198\", \"last_login\": \"2018-09-20T18:47:43.491Z\", \"logins_count\": 71, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "nih-gov-prod-access-token"," { \"email\": \"NOBODY\", \"sessionIndex\": \"blablablabla\", \"UserPrincipalName\": \"\", \"Mail\": \"\", \"FirstName\": \"\", \"LastName\": \"\", \"MiddleName\": \"\", \"NEDID\": \"\", \"nameIdAttributes\": { \"value\": \"NOBODY\", \"Format\": \"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" }, \"authenticationmethod\": \"urn:oasis:names:tc:SAML:2.0:ac:classes:Password\", \"issuer\": \"https://auth.nih.gov/IDP\", \"updated_at\": \"2018-07-23T19:32:51.505Z\", \"name\": \"\", \"picture\": \"https://cdn.auth0.com/avatars/default.png\", \"user_id\": \"samlp|NOBODY\", \"nickname\": \"\", \"identities\": [ { \"user_id\": \"NOBODY\", \"provider\": \"samlp\", \"connection\": \"nih-gov-prod\", \"isSocial\": false } ], \"created_at\": \"2018-04-02T13:10:25.654Z\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.195\", \"last_login\": \"2018-07-23T19:32:51.254Z\", \"logins_count\": 12, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "no-mapping-connection-token"," { \"email\": \"foo@bar.com\", \"UserName\": \"foooo\", \"FirstName\": \"foo\", \"LastName\": \"oooo\",\"user_id\": \"samlp|fooBar\", \"identities\": [ { \"user_id\": \"fooBar\", \"provider\": \"samlp\", \"connection\": \"no-mapping-connection\", \"isSocial\": false } ]}",
- "invalid-path-token"," { \"email\": \"bar@foo.com\", \"UserName\": \"bahh\", \"user_id\": \"samlp|barFoo\", \"identities\": [ { \"user_id\": \"barFoo\", \"provider\": \"samlp\", \"connection\": \"invalid-path\", \"isSocial\": true } ]}",
- "no-user-token"," { \"email\": \"no@user.com\", \"UserName\": \"nooooooo\", \"user_id\": \"samlp|noUser\", \"identities\": [ { \"user_id\": \"noUser\", \"provider\": \"samlp\", \"connection\": \"no-user-connection\", \"isSocial\": false } ]}"
- );
- return map.get(accessToken);
- }*/
-
-
- /**
- * Retrieve a user profile by access_token and match it to a pre-created user in the database using
- * pre-configured matching rules.
- *
- * @param userInfo UserInfo returned from auth0
- * @return The user that was matched or null if no match was possible.
- */
- public User matchTokenToUser(JsonNode userInfo) {
- // This retrieves a map of UserInfo as JSON.
- try {
- String userInfoString = mapper.writeValueAsString(userInfo);
- logger.info("Attempting to find match for user with info: " + userInfo);
-
- //Parse this once so it doesn't get re-parsed every time we read from it
- Configuration conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL).addOptions(Option.ALWAYS_RETURN_LIST);
- Object parsedInfo = conf.jsonProvider().parse(userInfoString);
- //Return lists or null so that we don't have to worry about whether it's a single object or an array, or catch errors
- List connections = JsonPath.using(conf).parse(parsedInfo).read("$.identities[0].connection");
- String connectionId = connections.get(0);
-
- Connection connection = connectionRepo.getUniqueResultByColumn("id", connectionId);
-
- List mappings = mappingService.getAllMappingsForConnection(connection);
-
- if (mappings == null || mappings.isEmpty()) {
- //We don't have any mappings for this connection yet
- logger.warn("Unable to find user metadata mappings for connectionId " + connection);
- return null;
- }
-
- //We only care about unmatched users
- List users = userRepo.listUnmatchedByConnectionId(connection);
- if (users == null || users.isEmpty()) {
- logger.info("No unmatched users exist with connectionId " + connection);
- return null;
- }
- for (UserMetadataMapping umm : mappings) {
- List auth0values = JsonPath.using(conf).parse(parsedInfo).read(umm.getAuth0MetadataJsonPath());
- if (auth0values == null || auth0values.isEmpty()) {
- //Well, nothing found, let's move on.
- logger.info("Fetched data has no value at " + umm.getAuth0MetadataJsonPath());
- break;
- }
- String auth0value = auth0values.get(0);
- for (User u : users) {
- List values = null;
- try{
- values = JsonPath.using(conf).parse(u.getGeneralMetadata()).read(umm.getGeneralMetadataJsonPath());
- } catch (JsonPathException e) {
- logger.warn("User " + u.getUuid() + " has invalid general metadata: " + u.getGeneralMetadata());
- continue;
- }
- if (values == null || values.isEmpty()) {
- logger.warn("User " + u.getUuid() + " has no value at " + umm.getGeneralMetadataJsonPath());
- continue;
- }
- String generalValue = values.get(0);
- if (auth0value.equalsIgnoreCase(generalValue)) {
- //Match found!!
- String userId = JsonPath.read(parsedInfo, "$.user_id");
- logger.info("Matching user with user_id " + userId);
- u.setAuth0metadata(userInfoString);
- u.setMatched(true);
- u.setSubject(userId);
- userService.updateEntity(Arrays.asList(u), userRepo);
- return u;
- }
- }
- }
- } catch (JsonProcessingException e ){
- logger.error("Unable to read UserInfo");
- }
- //No user found
- logger.info("No matching user found");
- return null;
- }
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
new file mode 100644
index 000000000..02253b5f4
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
@@ -0,0 +1,68 @@
+package edu.harvard.hms.dbmi.avillach.auth.service;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.List;
+import java.util.UUID;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
+
+@Service
+public class PrivilegeService extends BaseEntityService {
+
+ private final static Logger logger = LoggerFactory.getLogger(PrivilegeService.class.getName());
+
+ private final PrivilegeRepository privilegeRepository;
+
+ @Autowired
+ protected PrivilegeService(Class type, PrivilegeRepository privilegeRepository) {
+ super(type);
+ this.privilegeRepository = privilegeRepository;
+ }
+
+ @Transactional
+ public ResponseEntity> deletePrivilegeByPrivilegeId(String privilegeId) {
+ Privilege privilege = this.privilegeRepository.getById(UUID.fromString(privilegeId));
+
+ // Get security context with spring security context
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ // Get the principal name from the security context
+ String principalName = securityContext.getAuthentication().getName();
+
+ if (ADMIN.equals(privilege.getName())) {
+ logger.info("User: " + principalName
+ + ", is trying to remove the system admin privilege: " + ADMIN);
+ return PICSUREResponse.protocolError("System Admin privilege cannot be removed - uuid: " + privilege.getUuid().toString()
+ + ", name: " + privilege.getName());
+ }
+
+ return removeEntityById(privilegeId, this.privilegeRepository);
+ }
+
+ public ResponseEntity> updateEntity(List privileges) {
+ return updateEntity(privileges, this.privilegeRepository);
+ }
+
+ public ResponseEntity> addEntity(List privileges) {
+ return addEntity(privileges, this.privilegeRepository);
+ }
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(this.privilegeRepository);
+ }
+
+ public ResponseEntity> getEntityById(String privilegeId) {
+ return getEntityById(privilegeId, this.privilegeRepository);
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/UserMetadataMappingService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/UserMetadataMappingService.java
deleted file mode 100644
index 224c38737..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/UserMetadataMappingService.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
-
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.UserMetadataMapping;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserMetadataMappingRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.ws.rs.core.Response;
-import java.util.List;
-
-/**
- * Provides business logic for UserMetadataMapping endpoint.
- */
-public class UserMetadataMappingService extends BaseEntityService{
-
- Logger logger = LoggerFactory.getLogger(UserMetadataMappingService.class);
-
- @Inject
- UserMetadataMappingRepository userMetadataMappingRepo;
-
- @Inject
- ConnectionRepository connectionRepo;
-
- public UserMetadataMappingService() {
- super(UserMetadataMapping.class);
- }
-
- public List getAllMappingsForConnection(Connection connection) {
- return userMetadataMappingRepo.findByConnection(connection);
- }
-
- public Response addMappings(List mappings){
- String errorMessage = "The following connectionIds do not exist:\n";
- boolean error = false;
- for (UserMetadataMapping umm : mappings){
- Connection c = connectionRepo.findConnectionById(umm.getConnection().getId());
- if (c == null){
- error = true;
- errorMessage += umm.getConnection().getId() + "\n";
- } else {
- umm.setConnection(c);
- }
- }
- if (error){
- return Response.ok(errorMessage).build();
- }
- return addEntity(mappings, userMetadataMappingRepo);
- }
-
- public List getAllMappings() {
- return userMetadataMappingRepo.list();
- }
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java
deleted file mode 100644
index 7404ee118..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.service.auth;
-
-import java.util.*;
-
-import javax.inject.Inject;
-import javax.mail.MessagingException;
-import javax.ws.rs.NotAuthorizedException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.apache.http.Header;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.message.BasicHeader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.databind.JsonNode;
-
-import edu.harvard.dbmi.avillach.util.HttpClientUtil;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.rest.UserService;
-import edu.harvard.hms.dbmi.avillach.auth.service.MailService;
-import edu.harvard.hms.dbmi.avillach.auth.service.OauthUserMatchingService;
-import edu.harvard.hms.dbmi.avillach.auth.service.TOSService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
-
-/**
- * This class provides authentication functionality. This implements an authenticationService interface
- * in the future to support different modes of authentication.
- *
- * Thoughts of design
- * The main purpose of this class is returns a token that includes information of the roles of users.
- */
-public class AuthenticationService {
-
- private Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
-
- @Inject
- OauthUserMatchingService matchingService;
-
- @Inject
- UserRepository userRepository;
-
- @Inject
- RoleRepository roleRepo;
-
- @Inject
- TOSService tosService;
-
- @Inject
- MailService mailService;
-
- @Inject
- UserService userService;
-
- @Inject
- AuthUtils authUtil;
-
- private static final int AUTH_RETRY_LIMIT = 3;
-
- public Response getToken(Map authRequest){
- String accessToken = authRequest.get("access_token");
- String redirectURI = authRequest.get("redirectURI");
-
- if (accessToken == null || redirectURI == null || accessToken.isEmpty() || redirectURI.isEmpty())
- throw new ProtocolException("Missing accessToken or redirectURI in request body.");
-
- JsonNode userInfo = retrieveUserInfo(accessToken);
- JsonNode userIdNode = userInfo.get("user_id");
- if (userIdNode == null){
- logger.error("getToken() cannot find user_id by retrieveUserInfo(), return json response: " + userInfo.toString());
- throw new ApplicationException("cannot get sufficient user information. Please contact admin.");
- }
- String userId = userIdNode.asText();
-
- logger.info("Successfully retrieved userId, " + userId +
- ", from the provided code and redirectURI");
-
- String connectionId;
- try {
- connectionId = userInfo.get("identities").get(0).get("connection").asText();
- } catch (Exception e){
- logger.error("getToken() cannot find connection_id by retrieveUserInfo(), return json response: " + userInfo.toString());
- throw new ApplicationException("cannot get sufficient user information. Please contact admin.");
- }
-
- //Do we have this user already?
- User user = userRepository.findBySubjectAndConnection(userId, connectionId);
- if (user == null){
- //Try to match
- user = matchingService.matchTokenToUser(userInfo);
- if (user == null){
- if (JAXRSConfiguration.deniedEmailEnabled.startsWith("true")) {
- try {
- mailService.sendDeniedAccessEmail(userInfo);
- } catch (MessagingException e) {
- logger.warn("Failed to send user access denied email: ", e);
- }
- }
- throw new NotAuthorizedException("No user matching user_id " + userId + " present in database");
- }
- }
-
- HashMap claims = new HashMap();
- claims.put("sub", userId);
- claims.put("name", user.getName());
- claims.put("email", user.getEmail());
- HashMap responseMap = authUtil.getUserProfileResponse(claims);
-
- logger.info("LOGIN SUCCESS ___ " + user.getEmail() + ":" + user.getUuid().toString() + " ___ Authorization will expire at ___ " + responseMap.get("expirationDate") + "___");
-
- return PICSUREResponse.success(responseMap);
- }
-
- private JsonNode retrieveUserInfo(String accessToken){
- String auth0UserInfoURI = JAXRSConfiguration.auth0host + "/userinfo";
- Header[] headers = {
- new BasicHeader("Content-Type", MediaType.APPLICATION_JSON),
- new BasicHeader("Authorization", "Bearer " + accessToken)
- };
- JsonNode auth0Response = null;
- RequestConfig requestConfig = createRequestConfigWithCustomTimeout(2000);
-
- for(int i = 1; i <= AUTH_RETRY_LIMIT && auth0Response == null; i++) {
- try {
- auth0Response = HttpClientUtil.simpleGetWithConfig(
- auth0UserInfoURI,
- JAXRSConfiguration.client,
- JAXRSConfiguration.objectMapper,
- requestConfig,
- headers
- );
- } catch (ApplicationException e) {
- if(i < AUTH_RETRY_LIMIT ) {
- logger.warn("Failed to authenticate. Retrying");
- } else {
- logger.error("Failed to authenticate. Giving up!");
- throw e;
- }
- }
- }
- return auth0Response;
- }
-
- private RequestConfig createRequestConfigWithCustomTimeout(int timeoutMs) {
- return RequestConfig.custom()
- .setConnectionRequestTimeout(timeoutMs)
- .setConnectTimeout(timeoutMs)
- .setSocketTimeout(timeoutMs)
- .build();
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java.orig b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java.orig
deleted file mode 100644
index 045a774c8..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthenticationService.java.orig
+++ /dev/null
@@ -1,143 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.service.auth;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import edu.harvard.dbmi.avillach.util.HttpClientUtil;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-<<<<<<< HEAD
-import edu.harvard.hms.dbmi.avillach.auth.rest.UserService;
-import edu.harvard.hms.dbmi.avillach.auth.service.Auth0UserMatchingService;
-import edu.harvard.hms.dbmi.avillach.auth.service.MailService;
-import edu.harvard.hms.dbmi.avillach.auth.service.TermsOfServiceService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
-=======
-import edu.harvard.hms.dbmi.avillach.auth.service.OauthUserMatchingService;
-import edu.harvard.hms.dbmi.avillach.auth.service.MailService;
-import edu.harvard.hms.dbmi.avillach.auth.service.TOSService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
->>>>>>> master
-import org.apache.http.Header;
-import org.apache.http.message.BasicHeader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.ws.rs.NotAuthorizedException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This class is designed to provide authentication functionality. This implements an authenticationService interface
- * in the future to support different authentication mode.
- *
- * Thoughts of design
- * The main purpose of this class is returns a token that includes information of the roles of users.
- */
-public class AuthenticationService {
- private Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
-
- @Inject
- OauthUserMatchingService matchingService;
-
- @Inject
- UserRepository userRepository;
-
- @Inject
-<<<<<<< HEAD
- RoleRepository roleRepo;
-
- @Inject
- TermsOfServiceService tosService;
-=======
- TOSService tosService;
->>>>>>> master
-
- @Inject
- MailService mailService;
-
- @Inject
- UserService userService;
-
- @Inject
- AuthUtils authUtil;
-
- public Response getToken(Map authRequest){
- String accessToken = authRequest.get("access_token");
- String redirectURI = authRequest.get("redirectURI");
-
- if (accessToken == null || redirectURI == null || accessToken.isEmpty() || redirectURI.isEmpty())
- throw new ProtocolException("Missing accessToken or redirectURI in request body.");
-
- JsonNode userInfo = retrieveUserInfo(accessToken);
- JsonNode userIdNode = userInfo.get("user_id");
- if (userIdNode == null){
- logger.error("getToken() cannot find user_id by retrieveUserInfo(), return json response: " + userInfo.toString());
- throw new ApplicationException("cannot get sufficient user information. Please contact admin.");
- }
- String userId = userIdNode.asText();
-
- logger.info("Successfully retrieved userId, " + userId +
- ", from the provided code and redirectURI");
-
- String connectionId;
- try {
- connectionId = userInfo.get("identities").get(0).get("connection").asText();
- } catch (Exception e){
- logger.error("getToken() cannot find connection_id by retrieveUserInfo(), return json response: " + userInfo.toString());
- throw new ApplicationException("cannot get sufficient user information. Please contact admin.");
- }
-
- //Do we have this user already?
- User user = userRepository.findBySubjectAndConnection(userId, connectionId);
- if (user == null){
- //Try to match
- user = matchingService.matchTokenToUser(userInfo);
- if (user == null){
- if (JAXRSConfiguration.deniedEmailEnabled.startsWith("true")) {
- mailService.sendDeniedAccessEmail(userInfo);
- }
- throw new NotAuthorizedException("No user matching user_id " + userId + " present in database");
- }
- }
-
- HashMap claims = new HashMap();
- claims.put("sub", userId);
- claims.put("name", user.getName());
- claims.put("email", user.getEmail());
- HashMap responseMap = authUtil.getUserProfileResponse(claims);
-
- return PICSUREResponse.success(responseMap);
- }
-
- private JsonNode retrieveUserInfo(String accessToken){
- String auth0UserInfoURI = JAXRSConfiguration.auth0host + "/userinfo";
- List headers = new ArrayList<>();
- headers.add(new BasicHeader("Content-Type", MediaType.APPLICATION_JSON));
- headers.add(new BasicHeader("Authorization", "Bearer " + accessToken));
- return HttpClientUtil.simpleGet(auth0UserInfoURI,
- JAXRSConfiguration.client,
- JAXRSConfiguration.objectMapper,
- headers.toArray(new Header[headers.size()]));
- }
-
- private Map generateClaims(JsonNode userInfo, String... fields){
- Map claims = new HashMap<>();
-
- for (String field : fields) {
- JsonNode node = userInfo.get(field);
- if (node != null)
- claims.put(field, node.asText());
- }
-
- return claims;
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/package-info.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/package-info.java
deleted file mode 100644
index ee52f378f..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * This package provides services for authentication and authorization.
- */
-package edu.harvard.hms.dbmi.avillach.auth.service.auth;
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ApplicationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ApplicationService.java
new file mode 100644
index 000000000..56deb6d0c
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ApplicationService.java
@@ -0,0 +1,142 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ApplicationRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
+import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
+import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import org.hibernate.PropertyNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.*;
+
+@Service
+public class ApplicationService extends BaseEntityService {
+
+ private final static Logger logger = LoggerFactory.getLogger(ApplicationService.class);
+ private final ApplicationRepository applicationRepo;
+ private final PrivilegeRepository privilegeRepo;
+
+ @Value("${application.client.secret}")
+ private String CLIENT_SECRET;
+
+ @Autowired
+ protected ApplicationService(ApplicationRepository applicationRepo, PrivilegeRepository privilegeRepo) {
+ super(Application.class);
+ this.applicationRepo = applicationRepo;
+ this.privilegeRepo = privilegeRepo;
+ }
+
+ /**
+ * Retrieves an entity by its ID.
+ *
+ * @param applicationId the ID of the entity to retrieve
+ * @return a ResponseEntity representing the result of the operation
+ */
+ public ResponseEntity> getEntityById(String applicationId) {
+ return getEntityById(applicationId, applicationRepo);
+ }
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(applicationRepo);
+ }
+
+ @Transactional
+ public ResponseEntity> addNewApplications(List applications) {
+ checkAssociation(applications);
+ List appEntities = addOrUpdate(applications, true, applicationRepo);
+ for (Application application : appEntities) {
+ try {
+ application.setToken(
+ generateApplicationToken(application)
+ );
+ } catch (Exception e) {
+ logger.error("", e);
+ }
+ }
+
+ return updateEntity(appEntities, applicationRepo);
+ }
+
+ @Transactional
+ public ResponseEntity> deleteApplicationById(String applicationId) {
+ Application application = applicationRepo.getById(UUID.fromString(applicationId));
+ if (application == null) {
+ logger.error("deleteApplicationById() cannot find the application by applicationId: " + applicationId);
+ throw new IllegalArgumentException("Cannot find application by the given applicationId: " + applicationId);
+ }
+
+ return removeEntityById(applicationId, applicationRepo);
+ }
+
+ public ResponseEntity> updateApplications(List applications) {
+ checkAssociation(applications);
+ return updateEntity(applications, applicationRepo);
+ }
+
+ public ResponseEntity> refreshApplicationToken(String applicationId) {
+ Application application = applicationRepo.getById(UUID.fromString(applicationId));
+ if (application == null) {
+ logger.error("refreshApplicationToken() cannot find the application by applicationId: " + applicationId);
+ throw new IllegalArgumentException("Cannot find application by the given applicationId: " + applicationId);
+ }
+
+ String newApplicationToken = generateApplicationToken(application);
+ try {
+ application.setToken(
+ newApplicationToken
+ );
+
+ applicationRepo.merge(application);
+ } catch (Exception e) {
+ logger.error("", e);
+ }
+
+ return PICSUREResponse.success(Map.of("token", newApplicationToken));
+ }
+
+ private void checkAssociation(List applications) { //TODO: We need to refactor this into a service class
+ for (Application application : applications) {
+ if (application.getPrivileges() != null) {
+ Set privileges = new HashSet<>();
+ application.getPrivileges().forEach(p -> {
+ Privilege privilege = privilegeRepo.getById(p.getUuid());
+ if (privilege != null) {
+ privilege.setApplication(application);
+ privileges.add(privilege);
+ } else {
+ logger.error("Didn't find privilege by uuid: " + p.getUuid());
+ }
+ });
+ application.setPrivileges(privileges);
+
+ }
+ }
+ }
+
+ public String generateApplicationToken(Application application) { // TODO: Refactor this into a new service class
+ if (application == null || application.getUuid() == null) {
+ logger.error("generateApplicationToken() application is null or uuid is missing to generate the application token");
+ throw new PropertyNotFoundException("Cannot generate application token, please contact admin");
+ }
+
+ return JWTUtil.createJwtToken(
+ this.CLIENT_SECRET, null, null,
+ new HashMap<>(
+ Map.of(
+ "user_id", AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX + "|" + application.getName()
+ )
+ ),
+ AuthNaming.PSAMA_APPLICATION_TOKEN_PREFIX + "|" + application.getUuid().toString(), 365L * 1000 * 60 * 60 * 24);
+ }
+
+}
+
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
new file mode 100644
index 000000000..f2a2dbebc
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
@@ -0,0 +1,150 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import java.util.*;
+
+import javax.mail.MessagingException;
+
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import org.apache.http.Header;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.message.BasicHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import edu.harvard.dbmi.avillach.util.HttpClientUtil;
+import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+/**
+ * This class provides authentication functionality. This implements an authenticationService interface
+ * in the future to support different modes of authentication.
+ *
+ * Thoughts of design
+ * The main purpose of this class is returns a token that includes information of the roles of users.
+ */
+
+@Service
+public class AuthenticationService {
+
+ private final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
+
+ private final OauthUserMatchingService matchingService;
+
+ private final UserRepository userRepository;
+
+ private final MailService mailService;
+
+ private final UserService userService;
+ private static final int AUTH_RETRY_LIMIT = 3;
+
+ @Autowired
+ public AuthenticationService(OauthUserMatchingService matchingService, UserRepository userRepository, MailService mailService, UserService userService) {
+ this.matchingService = matchingService;
+ this.userRepository = userRepository;
+ this.mailService = mailService;
+ this.userService = userService;
+ }
+
+ public ResponseEntity> getToken(Map authRequest) {
+ String accessToken = authRequest.get("access_token");
+ String redirectURI = authRequest.get("redirectURI");
+
+ if (accessToken == null || redirectURI == null || accessToken.isEmpty() || redirectURI.isEmpty()) {
+ throw new IllegalArgumentException("Missing accessToken or redirectURI in request body.");
+ }
+
+ JsonNode userInfo = retrieveUserInfo(accessToken);
+ JsonNode userIdNode = userInfo.get("user_id");
+ if (userIdNode == null) {
+ logger.error("getToken() cannot find user_id by retrieveUserInfo(), return json response: " + userInfo.toString());
+ throw new NotAuthorizedException("cannot get sufficient user information. Please contact admin.");
+ }
+ String userId = userIdNode.asText();
+
+ logger.info("Successfully retrieved userId, " + userId +
+ ", from the provided code and redirectURI");
+
+ String connectionId;
+ try {
+ connectionId = userInfo.get("identities").get(0).get("connection").asText();
+ } catch (Exception e) {
+ logger.error("getToken() cannot find connection_id by retrieveUserInfo(), return json response: " + userInfo.toString());
+ throw new NotAuthorizedException("cannot get sufficient user information. Please contact admin.");
+ }
+
+ //Do we have this user already?
+ User user = userRepository.findBySubjectAndConnection(userId, connectionId);
+ if (user == null) {
+ //Try to match
+ user = matchingService.matchTokenToUser(userInfo);
+ if (user == null) {
+ if (JAXRSConfiguration.deniedEmailEnabled.startsWith("true")) {
+ try {
+ mailService.sendDeniedAccessEmail(userInfo);
+ } catch (MessagingException e) {
+ logger.warn("Failed to send user access denied email: ", e);
+ }
+ }
+ throw new NotAuthorizedException("No user matching user_id " + userId + " present in database");
+ }
+ }
+
+ HashMap claims = new HashMap();
+ claims.put("sub", userId);
+ claims.put("name", user.getName());
+ claims.put("email", user.getEmail());
+ HashMap responseMap = userService.getUserProfileResponse(claims);
+
+ logger.info("LOGIN SUCCESS ___ " + user.getEmail() + ":" + user.getUuid().toString() + " ___ Authorization will expire at ___ " + responseMap.get("expirationDate") + "___");
+
+ return PICSUREResponse.success(responseMap);
+ }
+
+ private JsonNode retrieveUserInfo(String accessToken) {
+ String auth0UserInfoURI = JAXRSConfiguration.auth0host + "/userinfo";
+ Header[] headers = {
+ new BasicHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE),
+ new BasicHeader("Authorization", "Bearer " + accessToken)
+ };
+ JsonNode auth0Response = null;
+ RequestConfig requestConfig = createRequestConfigWithCustomTimeout();
+
+ for (int i = 1; i <= AUTH_RETRY_LIMIT && auth0Response == null; i++) {
+ try {
+ auth0Response = HttpClientUtil.simpleGetWithConfig(
+ auth0UserInfoURI,
+ JAXRSConfiguration.client,
+ JAXRSConfiguration.objectMapper,
+ requestConfig,
+ headers
+ );
+ } catch (Exception e) {
+ if (i < AUTH_RETRY_LIMIT) {
+ logger.warn("Failed to authenticate. Retrying");
+ } else {
+ logger.error("Failed to authenticate. Giving up!");
+ throw e;
+ }
+ }
+ }
+ return auth0Response;
+ }
+
+ private RequestConfig createRequestConfigWithCustomTimeout() {
+ int timeoutMs = 2000; // 2 seconds, default is 3 seconds
+ return RequestConfig.custom()
+ .setConnectionRequestTimeout(timeoutMs)
+ .setConnectTimeout(timeoutMs)
+ .setSocketTimeout(timeoutMs)
+ .build();
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthorizationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationService.java
similarity index 95%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthorizationService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationService.java
index fee36726b..683979226 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/AuthorizationService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationService.java
@@ -1,15 +1,17 @@
-package edu.harvard.hms.dbmi.avillach.auth.service.auth;
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.AccessRule;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.rest.TokenController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@@ -20,7 +22,7 @@
* what endpoint they are trying to hit and the content of the request body (in HTTP POST method).
* Thoughts on design:
* The core technology used here is jsonpath.
- * In the {@link edu.harvard.hms.dbmi.avillach.auth.rest.TokenService#inspectToken(Map)} class, other registered applications
+ * In the {@link TokenController#inspectToken(Map)} class, other registered applications
* can hit the tokenIntrospection endpoint with a token they want PSAMA to introspect along
* with the URL the token holder is trying to hit and what data this token holder is trying to send. After
* checking if the token is valid or not, the authorization check in this class will start.
@@ -33,8 +35,9 @@
*
*
*/
+@Service
public class AuthorizationService {
- private Logger logger = LoggerFactory.getLogger(AuthorizationService.class);
+ private final Logger logger = LoggerFactory.getLogger(AuthorizationService.class);
/**
* Checking based on AccessRule in Privilege
@@ -61,7 +64,7 @@ public class AuthorizationService {
* @param requestBody
* @return
*
- * @see edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege
+ * @see Privilege
* @see AccessRule
*/
public boolean isAuthorized(Application application , Object requestBody, User user){
@@ -76,14 +79,6 @@ public boolean isAuthorized(Application application , Object requestBody, User u
return true;
}
- // Object parsedRequestBody = null;
- // Configuration conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL).addOptions(Option.ALWAYS_RETURN_LIST);
- // try {
- // parsedRequestBody = conf.jsonProvider().parse(JAXRSConfiguration.objectMapper.writeValueAsString(requestBody));
- // } catch (JsonProcessingException e) {
- // return true;
- // }
-
// start to process the jsonpath checking
String formattedQuery = null;
@@ -339,7 +334,7 @@ protected boolean evaluateAccessRule(Object parsedRequestBody, AccessRule access
// 2. if getGateAnyRelation is false, all gates passed
// 3. if getGateAnyRelation is true, one of the gate passed
if (gates != null && !gates.isEmpty()) {
- if (accessRule.getGateAnyRelation() == null || accessRule.getGateAnyRelation() == false) {
+ if (accessRule.getGateAnyRelation() == null || !accessRule.getGateAnyRelation()) {
// All gates are AND relationship
// means one fails all fail
@@ -374,12 +369,12 @@ protected boolean evaluateAccessRule(Object parsedRequestBody, AccessRule access
if (gatesPassed) {
logger.debug("evaluateAccessRule() gates passed");
- if (extractAndCheckRule(accessRule, parsedRequestBody) == false)
+ if (!extractAndCheckRule(accessRule, parsedRequestBody))
return false;
else {
if (accessRule.getSubAccessRule() != null) {
for (AccessRule subAccessRule : accessRule.getSubAccessRule()) {
- if (extractAndCheckRule(subAccessRule, parsedRequestBody) == false)
+ if (!extractAndCheckRule(subAccessRule, parsedRequestBody))
return false;
}
}
@@ -426,6 +421,8 @@ private boolean extractAndCheckRule(AccessRule accessRule, Object parsedRequestB
return false;
}
+
+ // TODO: Look into this more
// AccessRule type IS_EMPTY is very special, needs to be checked in front of any others
// in type IS_EMPTY, it doens't matter if the value is null or anything
if (accessRuleType == AccessRule.TypeNaming.IS_EMPTY
@@ -479,6 +476,7 @@ private boolean evaluateNode(Object requestBodyValue, AccessRule accessRule){
* 12 = "\demographics\AGE\"
*/
+ // TODO: Clean this up. We can use java 21 pattern matching to make this more readable
if (requestBodyValue instanceof String){
return decisionMaker(accessRule, (String)requestBodyValue);
} else if (requestBodyValue instanceof Collection) {
@@ -643,7 +641,7 @@ private boolean _decisionMaker(AccessRule accessRule, String requestBodyValue, S
logger.debug(requestBodyValue);
logger.debug(value);
-
+ // TODO: Clean this up. We can use java 21 pattern matching to make this more readable
switch (accessRule.getType()){
case AccessRule.TypeNaming.NOT_CONTAINS:
if (!requestBodyValue.contains(value))
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/BaseEntityService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
similarity index 85%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/BaseEntityService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
index d258597fb..4daaef8f4 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/BaseEntityService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
@@ -1,17 +1,15 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
import javax.validation.constraints.NotNull;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,14 +22,14 @@
*/
public abstract class BaseEntityService {
- private Logger logger;
+ private final Logger logger;
protected final Class type;
- private String auditLogName;
+ private final String auditLogName;
- @Context
- SecurityContext securityContext;
+// @Context
+// SecurityContext securityContext;
protected BaseEntityService(Class type){
this.type = type;
@@ -39,7 +37,7 @@ protected BaseEntityService(Class type){
logger = LoggerFactory.getLogger(type);
}
- public Response getEntityById(String id, BaseRepository baseRepository){
+ public ResponseEntity> getEntityById(String id, BaseRepository baseRepository){
logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext)
+ " Looking for " + type.getSimpleName() +
" by ID: " + id + "...");
@@ -53,7 +51,7 @@ public Response getEntityById(String id, BaseRepository baseRepository){
return PICSUREResponse.success(t);
}
- public Response getEntityAll(BaseRepository baseRepository){
+ public ResponseEntity> getEntityAll(BaseRepository baseRepository){
logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext) +
" Getting all " + type.getSimpleName() +
"s...");
@@ -69,7 +67,7 @@ public Response getEntityAll(BaseRepository baseRepository){
return PICSUREResponse.success(ts);
}
- public Response addEntity(List entities, BaseRepository baseRepository){
+ public ResponseEntity> addEntity(List entities, BaseRepository baseRepository){
String username = JAXRSConfiguration.getPrincipalName(securityContext);
if (entities == null || entities.isEmpty())
return PICSUREResponse.protocolError("No " + type.getSimpleName().toLowerCase() +
@@ -98,7 +96,7 @@ public Response addEntity(List entities, BaseRepository baseRepository){
"s are added.", addedEntities);
}
- public Response updateEntity(List entities, BaseRepository baseRepository){
+ public ResponseEntity> updateEntity(List entities, BaseRepository baseRepository){
if (entities == null || entities.isEmpty())
return PICSUREResponse.protocolError("No " + type.getSimpleName().toLowerCase() +
" to be updated.");
@@ -220,21 +218,6 @@ private boolean updateAllAttributes(T detachedT, BaseRepository baseRep
detachedT.getClass().getMethod(setter, field.getType())
.invoke(retrievedT, value);
}
- /**
- * This PropertyDescriptor is in java.beans package, which is the kind of
- * standard way of doing getter and setter, but there is one thing
- * this method is checking the return type of getter setter, if setter
- * return the Class object (which is pretty useful and normal these days),
- * this method will throw an IntrospectionException...,
- * but! I don't think using PropertyDescriptor to look for getter setter has
- * the best performance though...
- */
-// PropertyDescriptor pd = new PropertyDescriptor(fieldName, detachedT.getClass());
-// Object value = pd.getReadMethod()
-// .invoke(detachedT);
-// if (value != null){
-// pd.getWriteMethod().invoke(retrievedT, value);
-// }
}
} catch (IllegalArgumentException | ReflectiveOperationException ex /* | IntrospectionException ex */){
ex.printStackTrace();
@@ -246,7 +229,7 @@ private boolean updateAllAttributes(T detachedT, BaseRepository baseRep
return true;
}
- public Response removeEntityById(String id, BaseRepository baseRepository) {
+ public ResponseEntity> removeEntityById(String id, BaseRepository baseRepository) {
String username = JAXRSConfiguration.getPrincipalName(securityContext);
logger.info("User: " + username + " is trying to REMOVE an entity: "
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ConnectionWebService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ConnectionWebService.java
new file mode 100644
index 000000000..1d666a635
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/ConnectionWebService.java
@@ -0,0 +1,51 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class ConnectionWebService extends BaseEntityService {
+
+ private final ConnectionRepository connectionRepo;
+
+ @Autowired
+ protected ConnectionWebService(Class type, ConnectionRepository connectionRepo) {
+ super(type);
+ this.connectionRepo = connectionRepo;
+ }
+
+ public ResponseEntity> addEntity(List connections){
+ for (Connection c : connections){
+ if (c.getSubPrefix() == null || c.getRequiredFields() == null || c.getLabel() == null || c.getId() == null){
+ return PICSUREResponse.protocolError("Id, Label, Subprefix, and RequiredFields cannot be null");
+ }
+ Connection conn = connectionRepo.findConnectionById(c.getId());
+ if (conn != null){
+ return PICSUREResponse.protocolError("Id must be unique, a connection with id " + c.getId() + " already exists in the database");
+ }
+ }
+ return addEntity(connections, connectionRepo); // TODO: This should be moved to an actual service class. We shouldn't need to pass a repo to the service class
+ }
+
+ public ResponseEntity> getEntityById(String connectionId) {
+ return getEntityById(connectionId, connectionRepo);
+ }
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(connectionRepo);
+ }
+
+ public ResponseEntity> updateEntity(List connections) {
+ return updateEntity(connections, connectionRepo);
+ }
+
+ public ResponseEntity> removeEntityById(String connectionId) {
+ return removeEntityById(connectionId, connectionRepo);
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/FENCEAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
similarity index 89%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/FENCEAuthenticationService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
index bb01bf1c3..b3ee6a06c 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/auth/FENCEAuthenticationService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
@@ -1,75 +1,62 @@
-package edu.harvard.hms.dbmi.avillach.auth.service.auth;
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
-
import edu.harvard.dbmi.avillach.util.HttpClientUtil;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.AccessRule;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.AccessRuleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ApplicationRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthUtils;
-
+import edu.harvard.hms.dbmi.avillach.auth.entity.*;
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.*;
import org.apache.http.Header;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import javax.ws.rs.NotAuthorizedException;
-import javax.ws.rs.core.Response;
-
-import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.fence_consent_group_concept_path;
-import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.fence_harmonized_concept_path;
-import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.fence_standard_access_rules;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.*;
-public class FENCEAuthenticationService {
- private Logger logger = LoggerFactory.getLogger(FENCEAuthenticationService.class);
+import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.*;
- @Inject
- UserRepository userRepo;
+@Service
+public class FENCEAuthenticationService {
+ private final Logger logger = LoggerFactory.getLogger(FENCEAuthenticationService.class);
- @Inject
- RoleRepository roleRepo;
+ private final UserRepository userRepo;
- @Inject
- ConnectionRepository connectionRepo;
+ private final RoleRepository roleRepo;
- @Inject
- AccessRuleRepository accessruleRepo;
+ private final ConnectionRepository connectionRepo;
- @Inject
- UserRepository userRole;
+ private final AccessRuleRepository accessruleRepo;
- @Inject
- ApplicationRepository applicationRepo;
+ private final ApplicationRepository applicationRepo;
- @Inject
- PrivilegeRepository privilegeRepo;
+ private final PrivilegeRepository privilegeRepo;
- @Inject
- AuthUtils authUtil;
+ private final UserService userService;
private Application picSureApp;
private Connection fenceConnection;
private Map fenceMapping;
- @PostConstruct
+ @Autowired
+ public FENCEAuthenticationService(UserRepository userRepo, RoleRepository roleRepo, ConnectionRepository connectionRepo, AccessRuleRepository accessruleRepo, ApplicationRepository applicationRepo, PrivilegeRepository privilegeRepo, UserService userService) {
+ this.userRepo = userRepo;
+ this.roleRepo = roleRepo;
+ this.connectionRepo = connectionRepo;
+ this.accessruleRepo = accessruleRepo;
+ this.applicationRepo = applicationRepo;
+ this.privilegeRepo = privilegeRepo;
+ this.userService = userService;
+ }
+
+ // TODO: Find and equivalent of @PostConstruct in spring
+// @PostConstruct
public void initializeFenceService() {
picSureApp = applicationRepo.getUniqueResultByColumn("name", "PICSURE");
fenceConnection = connectionRepo.getUniqueResultByColumn("label", "FENCE");
@@ -128,7 +115,7 @@ private JsonNode getFENCEAccessToken(String fence_code) {
}
// Get access_token from FENCE, based on the provided `code`
- public Response getFENCEProfile(Map authRequest){
+ public ResponseEntity> getFENCEProfile(Map authRequest){
logger.debug("getFENCEProfile() starting...");
String fence_code = authRequest.get("code");
@@ -205,7 +192,7 @@ public Response getFENCEProfile(Map authRequest){
claims.put("name", fence_user_profile.get("name"));
claims.put("email", current_user.getEmail());
claims.put("sub", current_user.getSubject());
- HashMap responseMap = authUtil.getUserProfileResponse(claims);
+ HashMap responseMap = userService.getUserProfileResponse(claims);
logger.debug("getFENCEProfile() UserProfile response object has been generated");
logger.debug("getFENCEToken() finished");
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
similarity index 94%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
index c1ecde29a..5bf189714 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
@@ -1,4 +1,4 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
import java.io.FileNotFoundException;
import java.io.FileReader;
@@ -11,8 +11,10 @@
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
+import edu.harvard.hms.dbmi.avillach.auth.model.AccessEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
@@ -22,11 +24,12 @@
import com.github.mustachejava.MustacheFactory;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
/**
* Service class for sending email notifications.
*/
+@Service
public class MailService {
private static Logger logger = LoggerFactory.getLogger(MailService.class);
private static MustacheFactory mf = new DefaultMustacheFactory();
@@ -109,7 +112,7 @@ public void sendDeniedAccessEmail(JsonNode userInfo) throws AddressException, Me
/**
* Generate email from template and send it.
- * @param template Name of the template.
+ * @param emailTemplate Name of the template.
* @param to Recipients
* @param subject Subject of the email
* @param scope Object that contains attributes for template. e.g.: Map
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/OauthUserMatchingService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/OauthUserMatchingService.java
new file mode 100644
index 000000000..b7fa284f1
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/OauthUserMatchingService.java
@@ -0,0 +1,126 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import edu.harvard.hms.dbmi.avillach.auth.rest.UserController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jayway.jsonpath.*;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Matches users created by admins with user profiles created by a 3rd party Oauth provider.
+ */
+@Service
+public class OauthUserMatchingService {
+
+ private final Logger logger = LoggerFactory.getLogger(OauthUserMatchingService.class);
+
+ private final UserRepository userRepo;
+
+ private final UserController userController;
+
+ private final UserMetadataMappingService mappingService;
+
+ private final ConnectionRepository connectionRepo;
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Autowired
+ public OauthUserMatchingService(UserRepository userRepo, UserController userController, UserMetadataMappingService mappingService, ConnectionRepository connectionRepo) {
+ this.userRepo = userRepo;
+ this.userController = userController;
+ this.mappingService = mappingService;
+ this.connectionRepo = connectionRepo;
+ }
+
+ /**
+ * Retrieve a user profile by access_token and match it to a pre-created user in the database using
+ * pre-configured matching rules.
+ *
+ * @param userInfo UserInfo returned from auth0
+ * @return The user that was matched or null if no match was possible.
+ */
+ public User matchTokenToUser(JsonNode userInfo) {
+ // This retrieves a map of UserInfo as JSON.
+ try {
+ String userInfoString = mapper.writeValueAsString(userInfo);
+ logger.info("Attempting to find match for user with info: " + userInfo);
+
+ //Parse this once so it doesn't get re-parsed every time we read from it
+ Configuration conf = Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL).addOptions(Option.ALWAYS_RETURN_LIST);
+ Object parsedInfo = conf.jsonProvider().parse(userInfoString);
+ //Return lists or null so that we don't have to worry about whether it's a single object or an array, or catch errors
+ List connections = JsonPath.using(conf).parse(parsedInfo).read("$.identities[0].connection");
+ String connectionId = connections.get(0);
+
+ Connection connection = connectionRepo.getUniqueResultByColumn("id", connectionId);
+
+ List mappings = mappingService.getAllMappingsForConnection(connection);
+
+ if (mappings == null || mappings.isEmpty()) {
+ //We don't have any mappings for this connection yet
+ logger.warn("Unable to find user metadata mappings for connectionId " + connection);
+ return null;
+ }
+
+ //We only care about unmatched users
+ List users = userRepo.listUnmatchedByConnectionId(connection);
+ if (users == null || users.isEmpty()) {
+ logger.info("No unmatched users exist with connectionId " + connection);
+ return null;
+ }
+ for (UserMetadataMapping umm : mappings) {
+ List auth0values = JsonPath.using(conf).parse(parsedInfo).read(umm.getAuth0MetadataJsonPath());
+ if (auth0values == null || auth0values.isEmpty()) {
+ //Well, nothing found, let's move on.
+ logger.info("Fetched data has no value at " + umm.getAuth0MetadataJsonPath());
+ break;
+ }
+ String auth0value = auth0values.get(0);
+ for (User u : users) {
+ List values = null;
+ try{
+ values = JsonPath.using(conf).parse(u.getGeneralMetadata()).read(umm.getGeneralMetadataJsonPath());
+ } catch (JsonPathException e) {
+ logger.warn("User " + u.getUuid() + " has invalid general metadata: " + u.getGeneralMetadata());
+ continue;
+ }
+ if (values == null || values.isEmpty()) {
+ logger.warn("User " + u.getUuid() + " has no value at " + umm.getGeneralMetadataJsonPath());
+ continue;
+ }
+ String generalValue = values.get(0);
+ if (auth0value.equalsIgnoreCase(generalValue)) {
+ //Match found!!
+ String userId = JsonPath.read(parsedInfo, "$.user_id");
+ logger.info("Matching user with user_id " + userId);
+ u.setAuth0metadata(userInfoString);
+ u.setMatched(true);
+ u.setSubject(userId);
+ userController.updateEntity(Arrays.asList(u), userRepo);
+ return u;
+ }
+ }
+ }
+ } catch (JsonProcessingException e ){
+ logger.error("Unable to read UserInfo");
+ }
+ //No user found
+ logger.info("No matching user found");
+ return null;
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/TOSService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TOSService.java
similarity index 50%
rename from pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/TOSService.java
rename to pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TOSService.java
index 7dd111c68..13139d4e4 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/TOSService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TOSService.java
@@ -1,15 +1,18 @@
-package edu.harvard.hms.dbmi.avillach.auth.service;
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.TermsOfService;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.TermsOfServiceRepository;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.rest.UserService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.repository.TermsOfServiceRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.rest.TermsOfSerivceController;
+import edu.harvard.hms.dbmi.avillach.auth.rest.UserController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
-import javax.inject.Inject;
import javax.persistence.NoResultException;
import java.util.Arrays;
import java.util.Date;
@@ -18,22 +21,41 @@
/**
* Provides business logic for the TermsOfService endpoint.
>
*
- * @see edu.harvard.hms.dbmi.avillach.auth.rest.TermsOfServiceEndpoint
+ * @see TermsOfSerivceController
*/
+@Service
public class TOSService {
- Logger logger = LoggerFactory.getLogger(TOSService.class);
+ private final static Logger logger = LoggerFactory.getLogger(TOSService.class);
- @Inject
- TermsOfServiceRepository termsOfServiceRepo;
+ @Value("${application.tos.enabled}")
+ private boolean isToSEnabled;
- @Inject
- UserRepository userRepo;
+ private final TermsOfServiceRepository termsOfServiceRepo;
+
+ private final UserRepository userRepo;
+
+ private final UserController userController; // TODO: This isn't a service its a controller. Why are we doing this?
+
+ @Autowired
+ public TOSService(TermsOfServiceRepository termsOfServiceRepo, UserRepository userRepo, UserController userController) {
+ this.termsOfServiceRepo = termsOfServiceRepo;
+ this.userRepo = userRepo;
+ this.userController = userController;
+ }
- @Inject
- UserService userService;
public boolean hasUserAcceptedLatest(String userId){
+ // If TOS is not enabled, then the user has accepted it
+ if (!isToSEnabled){
+ return true;
+ }
+
+ // If there is no TOS, then the user has accepted it
+ if (getLatest() == null) {
+ return true;
+ }
+
logger.info("Checking Terms Of Service acceptance for user with id " + userId);
return userRepo.checkAgainstTOSDate(userId);
}
@@ -63,7 +85,8 @@ public void acceptTermsOfService(String userId){
user.setAcceptedTOS(new Date());
List users = Arrays.asList(user);
Date tosDate = termsOfServiceRepo.getLatest().getDateUpdated();
- userService.updateUser(users);
+ userController.updateUser(users);
logger.info("TOS_LOG : User " + (!StringUtils.isEmpty(user.getEmail()) ? user.getEmail() : user.getGeneralMetadata()) + " accepted the Terms of Service dated " + tosDate.toString());
}
+
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
new file mode 100644
index 000000000..02df69e76
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
@@ -0,0 +1,57 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserMetadataMappingRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * Provides business logic for UserMetadataMapping endpoint.
+ */
+@Service
+public class UserMetadataMappingService extends BaseEntityService {
+
+ private final UserMetadataMappingRepository userMetadataMappingRepo;
+
+ private final ConnectionRepository connectionRepo;
+
+ @Autowired
+ public UserMetadataMappingService(UserMetadataMappingRepository userMetadataMappingRepo, ConnectionRepository connectionRepo) {
+ super(UserMetadataMapping.class);
+ this.userMetadataMappingRepo = userMetadataMappingRepo;
+ this.connectionRepo = connectionRepo;
+ }
+
+ public List getAllMappingsForConnection(Connection connection) {
+ return userMetadataMappingRepo.findByConnection(connection);
+ }
+
+ public ResponseEntity> addMappings(List mappings) {
+ String errorMessage = "The following connectionIds do not exist:\n";
+ boolean error = false;
+ for (UserMetadataMapping umm : mappings) {
+ Connection c = connectionRepo.findConnectionById(umm.getConnection().getId());
+ if (c == null) {
+ error = true;
+ errorMessage += umm.getConnection().getId() + "\n";
+ } else {
+ umm.setConnection(c);
+ }
+ }
+ if (error) {
+ return PICSUREResponse.success(errorMessage);
+ }
+ return addEntity(mappings, userMetadataMappingRepo);
+ }
+
+ public List getAllMappings() {
+ return userMetadataMappingRepo.list();
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
new file mode 100644
index 000000000..4edc5afef
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
@@ -0,0 +1,67 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+@Service
+public class UserService {
+
+ private final Logger logger = Logger.getLogger(UserService.class.getName());
+
+ private final TOSService tosService;
+
+ @Autowired
+ public UserService(TOSService tosService) {
+ this.tosService = tosService;
+ }
+
+ public HashMap getUserProfileResponse(Map claims) {
+ logger.info("getUserProfileResponse() starting...");
+
+ HashMap responseMap = new HashMap();
+ logger.info("getUserProfileResponse() initialized map");
+
+ logger.info("getUserProfileResponse() using claims:" + claims.toString());
+
+ String token = JWTUtil.createJwtToken(
+ JAXRSConfiguration.clientSecret,
+ "whatever",
+ "edu.harvard.hms.dbmi.psama",
+ claims,
+ claims.get("sub").toString(),
+ JAXRSConfiguration.tokenExpirationTime
+ );
+ logger.info("getUserProfileResponse() PSAMA JWT token has been generated. Token:" + token);
+ responseMap.put("token", token);
+
+ logger.info("getUserProfileResponse() .usedId field is set");
+ responseMap.put("userId", claims.get("sub").toString());
+
+ logger.info("getUserProfileResponse() .email field is set");
+ responseMap.put("email", claims.get("email").toString());
+
+ logger.info("getUserProfileResponse() acceptedTOS is set");
+
+ boolean acceptedTOS = tosService.hasUserAcceptedLatest(claims.get("sub").toString());
+
+ responseMap.put("acceptedTOS", "" + acceptedTOS);
+
+ logger.info("getUserProfileResponse() expirationDate is set");
+ Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
+ responseMap.put("expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString());
+
+ logger.info("getUserProfileResponse() finished");
+ return responseMap;
+ }
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/package-info.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/package-info.java
new file mode 100644
index 000000000..9a626b7e0
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/package-info.java
@@ -0,0 +1 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/AuthUtils.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/AuthUtils.java
deleted file mode 100644
index 3e74e87a5..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/AuthUtils.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.utils;
-
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.service.TOSService;
-import io.jsonwebtoken.*;
-import org.apache.commons.codec.binary.Base64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.NotAuthorizedException;
-import java.io.UnsupportedEncodingException;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Contains common methods for authentication and authorization.
- */
-public class AuthUtils {
-
- private static Logger logger = LoggerFactory.getLogger(AuthUtils.class);
-
- @Inject
- TOSService tosService;
-
- /**
- * support both Base64 encrypted and non-Base64 encrypted
- * @param clientSecret
- * @param token
- * @return
- */
- public static Jws parseToken(@NotNull String clientSecret, String token)
- throws NotAuthorizedException{
- Jws jws;
-
- try {
- jws = Jwts.parser().setSigningKey(clientSecret.getBytes()).parseClaimsJws(token);
- } catch (SignatureException e) {
- try {
- if(JAXRSConfiguration.clientSecretIsBase64.startsWith("true")) {
- // handle if client secret is base64 encoded
- jws = Jwts.parser().setSigningKey(Base64.decodeBase64(clientSecret
- .getBytes("UTF-8")))
- .parseClaimsJws(token);
- } else {
- // handle if client secret is not base64 encoded
- jws = Jwts.parser().setSigningKey(clientSecret
- .getBytes("UTF-8"))
- .parseClaimsJws(token);
- }
- } catch (UnsupportedEncodingException ex){
- logger.error("parseToken() clientSecret encoding UTF-8 is not supported. "
- + ex.getClass().getSimpleName() + ": " + ex.getMessage());
- throw new NotAuthorizedException("encoding is not supported");
- } catch (JwtException | IllegalArgumentException ex) {
- logger.error("parseToken() throws: " + e.getClass().getSimpleName() + ", " + e.getMessage());
- throw new NotAuthorizedException(ex.getClass().getSimpleName());
- }
- } catch (JwtException | IllegalArgumentException e) {
- logger.error("parseToken() throws: " + e.getClass().getSimpleName() + ", " + e.getMessage());
- throw new NotAuthorizedException(e.getClass().getSimpleName());
- }
-
- if (jws == null) {
- logger.error("parseToken() get null for jws body by parsing Token - " + token);
- throw new NotAuthorizedException("please contact admin to see the log");
- }
-
- return jws;
- }
-
- /*
- * Generate a HashMap of all the information used in the JSON response back to the UI client, while also
- * package the same information inside a valid PSAMA JWT token
- *
- */
- public HashMap getUserProfileResponse(Map claims) {
- logger.debug("getUserProfileResponse() starting...");
-
- HashMap responseMap = new HashMap();
- logger.debug("getUserProfileResponse() initialized map");
-
- logger.debug("getUserProfileResponse() using claims:"+claims.toString());
-
- String token = JWTUtil.createJwtToken(
- JAXRSConfiguration.clientSecret,
- "whatever",
- "edu.harvard.hms.dbmi.psama",
- claims,
- claims.get("sub").toString(),
- JAXRSConfiguration.tokenExpirationTime
- );
- logger.debug("getUserProfileResponse() PSAMA JWT token has been generated. Token:"+token);
- responseMap.put("token", token);
-
- logger.debug("getUserProfileResponse() .usedId field is set");
- responseMap.put("userId", claims.get("sub").toString());
-
- logger.debug("getUserProfileResponse() .email field is set");
- responseMap.put("email", claims.get("email").toString());
-
- logger.debug("getUserProfileResponse() acceptedTOS is set");
-
- boolean acceptedTOS = acceptedTOSBySub(claims.get("sub").toString());
-
- responseMap.put("acceptedTOS", ""+acceptedTOS);
-
- logger.debug("getUserProfileResponse() expirationDate is set");
- Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
- responseMap.put("expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString());
-
- logger.debug("getUserProfileResponse() finished");
- return responseMap;
- }
-
- public boolean acceptedTOSBySub(String subject) {
- return JAXRSConfiguration.tosEnabled.startsWith("true") ?
- tosService.getLatest() == null || tosService.hasUserAcceptedLatest(subject) : true;
- }
-
-
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
index 77ceae132..80f979add 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
@@ -1,27 +1,34 @@
package edu.harvard.hms.dbmi.avillach.auth.utils;
-import io.jsonwebtoken.JwtBuilder;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
+import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import io.jsonwebtoken.*;
+import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.Map;
+import java.util.Optional;
/**
* This class is for generating a JWT token and contains common methods for operations on JWT tokens.
* For more information on JWT tokens, see https://github.com/hms-dbmi/jwt-creator/blob/master/src/main/java/edu/harvard/hms/dbmi/avillach/jwt/App.java
*/
public class JWTUtil {
- private static Logger logger = LoggerFactory.getLogger(JWTUtil.class);
+ private final static Logger logger = LoggerFactory.getLogger(JWTUtil.class);
private static final long defaultTTLMillis = 1000L * 60 * 60 * 24 * 7;
+ @Value("${application.client.secret}")
+ private static String CLIENT_SECRET;
+
/**
- *
* @param clientSecret
* @param id
* @param issuer
@@ -30,15 +37,17 @@ public class JWTUtil {
* @param ttlMillis
* @return
*/
- public static String createJwtToken(String clientSecret, String id, String issuer, Map claims , String subject, long ttlMillis) {
+ public static String createJwtToken(String clientSecret, String id, String issuer, Map claims, String subject, long ttlMillis) {
logger.debug("createJwtToken() starting...");
String jwt_token = null;
- if (ttlMillis < 0)
+ if (ttlMillis < 0) {
ttlMillis = defaultTTLMillis;
+ }
- if (ttlMillis == 0)
+ if (ttlMillis == 0) {
ttlMillis = 999L * 1000 * 60 * 60 * 24;
+ }
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
@@ -48,32 +57,60 @@ public static String createJwtToken(String clientSecret, String id, String issue
byte[] apiKeySecretBytes = clientSecret.getBytes();
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
- //Let's set the JWT Claims
- JwtBuilder builder = null;
+ //Builds the JWT and serializes it to a compact, URL-safe string
+ JwtBuilder builder = Jwts.builder()
+ .setClaims(claims)
+ .setId(id)
+ .setIssuedAt(now)
+ .setSubject(subject)
+ .setIssuer(issuer)
+ .signWith(signatureAlgorithm, signingKey);
+
+ //if it has been specified, let's add the expiration
+ long expMillis = nowMillis + ttlMillis;
+ Date exp = new Date(expMillis);
+ builder.setExpiration(exp);
+ jwt_token = builder.compact();
+
+ return jwt_token;
+ }
+
+ public static Jws parseToken(String token) {
+ Jws jws = null;
+
try {
- //Builds the JWT and serializes it to a compact, URL-safe string
- builder = Jwts.builder()
- .setClaims(claims)
- .setId(id)
- .setIssuedAt(now)
- .setSubject(subject)
- .setIssuer(issuer)
- .signWith(signatureAlgorithm, signingKey);
-
- //if it has been specified, let's add the expiration
- if (ttlMillis >= 0) {
- long expMillis = nowMillis + ttlMillis;
- Date exp = new Date(expMillis);
- builder.setExpiration(exp);
+ jws = Jwts.parser().setSigningKey(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
+ } catch (SignatureException e) {
+ try {
+ if (JAXRSConfiguration.clientSecretIsBase64.startsWith("true")) {
+ // handle if client secret is base64 encoded
+ jws = Jwts.parser().setSigningKey(Base64.decodeBase64(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8))).parseClaimsJws(token);
+ } else {
+ // handle if client secret is not base64 encoded
+ jws = Jwts.parser().setSigningKey(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
+ }
+ } catch (JwtException | IllegalArgumentException ex) {
+ logger.error("parseToken() throws: " + e.getClass().getSimpleName() + ", " + e.getMessage());
+ throw new NotAuthorizedException(ex.getClass().getSimpleName() + ": " + ex.getMessage());
}
+ } catch (JwtException | IllegalArgumentException e) {
+ logger.error("parseToken() throws: " + e.getClass().getSimpleName() + ", " + e.getMessage());
+ throw new NotAuthorizedException(e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
- jwt_token = builder.compact();
- } catch (Exception ex) {
- logger.error("createJwtToken() Exception:"+ex.getClass().getSimpleName());
- ex.printStackTrace();
+ if (jws == null) {
+ logger.error("parseToken() get null for jws body by parsing Token - " + token);
+ throw new NotAuthorizedException("Please contact admin to see the log");
}
+ return jws;
+ }
- return jwt_token;
+ public static Optional getTokenFromAuthorizationHeader(String authorizationHeader) {
+ if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
+ return Optional.empty();
+ }
+
+ return Optional.of(authorizationHeader.substring("Bearer".length()).trim());
}
}
diff --git a/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 000000000..8c7e347ad
--- /dev/null
+++ b/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,118 @@
+{
+ "properties": [
+ {
+ "name": "application.client.id",
+ "type": "java.lang.String",
+ "description": "Description for application.client.id."
+ },
+ {
+ "name": "application.client.secret",
+ "type": "java.lang.String",
+ "description": "Description for application.client.secret."
+ },
+ {
+ "name": "application.client.secret.base64",
+ "type": "java.lang.String",
+ "description": "Description for application.client.secret.base64."
+ },
+ {
+ "name": "application.user.id.claim",
+ "type": "java.lang.String",
+ "description": "Description for application.user.id.claim."
+ },
+ {
+ "name": "application.auth0.host",
+ "type": "java.lang.String",
+ "description": "Description for application.auth0.host."
+ },
+ {
+ "name": "application.tos.enabled",
+ "type": "java.lang.String",
+ "description": "Description for application.tos.enabled."
+ },
+ {
+ "name": "application.default.application.uuid",
+ "type": "java.lang.String",
+ "description": "Description for application.default.application.uuid."
+ },
+ {
+ "name": "application.system.name",
+ "type": "java.lang.String",
+ "description": "Description for application.system.name."
+ },
+ {
+ "name": "application.template.path",
+ "type": "java.lang.String",
+ "description": "Description for application.template.path."
+ },
+ {
+ "name": "application.access.grant.email.subject",
+ "type": "java.lang.String",
+ "description": "Description for application.access.grant.email.subject."
+ },
+ {
+ "name": "application.user.activation.reply.to",
+ "type": "java.lang.String",
+ "description": "Description for application.user.activation.reply.to."
+ },
+ {
+ "name": "application.admin.users",
+ "type": "java.lang.String",
+ "description": "Description for application.admin.users."
+ },
+ {
+ "name": "application.denied.email.enabled",
+ "type": "java.lang.String",
+ "description": "Description for application.denied.email.enabled."
+ },
+ {
+ "name": "application.idp.provider",
+ "type": "java.lang.String",
+ "description": "Description for application.idp.provider."
+ },
+ {
+ "name": "application.idp.provider.uri",
+ "type": "java.lang.String",
+ "description": "Description for application.idp.provider.uri."
+ },
+ {
+ "name": "application.fence.client.id",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.client.id."
+ },
+ {
+ "name": "application.fence.client.secret",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.client.secret."
+ },
+ {
+ "name": "application.fence.redirect.url",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.redirect.url."
+ },
+ {
+ "name": "application.fence.consent.group.concept.path",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.consent.group.concept.path."
+ },
+ {
+ "name": "application.fence.standard.access.rules",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.standard.access.rules."
+ },
+ {
+ "name": "application.fence.harmonized.concept.path",
+ "type": "java.lang.String",
+ "description": "Description for application.fence.harmonized.concept.path."
+ },
+ {
+ "name": "application.token.expiration.time",
+ "type": "java.lang.String",
+ "description": "Description for application.token.expiration.time."
+ },
+ {
+ "name": "application.long.term.token.expiration.time",
+ "type": "java.lang.String",
+ "description": "Description for application.long.term.token.expiration.time."
+ }
+ ] }
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/resources/application.properties b/pic-sure-auth-services/src/main/resources/application.properties
new file mode 100644
index 000000000..28d01886d
--- /dev/null
+++ b/pic-sure-auth-services/src/main/resources/application.properties
@@ -0,0 +1,50 @@
+# Application server port and context path
+server.port=8090
+server.servlet.context-path=/auth
+
+spring.datasource.url=jdbc:your_database_url
+spring.datasource.username=your_db_username
+spring.datasource.password=your_db_password
+spring.datasource.driver-class-name=com.mysql.jdbc.Driver
+
+# JPA/Hibernate properties
+spring.jpa.show-sql=true
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.YourDialect
+
+# Mail session configuration (Assuming Gmail SMTP for example)
+spring.mail.host=smtp.gmail.com
+spring.mail.port=587
+spring.mail.username=your_email@gmail.com
+spring.mail.password=your_email_password
+spring.mail.properties.mail.smtp.auth=true
+spring.mail.properties.mail.smtp.starttls.enable=true
+
+# Custom application properties
+application.client.id=your_client_id
+application.client.secret=your_client_secret
+application.client.secret.base64=your_base64_flag
+application.user.id.claim=sub
+application.auth0.host=your_auth0_host
+application.tos.enabled=true
+application.default.application.uuid=your_default_application_uuid
+application.system.name=your_system_name
+application.template.path=your_template_path
+application.access.grant.email.subject=your_email_subject
+application.user.activation.reply.to=your_reply_to_address
+application.admin.users=your_admin_users
+application.denied.email.enabled=your_denied_email_enabled_flag
+
+# IDP Provider configurations
+application.idp.provider=your_idp_provider
+application.idp.provider.uri=your_idp_provider_uri
+application.fence.client.id=your_fence_client_id
+application.fence.client.secret=your_fence_client_secret
+application.fence.redirect.url=your_fence_redirect_url
+application.fence.consent.group.concept.path=your_consent_group_concept_path
+application.fence.standard.access.rules=your_standard_access_rules
+application.fence.harmonized.concept.path=your_harmonized_concept_path
+
+# Token Expiration
+application.token.expiration.time=3600000 # 1 hour in milliseconds
+application.long.term.token.expiration.time=2592000000 # 30 days in milliseconds
diff --git a/pic-sure-auth-services/src/main/webapp/META-INF/context.xml b/pic-sure-auth-services/src/main/webapp/META-INF/context.xml
deleted file mode 100644
index d123dd3f8..000000000
--- a/pic-sure-auth-services/src/main/webapp/META-INF/context.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/pic-sure-auth-services/src/main/webapp/WEB-INF/beans.xml b/pic-sure-auth-services/src/main/webapp/WEB-INF/beans.xml
deleted file mode 100644
index 1c97df0e3..000000000
--- a/pic-sure-auth-services/src/main/webapp/WEB-INF/beans.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/ApplicationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/ApplicationServiceTest.java
index 4618a11b4..35d46c8e7 100644
--- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/ApplicationServiceTest.java
+++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/ApplicationServiceTest.java
@@ -1,19 +1,21 @@
package edu.harvard.hms.dbmi.avillach;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.rest.ApplicationService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.ApplicationService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
import java.util.UUID;
public class ApplicationServiceTest {
+ @Mock
+ private ApplicationService applicationService;
+
@Before
public void init() {
- JAXRSConfiguration.clientSecret = "test";
}
@Test
@@ -24,7 +26,7 @@ public void testGenerateToken(){
application.setName("Testing Application");
application.setUrl("https://psama.hms.harvard.edu");
- String token = new ApplicationService().generateApplicationToken(application);
+ String token = applicationService.generateApplicationToken(application);
Assert.assertNotNull("Token is null, given application: " + application.getUuid(), token);
Assert.assertTrue("Token is too short",token.length() > 10);
diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
index f864b2d2f..7a40e5f07 100644
--- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
+++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
@@ -4,13 +4,14 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.data.entity.UserMetadataMapping;
-import edu.harvard.hms.dbmi.avillach.auth.data.repository.UserRepository;
-import edu.harvard.hms.dbmi.avillach.auth.rest.UserService;
-import edu.harvard.hms.dbmi.avillach.auth.service.OauthUserMatchingService;
-import edu.harvard.hms.dbmi.avillach.auth.service.UserMetadataMappingService;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.rest.UserController;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.OauthUserMatchingService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserMetadataMappingService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
@@ -41,10 +42,10 @@ public class Auth0MatchingServiceTest {
UserMetadataMappingService mappingService = mock(UserMetadataMappingService.class);
@Mock
- UserService userService = mock(UserService.class);
+ UserController userController = mock(UserController.class);
@InjectMocks
- OauthUserMatchingService cut = new OauthUserMatchingService();
+ OauthUserMatchingService cut = new OauthUserMatchingService(userRepo, userController, mappingService, mock(ConnectionRepository.class));
User persistedUser;
ObjectMapper mapper = new ObjectMapper();
@@ -64,7 +65,7 @@ public Void answer(InvocationOnMock invocation) {
persistedUser = userList.get(0);
return null;
}
- }).when(userService).updateUser(any(List.class));
+ }).when(userController).updateUser(any(List.class));
}
@Test
@@ -170,7 +171,7 @@ private List getAllMappingsForConnectionMock(String connect
new UserMetadataMapping().setConnection(new Connection().setId("no-user-connection")).setGeneralMetadataJsonPath("$.email").setAuth0MetadataJsonPath("$.email"),
new UserMetadataMapping().setConnection(new Connection().setId("invalid-path")).setGeneralMetadataJsonPath("$.email").setAuth0MetadataJsonPath("$.noPath")
- );
+ );
return allMappings.stream().filter((UserMetadataMapping mapping) -> {
return mapping.getConnection().getId().equalsIgnoreCase(connectionId);
}).collect(Collectors.toList());
@@ -179,15 +180,16 @@ private List getAllMappingsForConnectionMock(String connect
public JsonNode mockAuthAPIUserInfo(String accessToken) throws IOException {
Map map = Map.of("ldap-connector-access-token",
"{ \"name\": \"Guy,Some\", \"family_name\": \"Guy\", \"given_name\": \"Some\", \"nickname\": \"CH000000000\", \"groups\": [], \"emails\": [ \"foo@childrens.harvard.edu\" ], \"dn\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"distinguishedName\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"organizationUnits\": \"CN=CH0000000,OU=users,DC=chbdir,DC=org\", \"email\": \"foo@childrens.harvard.edu\", \"updated_at\": \"2018-10-04T18:28:23.371Z\", \"picture\": \"https://s.gravatar.com/avatar/blablablablablablablablablablablabla?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fsp.png\", \"user_id\": \"ad|ldap-connector|blablablablablablablablablablablabla\", \"identities\": [ { \"user_id\": \"ldap-connector|blablablablablablablablablablablabla\", \"provider\": \"ad\", \"connection\": \"ldap-connector\", \"isSocial\": false } ], \"created_at\": \"2018-01-26T14:06:50.413Z\", \"username\": \"CH0000000\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.32\", \"last_login\": \"2018-10-04T18:28:23.091Z\", \"logins_count\": 399, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "github-access-token","{ \"email\": \"blablabla@gmail.com\", \"name\": \"Some Girl\", \"picture\": \"https://avatars3.githubusercontent.com/u/0000000000?v=4\", \"nickname\": \"blablabla\", \"gravatar_id\": \"\", \"url\": \"https://api.github.com/users/blablabla\", \"html_url\": \"https://github.com/blablabla\", \"followers_url\": \"https://api.github.com/users/blablabla/followers\", \"following_url\": \"https://api.github.com/users/blablabla/following{/other_user}\", \"gists_url\": \"https://api.github.com/users/blablabla/gists{/gist_id}\", \"starred_url\": \"https://api.github.com/users/blablabla/starred{/owner}{/repo}\", \"subscriptions_url\": \"https://api.github.com/users/blablabla/subscriptions\", \"organizations_url\": \"https://api.github.com/users/blablabla/orgs\", \"repos_url\": \"https://api.github.com/users/blablabla/repos\", \"events_url\": \"https://api.github.com/users/blablabla/events{/privacy}\", \"received_events_url\": \"https://api.github.com/users/blablabla/received_events\", \"type\": \"User\", \"site_admin\": false, \"location\": \"Nowhere, USA\", \"hireable\": true, \"public_repos\": 8, \"public_gists\": \"0\", \"followers\": 3, \"following\": 1, \"updated_at\": \"2018-09-20T18:47:43.703Z\", \"emails\": [ { \"email\": \"blablabla@gmail.com\", \"primary\": true, \"verified\": true, \"visibility\": \"public\" }, { \"email\": \"blablabla@users.noreply.github.com\", \"primary\": false, \"verified\": true, \"visibility\": null } ], \"email_verified\": true, \"user_id\": \"github|0000000\", \"identities\": [ { \"provider\": \"github\", \"user_id\": \"000000000\", \"connection\": \"github\", \"isSocial\": true } ], \"created_at\": \"2016-10-22T22:38:20.437Z\", \"blog\": \"\", \"node_id\": \"blablabla=\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.198\", \"last_login\": \"2018-09-20T18:47:43.491Z\", \"logins_count\": 71, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "nih-gov-prod-access-token","{ \"email\": \"NOBODY\", \"sessionIndex\": \"blablablabla\", \"UserPrincipalName\": \"\", \"Mail\": \"\", \"FirstName\": \"\", \"LastName\": \"\", \"MiddleName\": \"\", \"NEDID\": \"\", \"nameIdAttributes\": { \"value\": \"NOBODY\", \"Format\": \"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" }, \"authenticationmethod\": \"urn:oasis:names:tc:SAML:2.0:ac:classes:Password\", \"issuer\": \"https://auth.nih.gov/IDP\", \"updated_at\": \"2018-07-23T19:32:51.505Z\", \"name\": \"\", \"picture\": \"https://cdn.auth0.com/avatars/default.png\", \"user_id\": \"samlp|NOBODY\", \"nickname\": \"\", \"identities\": [ { \"user_id\": \"NOBODY\", \"provider\": \"samlp\", \"connection\": \"nih-gov-prod\", \"isSocial\": false } ], \"created_at\": \"2018-04-02T13:10:25.654Z\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.195\", \"last_login\": \"2018-07-23T19:32:51.254Z\", \"logins_count\": 12, \"blocked_for\": [], \"guardian_authenticators\": []}",
- "no-mapping-connection-token","{ \"email\": \"foo@bar.com\", \"UserName\": \"foooo\", \"FirstName\": \"foo\", \"LastName\": \"oooo\",\"user_id\": \"samlp|fooBar\", \"identities\": [ { \"user_id\": \"fooBar\", \"provider\": \"samlp\", \"connection\": \"no-mapping-connection\", \"isSocial\": false } ]}",
- "invalid-path-token","{ \"email\": \"bar@foo.com\", \"UserName\": \"bahh\", \"user_id\": \"samlp|barFoo\", \"identities\": [ { \"user_id\": \"barFoo\", \"provider\": \"samlp\", \"connection\": \"invalid-path\", \"isSocial\": true } ]}",
- "no-user-token","{ \"email\": \"no@user.com\", \"UserName\": \"nooooooo\", \"user_id\": \"samlp|noUser\", \"identities\": [ { \"user_id\": \"noUser\", \"provider\": \"samlp\", \"connection\": \"no-user-connection\", \"isSocial\": false } ]}"
+ "github-access-token", "{ \"email\": \"blablabla@gmail.com\", \"name\": \"Some Girl\", \"picture\": \"https://avatars3.githubusercontent.com/u/0000000000?v=4\", \"nickname\": \"blablabla\", \"gravatar_id\": \"\", \"url\": \"https://api.github.com/users/blablabla\", \"html_url\": \"https://github.com/blablabla\", \"followers_url\": \"https://api.github.com/users/blablabla/followers\", \"following_url\": \"https://api.github.com/users/blablabla/following{/other_user}\", \"gists_url\": \"https://api.github.com/users/blablabla/gists{/gist_id}\", \"starred_url\": \"https://api.github.com/users/blablabla/starred{/owner}{/repo}\", \"subscriptions_url\": \"https://api.github.com/users/blablabla/subscriptions\", \"organizations_url\": \"https://api.github.com/users/blablabla/orgs\", \"repos_url\": \"https://api.github.com/users/blablabla/repos\", \"events_url\": \"https://api.github.com/users/blablabla/events{/privacy}\", \"received_events_url\": \"https://api.github.com/users/blablabla/received_events\", \"type\": \"User\", \"site_admin\": false, \"location\": \"Nowhere, USA\", \"hireable\": true, \"public_repos\": 8, \"public_gists\": \"0\", \"followers\": 3, \"following\": 1, \"updated_at\": \"2018-09-20T18:47:43.703Z\", \"emails\": [ { \"email\": \"blablabla@gmail.com\", \"primary\": true, \"verified\": true, \"visibility\": \"public\" }, { \"email\": \"blablabla@users.noreply.github.com\", \"primary\": false, \"verified\": true, \"visibility\": null } ], \"email_verified\": true, \"user_id\": \"github|0000000\", \"identities\": [ { \"provider\": \"github\", \"user_id\": \"000000000\", \"connection\": \"github\", \"isSocial\": true } ], \"created_at\": \"2016-10-22T22:38:20.437Z\", \"blog\": \"\", \"node_id\": \"blablabla=\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.198\", \"last_login\": \"2018-09-20T18:47:43.491Z\", \"logins_count\": 71, \"blocked_for\": [], \"guardian_authenticators\": []}",
+ "nih-gov-prod-access-token", "{ \"email\": \"NOBODY\", \"sessionIndex\": \"blablablabla\", \"UserPrincipalName\": \"\", \"Mail\": \"\", \"FirstName\": \"\", \"LastName\": \"\", \"MiddleName\": \"\", \"NEDID\": \"\", \"nameIdAttributes\": { \"value\": \"NOBODY\", \"Format\": \"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" }, \"authenticationmethod\": \"urn:oasis:names:tc:SAML:2.0:ac:classes:Password\", \"issuer\": \"https://auth.nih.gov/IDP\", \"updated_at\": \"2018-07-23T19:32:51.505Z\", \"name\": \"\", \"picture\": \"https://cdn.auth0.com/avatars/default.png\", \"user_id\": \"samlp|NOBODY\", \"nickname\": \"\", \"identities\": [ { \"user_id\": \"NOBODY\", \"provider\": \"samlp\", \"connection\": \"nih-gov-prod\", \"isSocial\": false } ], \"created_at\": \"2018-04-02T13:10:25.654Z\", \"app_metadata\": { \"roles\": [ \"ROLE_CITI_USER\" ] }, \"last_ip\": \"134.174.140.195\", \"last_login\": \"2018-07-23T19:32:51.254Z\", \"logins_count\": 12, \"blocked_for\": [], \"guardian_authenticators\": []}",
+ "no-mapping-connection-token", "{ \"email\": \"foo@bar.com\", \"UserName\": \"foooo\", \"FirstName\": \"foo\", \"LastName\": \"oooo\",\"user_id\": \"samlp|fooBar\", \"identities\": [ { \"user_id\": \"fooBar\", \"provider\": \"samlp\", \"connection\": \"no-mapping-connection\", \"isSocial\": false } ]}",
+ "invalid-path-token", "{ \"email\": \"bar@foo.com\", \"UserName\": \"bahh\", \"user_id\": \"samlp|barFoo\", \"identities\": [ { \"user_id\": \"barFoo\", \"provider\": \"samlp\", \"connection\": \"invalid-path\", \"isSocial\": true } ]}",
+ "no-user-token", "{ \"email\": \"no@user.com\", \"UserName\": \"nooooooo\", \"user_id\": \"samlp|noUser\", \"identities\": [ { \"user_id\": \"noUser\", \"provider\": \"samlp\", \"connection\": \"no-user-connection\", \"isSocial\": false } ]}"
);
- String result = map.get(accessToken);
+ String result = map.get(accessToken);
Map jsonMap = mapper.readValue(result,
- new TypeReference
*/
@Api
-@Path("/role")
-public class RoleController extends BaseEntityService {
+@Controller
+@RequestMapping(value = "/role")
+public class RoleController {
- Logger logger = LoggerFactory.getLogger(RoleController.class);
+ private final RoleService roleService;
- @Context
- SecurityContext securityContext;
-
- @Inject
- RoleRepository roleRepo;
-
- @Inject
- PrivilegeRepository privilegeRepo;
-
- public RoleController() {
- super(Role.class);
+ @Autowired
+ public RoleController(RoleService roleService) {
+ this.roleService = roleService;
}
@ApiOperation(value = "GET information of one Role with the UUID, requires ADMIN or SUPER_ADMIN role")
- @GET
- @Path("/{roleId}")
@RolesAllowed({ADMIN, SUPER_ADMIN})
+ @GetMapping(produces = "application/json", path = "/{roleId}")
public ResponseEntity> getRoleById(
@ApiParam(value="The UUID of the Role to fetch information about")
- @PathParam("roleId") String roleId) {
- return getEntityById(roleId,roleRepo);
+ @PathVariable("roleId") String roleId) {
+ return this.roleService.getEntityById(roleId);
}
@ApiOperation(value = "GET a list of existing Roles, requires ADMIN or SUPER_ADMIN role")
- @GET
- @Path("")
+ @GetMapping(produces = "application/json")
@RolesAllowed({ADMIN, SUPER_ADMIN})
public ResponseEntity> getRoleAll() {
- return getEntityAll(roleRepo);
+ return this.roleService.getEntityAll();
}
@ApiOperation(value = "POST a list of Roles, requires SUPER_ADMIN role")
- @Transactional
- @POST
@RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
+ @PostMapping(produces = "application/json")
public ResponseEntity> addRole(
@ApiParam(required = true, value = "A list of Roles in JSON format")
List roles){
- checkPrivilegeAssociation(roles);
- return addEntity(roles, roleRepo);
+ return this.roleService.addEntity(roles);
}
@ApiOperation(value = "Update a list of Roles, will only update the fields listed, requires SUPER_ADMIN role")
- @Transactional
- @PUT
@RolesAllowed({SUPER_ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
+ @PutMapping(produces = "application/json")
public ResponseEntity> updateRole(
@ApiParam(required = true, value = "A list of Roles with fields to be updated in JSON format")
List roles){
- checkPrivilegeAssociation(roles);
- return updateEntity(roles, roleRepo);
+ return this.roleService.updateEntity(roles);
}
@ApiOperation(value = "DELETE an Role by Id only if the Role is not associated by others, requires SUPER_ADMIN role")
- @Transactional
- @DELETE
@RolesAllowed({SUPER_ADMIN})
- @Path("/{roleId}")
+ @DeleteMapping(produces = "application/json", path = "/{roleId}")
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid Role Id")
- @PathParam("roleId") final String roleId) {
- Role role = roleRepo.getById(UUID.fromString(roleId));
- if (JAXRSConfiguration.defaultAdminRoleName.equals(role.getName())){
- logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext)
- + ", is trying to remove the default system role: " + JAXRSConfiguration.defaultAdminRoleName);
- return PICSUREResponse.protocolError("Default System Role cannot be removed - uuid: " + role.getUuid().toString()
- + ", name: " + role.getName());
- }
- return removeEntityById(roleId, roleRepo);
+ @PathVariable("roleId") final String roleId) {
+ return this.roleService.removeEntityById(roleId);
}
- /**
- * check if the privileges under role is in the database or not,
- * then retrieve it from database and attach it to role object
- *
- * @param roles
- * @return
- */
- private void checkPrivilegeAssociation(List roles){
- for (Role role: roles){
- if (role.getPrivileges() != null) {
- Set privileges = new HashSet<>();
- role.getPrivileges().stream().forEach(p -> privilegeRepo.addObjectToSet(privileges, privilegeRepo, p));
- role.setPrivileges(privileges);
- }
- }
-
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
index f4125a8e8..577ecbd7e 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TermsOfSerivceController.java
@@ -1,69 +1,65 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
+import jakarta.annotation.security.RolesAllowed;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
/**
* Endpoint for creating and updating terms of service entities. Records when a user accepts a term of service.
*/
-@Path("tos")
-public class TermsOfSerivceController extends BaseEntityService {
+@Controller
+@RequestMapping("/tos")
+public class TermsOfSerivceController {
- public TermsOfSerivceController() {
- super(TermsOfService.class);
- }
+ private final TOSService tosService;
- @Inject
- TOSService tosService;
+ @Autowired
+ public TermsOfSerivceController(TOSService tosService) {
+ this.tosService = tosService;
+ }
@ApiOperation(value = "GET the latest Terms of Service")
- @Path("/latest")
- @GET
- @Produces("text/html")
+ @GetMapping(path = "/latest", produces = "text/html")
public ResponseEntity> getLatestTermsOfService(){
- return Response.ok(tosService.getLatest()).build();
+ return PICSUREResponse.success(tosService.getLatest());
}
@ApiOperation(value = "Update the Terms of Service html body")
- @POST
@RolesAllowed({AuthNaming.AuthRoleNaming.ADMIN, SUPER_ADMIN})
- @Consumes("text/html")
- @Produces("application/json")
+ @PostMapping(path = "/update", consumes = "text/html", produces = "application/json")
public ResponseEntity> updateTermsOfService(
@ApiParam(required = true, value = "A html page for updating") String html){
- return Response.status(201).entity(tosService.updateTermsOfService(html)).build();
+ return PICSUREResponse.success(tosService.updateTermsOfService(html));
}
@ApiOperation(value = "GET if current user has acceptted his TOS or not")
- @Path("/")
- @GET
- @Produces("text/plain")
- public ResponseEntity> hasUserAcceptedTOS(@Context SecurityContext securityContext){
- String userSubject = securityContext.getUserPrincipal().getName();
- return Response.ok(tosService.hasUserAcceptedLatest(userSubject)).build();
+ @GetMapping(path = "/", produces = "text/plain")
+ public ResponseEntity> hasUserAcceptedTOS(){
+ SecurityContext context = SecurityContextHolder.getContext();
+ String userSubject = context.getAuthentication().getName();
+ return PICSUREResponse.success(tosService.hasUserAcceptedLatest(userSubject));
}
@ApiOperation(value = "Endpoint for current user to accept his terms of service")
- @Path("/accept")
- @POST
- @Produces("application/json")
- public ResponseEntity> acceptTermsOfService(@Context SecurityContext securityContext){
- String userSubject = securityContext.getUserPrincipal().getName();
+ @PostMapping(path = "/accept", produces = "application/json")
+ public ResponseEntity> acceptTermsOfService(){
+ SecurityContext context = SecurityContextHolder.getContext();
+ String userSubject = context.getAuthentication().getName();
tosService.acceptTermsOfService(userSubject);
- return Response.ok().build();
+ return PICSUREResponse.success();
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
index f3da366a1..fdff23928 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/TokenController.java
@@ -1,36 +1,18 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.entity.User;
-import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
-import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthorizationService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jws;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.TokenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.context.SecurityContext;
+import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
-import java.security.Principal;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil.parseToken;
+import java.util.Map;
/**
* Token introspection endpoint called by an application to validate a user's token and permissions by request.
@@ -45,275 +27,26 @@
@Controller("/token")
public class TokenController {
- private Logger logger = LoggerFactory.getLogger(TokenController.class);
-
- private final UserRepository userRepo;
-
- private final AuthorizationService authorizationService;
-
- @Context
- SecurityContext securityContext;
+ private final TokenService tokenService;
@Autowired
- public TokenController(UserRepository userRepo, AuthorizationService authorizationService) {
- this.userRepo = userRepo;
- this.authorizationService = authorizationService;
+ public TokenController(TokenService tokenService) {
+ this.tokenService = tokenService;
}
@ApiOperation(value = "Token introspection endpoint for user to retrieve a valid token")
- @PostMapping("/inspect")
- @Consumes("application/json")
+ @PostMapping(path = "/inspect", produces = "application/json")
public ResponseEntity> inspectToken(
@ApiParam(required = true, value = "A JSON object that at least" +
" include a user the token for validation")
Map inputMap) {
- logger.info("TokenInspect starting...");
- TokenInspection tokenInspection = _inspectToken(inputMap);
- if (tokenInspection.message != null)
- tokenInspection.responseMap.put("message", tokenInspection.message);
-
- logger.info("Finished token introspection.");
- return PICSUREResponse.success(tokenInspection.responseMap);
+ return this.tokenService.inspectToken(inputMap);
}
- /**
- * This endpoint currently is only for a user token to be refreshed.
- * Application token won't work here.
- *
- * @return
- */
@ApiOperation(value = "To refresh current user's token if the user is an active user")
- @GET
- @Path("/refresh")
+ @GetMapping(path = "/refresh", produces = "application/json")
public ResponseEntity> refreshToken(@RequestHeader("Authorization") String authorizationHeader) {
- logger.debug("RefreshToken starting...");
-
- // still need to check if the user is in the database or not,
- // just in case something changes in the middle
- Principal principal = securityContext.getUserPrincipal();
- if (!(principal instanceof User)) {
- logger.error("refreshToken() Security context didn't have a user stored.");
- }
- User user = (User) principal;
-
- if (user.getUuid() == null) {
- logger.error("refreshToken() Stored user doesn't have a uuid.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- user = userRepo.getById(user.getUuid());
- if (user == null) {
- logger.error("refreshToken() When retrieving current user, it returned null, the user might be removed from database");
- throw new NotAuthorizedException("User doesn't exist anymore");
- }
-
- if (!user.isActive()) {
- logger.error("refreshToken() The user has just been deactivated.");
- throw new NotAuthorizedException("User has been deactivated.");
- }
-
- String subject = user.getSubject();
- if (subject == null || subject.isEmpty()) {
- logger.error("refreshToken() subject doesn't exist in the user.");
- }
-
- // parse origin token
- Jws jws;
- try {
- String token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader).orElseThrow(() -> new NotAuthorizedException("Token not found"));
- jws = parseToken(token);
-
- } catch (NotAuthorizedException ex) {
- return PICSUREResponse.protocolError("Cannot parse original token");
- }
-
- Claims claims = jws.getBody();
-
- // just check if the subject is along with the database record,
- // just in case something has changed in middle
- if (!subject.equals(claims.getSubject())) {
- logger.error("refreshToken() user subject is not the same as the subject of the input token");
- return PICSUREResponse.applicationError("Inner application error, try again or contact admin.");
- }
-
- Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
- String refreshedToken = JWTUtil.createJwtToken(JAXRSConfiguration.clientSecret,
- claims.getId(),
- claims.getIssuer(),
- claims,
- subject,
- JAXRSConfiguration.tokenExpirationTime);
-
- logger.debug("Finished RefreshToken and new token has been generated.");
- return PICSUREResponse.success(Map.of(
- "token", refreshedToken,
- "expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString()
- ));
- }
-
-
- /**
- * @param inputMap
- * @return
- */
- private TokenInspection _inspectToken(Map inputMap) {
- logger.debug("_inspectToken, the incoming token map is: {}", inputMap.entrySet()
- .stream()
- .map(entry -> entry.getKey() + " - " + entry.getValue())
- .collect(Collectors.joining(", ")));
-
- TokenInspection tokenInspection = new TokenInspection();
-
- String token = (String) inputMap.get("token");
- if (token == null || token.isEmpty()) {
- logger.error("Token - " + token + " is blank");
- tokenInspection.message = "Token not found";
- return tokenInspection;
- }
-
- // parse the token based on client secret
- // don't need to check if jws is null or not, since parse function has already checked
- Jws jws;
- try {
- jws = parseToken(token);
-
- /*
- * token has been verified, now we remove it from inputMap, so further logs will not be able to log
- * the token accidentally!
- */
- inputMap.remove("token");
- } catch (NotAuthorizedException ex) {
- // only when the token is for sure invalid, we can dump it into the log.
- logger.error("_inspectToken() the token - " + token + " - is invalid with exception: " + ex.getMessage());
- tokenInspection.message = ex.getMessage();
- return tokenInspection;
- }
-
-
- Application application;
-
- try {
- application = (Application) securityContext.getUserPrincipal();
- } catch (ClassCastException ex) {
- logger.error(securityContext.getUserPrincipal().getName()
- + " - " + securityContext.getUserPrincipal().getClass().getSimpleName() +
- " - is trying to use token introspection endpoint" +
- ", but it is not an application");
- throw new ApplicationException("The application token does not associate with an application but "
- + securityContext.getUserPrincipal().getClass().getSimpleName());
- }
-
- // application null check should be finished when application token goes through the JWTFilter authentication process,
- // here we just double check it to prevent a null application object goes further.
- if (application == null) {
- logger.error("_inspectToken() There is no application in securityContext, which shall not be.");
- throw new ApplicationException("Inner application error, please ask admin to check the log.");
- }
-
- String subject = jws.getBody().getSubject();
-
- // get the user based on subject field in token
- User user;
-
- // check if the token is the special LONG_TERM_TOKEN,
- // the differences between this special token and normal token is
- // one user only has one long_term_token stored in database,
- // this token needs to be exactly the same as the database one.
- // If the token refreshed, the old one will be invalid. But normal
- // token will not invalid the old ones if refreshed.
- boolean isLongTermToken = false;
- if (subject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
- subject = subject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
- isLongTermToken = true;
- }
-
- user = userRepo.getUniqueResultByColumn("subject", subject);
- logger.info("_inspectToken() user with subject - " + subject + " - exists in database");
- if (user == null) {
- logger.error("_inspectToken() could not find user with subject " + subject);
- tokenInspection.message = "user doesn't exist";
- return tokenInspection;
- }
-
-
- //Essentially we want to return jws.getBody() with an additional active: true field
- //only under certain circumstances, the token will return active
- boolean isAuthorizationPassed = false;
- String errorMsg = null;
-
- // long term token needs to be the same as the token in the database user table, if
- // not the token might has been compromised, which will not go through the authorization check
- boolean isLongTermTokenCompromised = false;
- if (isLongTermToken && !token.equals(user.getToken())) {
- // in long_term_token mode, the token needs to be exactly the same as the token in user table
- isLongTermTokenCompromised = true;
- logger.error("_inspectToken User " + user.getUuid() + "|" + user.getSubject()
- + "is sending a long term token that is not matching the record in database user table.");
- errorMsg = "Cannot find matched long term token, your token might have been refreshed.";
- }
-
- // we go through the authorization layer check only if we need to in order to improve the performance
- // the logic here, if the token associated with a user, we will start the authorization check.
- // If the current application has at least one privilege, the user must have one privilege associated to the application
- // pass the accessRule check if there is any accessRules associated with.
- if (application.getPrivileges() == null || application.getPrivileges().isEmpty()) {
- // if no privileges associated
- isAuthorizationPassed = true;
- //we still want to log this, though.
- logger.info("ACCESS_LOG ___ " + user.getUuid() + "," + user.getEmail() + "," + user.getName() +
- " ___ has been granted access to execute query ___ " + inputMap.get("request") + " ___ in application ___ " + application.getName()
- + " ___ NO APP PRIVILEGES DEFINED");
- } else if (!isLongTermTokenCompromised
- && user.getRoles() != null
- // The protocol between applications and PSAMA is application will
- // attach everything that needs to be verified in request field of inputMap
- // besides token. So here we should attach everything in request.
- && authorizationService.isAuthorized(application, inputMap.get("request"), user)) {
- isAuthorizationPassed = true;
- } else {
- // if isLongTermTokenCompromised flag is true,
- // the error message has already been set previously
- if (!isLongTermTokenCompromised)
- errorMsg = "User doesn't have enough privileges.";
- }
-
- if (isAuthorizationPassed) {
- tokenInspection.responseMap.put("active", true);
- ArrayList roles = new ArrayList();
- for (Privilege p : user.getTotalPrivilege()) {
- roles.add(p.getName());
- }
- tokenInspection.responseMap.put("roles", String.join(",", roles));
- } else {
- if (errorMsg != null)
- tokenInspection.message = errorMsg;
- return tokenInspection;
- }
-
- tokenInspection.responseMap.putAll(jws.getBody());
-
- // attach all privileges associated with the application to the responseMap
- tokenInspection.responseMap.put("privileges", user.getPrivilegeNameSetByApplication(application));
-
-
- logger.info("_inspectToken() Successfully inspect and return response map: "
- + tokenInspection.responseMap.entrySet()
- .stream()
- .map(entry -> entry.getKey() + " - " + entry.getValue())
- .collect(Collectors.joining(", ")));
- return tokenInspection;
- }
-
- /**
- * inner used token introspection class with active:false included
- */
- private class TokenInspection {
- Map responseMap = new HashMap<>();
- String message = null;
-
- public TokenInspection() {
- responseMap.put("active", false);
- }
+ return this.tokenService.refreshToken(authorizationHeader);
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
index 532079cf1..851120dc1 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
@@ -1,18 +1,12 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponseOKwithMsgAndContent;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.*;
-import edu.harvard.hms.dbmi.avillach.auth.repository.ApplicationRepository;
-import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.MailService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
import edu.harvard.hms.dbmi.avillach.auth.utils.JsonUtils;
@@ -21,22 +15,22 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import jakarta.annotation.security.RolesAllowed;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.*;
-import javax.mail.MessagingException;
import javax.transaction.Transactional;
-import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.*;
-import java.util.stream.Collectors;
-import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.*;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
@@ -45,222 +39,52 @@
*/
@Api
@Controller("/user")
-public class UserController extends BaseEntityService {
+public class UserController {
- Logger logger = LoggerFactory.getLogger(UserController.class);
+ private final static Logger logger = LoggerFactory.getLogger(UserController.class);
-// @Context
-// SecurityContext securityContext;
+ private final UserService userService;
- private final RoleRepository roleRepo;
-
- private final ConnectionRepository connectionRepo;
-
- private final ApplicationRepository applicationRepo;
-
- private final AuthUtils authUtil;
-
- private MailService mailService;
+ private final TOSService tosService;
@Autowired
- public UserController(RoleRepository roleRepo, ConnectionRepository connectionRepo, ApplicationRepository applicationRepo, AuthUtils authUtil) {
- super(User.class);
- this.roleRepo = roleRepo;
- this.connectionRepo = connectionRepo;
- this.applicationRepo = applicationRepo;
- this.authUtil = authUtil;
+ public UserController(UserService userService, TOSService tosService) {
+ this.userService = userService;
+ this.tosService = tosService;
}
@ApiOperation(value = "GET information of one user with the UUID, requires ADMIN or SUPER_ADMIN roles")
- @GET
@RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("/{userId}")
+ @GetMapping(path = "/{userId}", produces = "application/json")
public ResponseEntity> getUserById(
@ApiParam(required = true, value = "The UUID of the user to fetch information about")
- @PathParam("userId") String userId) {
- return getEntityById(userId, userRepo);
+ @PathVariable("userId") String userId) {
+ return this.userService.getEntityById(userId);
}
@ApiOperation(value = "GET a list of existing users, requires ADMIN or SUPER_ADMIN roles")
- @GET
@RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("")
+ @GetMapping(produces = "application/json")
public ResponseEntity> getUserAll() {
- return getEntityAll(userRepo);
+ return this.userService.getEntityAll();
}
@ApiOperation(value = "POST a list of users, requires ADMIN role")
- @Transactional
- @POST
+ @Transactional // TODO: Move this to the service layer
@RolesAllowed({ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
+ @PostMapping(produces = "application/json")
public ResponseEntity> addUser(
@ApiParam(required = true, value = "A list of user in JSON format")
List users) {
- User currentUser = (User) securityContext.getUserPrincipal();
- if (currentUser == null || currentUser.getUuid() == null) {
- logger.error("Security context didn't have a user stored.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- checkAssociation(users);
-
- boolean allowAdd = true;
- for (User user : users) {
- logger.debug("Adding User " + user);
- if (!allowUpdateSuperAdminRole(currentUser, user, null)) {
- allowAdd = false;
- break;
- }
-
- if (user.getEmail() == null) {
- try {
- HashMap metadata = new HashMap(new ObjectMapper().readValue(user.getGeneralMetadata(), Map.class));
- List emailKeys = metadata.keySet().stream().filter((key) -> {
- return key.toLowerCase().contains("email");
- }).collect(Collectors.toList());
- if (emailKeys.size() > 0) {
- user.setEmail(metadata.get(emailKeys.get(0)));
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
+ return this.userService.addUsers(users);
- if (allowAdd) {
- Response updateResponse = addEntity(users, userRepo);
- sendUserUpdateEmailsFromResponse(updateResponse);
- return updateResponse;
- } else {
- logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant "
- + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " role when adding a user.");
- throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to add a user with a " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege associated.");
- }
}
@ApiOperation(value = "Update a list of users, will only update the fields listed, requires ADMIN role")
- @Transactional
- @PUT
@RolesAllowed({ADMIN})
- @Consumes(MediaType.APPLICATION_JSON)
- @Path("/")
+ @PutMapping(produces = "application/json")
public ResponseEntity> updateUser(List users) {
- User currentUser = (User) securityContext.getUserPrincipal();
- if (currentUser == null || currentUser.getUuid() == null) {
- logger.error("Security context didn't have a user stored.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- checkAssociation(users);
-
- boolean allowUpdate = true;
- for (User user : users) {
-
- User originalUser = userRepo.getById(user.getUuid());
- if (allowUpdateSuperAdminRole(currentUser, user, originalUser)) {
- continue;
- } else {
- allowUpdate = false;
- break;
- }
- }
-
- if (allowUpdate) {
- Response updateResponse = updateEntity(users, userRepo);
- sendUserUpdateEmailsFromResponse(updateResponse);
- return updateResponse;
- } else {
- logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant or remove "
- + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
- throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to update a user with changes associated to " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
- }
- }
-
- private void sendUserUpdateEmailsFromResponse(Response updateResponse) {
- logger.debug("Sending email");
- try {
- Object entity = updateResponse.getEntity();
- if (entity != null && entity instanceof PICSUREResponseOKwithMsgAndContent) {
- PICSUREResponseOKwithMsgAndContent okResponse = (PICSUREResponseOKwithMsgAndContent) entity;
- List addedUsers = (List) okResponse.getContent();
- String message = okResponse.getMessage();
- for (User user : addedUsers) {
- try {
- mailService.sendUsersAccessEmail(user);
- } catch (MessagingException e) {
- logger.error("Failed to send email! " + e.getLocalizedMessage());
- logger.debug("Exception Trace: ", e);
- okResponse.setMessage(message + " WARN - could not send email to user " + user.getEmail() + " see logs for more info");
- }
- }
- }
- } catch (Exception e) {
- logger.error("Failed to send email - unhandled exception: ", e);
- }
- logger.debug("finished email sending method");
- }
-
- /**
- * This check is to prevent non-super-admin user to create/remove a super admin role
- * against a user(include themselves). Only super admin user could perform such actions.
- *
- *
- * if operations not related to super admin role updates, this will return true.
- *
- *
- * The logic here is checking the state of the super admin role in the input and output users,
- * if the state is changed, check if the user is a super admin to determine if the user could perform the action.
- *
- * @param currentUser the user trying to perform the action
- * @param inputUser
- * @param originalUser there could be no original user when adding a new user
- * @return
- */
- private boolean allowUpdateSuperAdminRole(
- @NotNull User currentUser,
- @NotNull User inputUser,
- User originalUser) {
-
- // if current user is a super admin, this check will return true
- for (Role role : currentUser.getRoles()) {
- for (Privilege privilege : role.getPrivileges()) {
- if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
- return true;
- }
- }
- }
-
- boolean inputUserHasSuperAdmin = false;
- boolean originalUserHasSuperAdmin = false;
-
- for (Role role : inputUser.getRoles()) {
- for (Privilege privilege : role.getPrivileges()) {
- if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
- inputUserHasSuperAdmin = true;
- break;
- }
- }
- }
-
- if (originalUser != null) {
- for (Role role : originalUser.getRoles()) {
- for (Privilege privilege : role.getPrivileges()) {
- if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
- originalUserHasSuperAdmin = true;
- break;
- }
- }
- }
-
- // when they equals, nothing has changed, a non super admin user could perform the action
- return inputUserHasSuperAdmin == originalUserHasSuperAdmin;
- } else {
- // if inputUser has super admin, it should return false
- return !inputUserHasSuperAdmin;
- }
-
+ return this.userService.updateUser(users);
}
/**
@@ -268,19 +92,18 @@ private boolean allowUpdateSuperAdminRole(
* every time a user hit this endpoint /me
with the query parameter ?hasToken presented,
* it will refresh the long term token.
*
- * @param httpHeaders
* @param hasToken
* @return
*/
@ApiOperation(value = "Retrieve information of current user")
- @Transactional
- @GET
- @Path("/me")
+ @Transactional // TODO: Move this to the service layer
+ @GetMapping(produces = "application/json", path = "/me")
public ResponseEntity> getCurrentUser(
@RequestHeader("Authorization") String authorizationHeader,
@ApiParam(required = false, value = "Attribute that represents if a long term token will attach to the response")
- @QueryParam("hasToken") Boolean hasToken) {
- User user = (User) securityContext.getUserPrincipal();
+ @RequestParam("hasToken") Boolean hasToken) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User user = (User) securityContext.getAuthentication().getPrincipal();
if (user == null || user.getUuid() == null) {
logger.error("Security context didn't have a user stored.");
return PICSUREResponse.applicationError("Inner application error, please contact admin.");
@@ -296,7 +119,7 @@ public ResponseEntity> getCurrentUser(
.setEmail(user.getEmail())
.setPrivileges(user.getPrivilegeNameSet())
.setUuid(user.getUuid().toString())
- .setAcceptedTOS(authUtil.acceptedTOSBySub(user.getSubject()));
+ .setAcceptedTOS(this.tosService.hasUserAcceptedLatest(user.getSubject()));
// currently, the queryScopes are simple combination of queryScope string together as a set.
// We are expecting the queryScope string as plain string. If it is a JSON, we could change the
@@ -494,28 +317,7 @@ private String generateUserLongTermToken(String authorizationHeader) {
}
- /**
- * check all referenced field if they are already in database. If
- * they are in database, then retrieve it by id, and attach it to
- * user object.
- *
- * @param users
- * @return
- */
- private void checkAssociation(List users) {
- for (User user : users) {
- if (user.getRoles() != null) {
- Set roles = new HashSet<>();
- user.getRoles().stream().forEach(t -> roleRepo.addObjectToSet(roles, roleRepo, t));
- user.setRoles(roles);
- }
- if (user.getConnection() != null) {
- Connection connection = connectionRepo.getUniqueResultByColumn("id", user.getConnection().getId());
- user.setConnection(connection);
- }
- }
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java
new file mode 100644
index 000000000..a531e0f6f
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java
@@ -0,0 +1,96 @@
+package edu.harvard.hms.dbmi.avillach.auth.service;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.enums.SecurityRoles;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+@Service
+public class RoleService extends BaseEntityService {
+
+ private final static Logger logger = Logger.getLogger(RoleService.class.getName());
+ private final RoleRepository roleRepository;
+
+ private final PrivilegeRepository privilegeRepo;
+
+ @Autowired
+ protected RoleService(Class type, RoleRepository roleRepository, PrivilegeRepository privilegeRepo) {
+ super(type);
+ this.roleRepository = roleRepository;
+ this.privilegeRepo = privilegeRepo;
+ }
+
+ public ResponseEntity> getEntityById(String roleId) {
+ return getEntityById(roleId, roleRepository);
+ }
+
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(roleRepository);
+ }
+
+ @Transactional
+ public ResponseEntity> addEntity(List roles) {
+ checkPrivilegeAssociation(roles);
+ return addEntity(roles, roleRepository);
+ }
+
+ /**
+ * check if the privileges under role is in the database or not,
+ * then retrieve it from database and attach it to role object
+ *
+ * @param roles list of roles
+ */
+ private void checkPrivilegeAssociation(List roles) throws RuntimeException {
+ for (Role role: roles){
+ if (role.getPrivileges() != null) {
+ Set privileges = new HashSet<>(); // TODO: Determine how we can fix this issue. The javax code does not work with java 21 in this case.
+ role.getPrivileges().stream().forEach(p -> privilegeRepo.addObjectToSet(privileges, privilegeRepo, p));
+ role.setPrivileges(privileges);
+ }
+ }
+
+ }
+
+ @Transactional
+ public ResponseEntity> updateEntity(List roles) {
+ checkPrivilegeAssociation(roles);
+ return updateEntity(roles, roleRepository);
+ }
+
+ @Transactional
+ public ResponseEntity> removeEntityById(String roleId) {
+ Role role = roleRepository.getById(UUID.fromString(roleId));
+
+ // Get principal roles from security context
+ SecurityContext context = SecurityContextHolder.getContext();
+ Set roles = context.getAuthentication().getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
+
+
+ if (SecurityRoles.contains(roles, SecurityRoles.PIC_SURE_TOP_ADMIN.getRole())){
+ logger.info("User has PIC-SURE Top Admin role, can remove any role");
+ return PICSUREResponse.protocolError("Default System Role cannot be removed - uuid: " + role.getUuid().toString()
+ + ", name: " + role.getName());
+ }
+ return removeEntityById(roleId, roleRepository);
+ }
+}
+
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java
new file mode 100644
index 000000000..625fc574b
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java
@@ -0,0 +1,58 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
+import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.List;
+
+@Service
+public class AccessRuleService extends BaseEntityService{
+
+ private final AccessRuleRepository accessRuleRepo;
+
+ @Autowired
+ protected AccessRuleService(Class type, AccessRuleRepository accessRuleRepo) {
+ super(type);
+ this.accessRuleRepo = accessRuleRepo;
+ }
+
+
+ public ResponseEntity> getEntityById(String accessRuleId) {
+ return getEntityById(accessRuleId, accessRuleRepo);
+ }
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(accessRuleRepo);
+ }
+
+ public ResponseEntity> addEntity(List accessRules) {
+ accessRules.forEach(accessRule -> {
+ if (accessRule.getEvaluateOnlyByGates() == null)
+ accessRule.setEvaluateOnlyByGates(false);
+
+ if (accessRule.getCheckMapKeyOnly() == null)
+ accessRule.setCheckMapKeyOnly(false);
+
+ if (accessRule.getCheckMapNode() == null)
+ accessRule.setCheckMapNode(false);
+
+ if (accessRule.getGateAnyRelation() == null)
+ accessRule.setGateAnyRelation(false);
+ });
+
+ return addEntity(accessRules, accessRuleRepo);
+ }
+
+ public ResponseEntity> updateEntity(List accessRules) {
+ return updateEntity(accessRules, accessRuleRepo);
+ }
+
+ @Transactional
+ public ResponseEntity> removeEntityById(String accessRuleId) {
+ return removeEntityById(accessRuleId, accessRuleRepo);
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
index f2a2dbebc..7e60a5971 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
@@ -15,7 +15,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import edu.harvard.dbmi.avillach.util.HttpClientUtil;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
index 4daaef8f4..d9f10d0ef 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
@@ -28,8 +28,6 @@ public abstract class BaseEntityService {
private final String auditLogName;
-// @Context
-// SecurityContext securityContext;
protected BaseEntityService(Class type){
this.type = type;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TokenService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TokenService.java
new file mode 100644
index 000000000..3efaa981c
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/TokenService.java
@@ -0,0 +1,292 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
+import edu.harvard.hms.dbmi.avillach.auth.model.TokenInspection;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
+import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil.parseToken;
+
+@Service
+public class TokenService {
+
+ private final static Logger logger = LoggerFactory.getLogger(TokenService.class);
+
+ private final AuthorizationService authorizationService;
+
+ private final UserRepository userRepository;
+
+ private final long tokenExpirationTime;
+ private final String clientSecret;
+
+ private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour TODO: Move to a global configuration or enum?
+
+ @Autowired
+ public TokenService(AuthorizationService authorizationService, UserRepository userRepository,
+ @Value("${application.client.secret}") String clientSecret,
+ @Value("${application.token.expiration.time}") long tokenExpirationTime) {
+ this.authorizationService = authorizationService;
+ this.userRepository = userRepository;
+ this.clientSecret = clientSecret;
+ this.tokenExpirationTime = tokenExpirationTime > 0 ? tokenExpirationTime : defaultTokenExpirationTime;
+ }
+
+ public ResponseEntity> inspectToken(Map inputMap) {
+ logger.info("TokenInspect starting...");
+ TokenInspection tokenInspection;
+ try {
+ tokenInspection = _inspectToken(inputMap);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ if (tokenInspection.getMessage() != null) {
+ tokenInspection.addField("message", tokenInspection.getMessage());
+ }
+
+ logger.info("Finished token introspection.");
+ return PICSUREResponse.success(tokenInspection.getResponseMap());
+ }
+
+ private TokenInspection _inspectToken(Map inputMap) throws IllegalAccessException {
+ logger.debug("_inspectToken, the incoming token map is: {}", inputMap.entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + " - " + entry.getValue())
+ .collect(Collectors.joining(", ")));
+
+ TokenInspection tokenInspection = new TokenInspection();
+ String token = (String) inputMap.get("token");
+ if (token == null || token.isEmpty()) {
+ logger.error("Token - " + token + " is blank");
+ tokenInspection.setMessage("Token not found");
+ return tokenInspection;
+ }
+
+ // parse the token based on client secret
+ // don't need to check if jws is null or not, since parse function has already checked
+ Jws jws;
+ try {
+ jws = JWTUtil.parseToken(token);
+
+ /*
+ * token has been verified, now we remove it from inputMap, so further logs will not be able to log
+ * the token accidentally!
+ */
+ inputMap.remove("token");
+ } catch (NotAuthorizedException ex) {
+ // only when the token is for sure invalid, we can dump it into the log.
+ logger.error("_inspectToken() the token - " + token + " - is invalid with exception: " + ex.getMessage());
+ tokenInspection.setMessage(ex.getMessage());
+ return tokenInspection;
+ }
+
+
+ Application application;
+ try {
+ application = (Application) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+ } catch (ClassCastException ex) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String principalName = securityContext.getAuthentication().getName();
+ logger.error(principalName
+ + " - " + principalName +
+ " - is trying to use token introspection endpoint" +
+ ", but it is not an application");
+ throw new IllegalAccessException("The application token does not associate with an application but "
+ + principalName);
+ }
+
+ // application null check should be finished when application token goes through the JWTFilter authentication process,
+ // here we just double check it to prevent a null application object goes further.
+ if (application == null) {
+ logger.error("_inspectToken() There is no application in securityContext, which shall not be.");
+ throw new NullPointerException("Inner application error, please ask admin to check the log.");
+ }
+
+ String subject = jws.getBody().getSubject();
+
+ // get the user based on subject field in token
+ User user;
+
+ // check if the token is the special LONG_TERM_TOKEN,
+ // the differences between this special token and normal token is
+ // one user only has one long_term_token stored in database,
+ // this token needs to be exactly the same as the database one.
+ // If the token refreshed, the old one will be invalid. But normal
+ // token will not invalid the old ones if refreshed.
+ boolean isLongTermToken = false;
+ if (subject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
+ subject = subject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
+ isLongTermToken = true;
+ }
+
+ user = this.userRepository.getUniqueResultByColumn("subject", subject);
+ logger.info("_inspectToken() user with subject - " + subject + " - exists in database");
+ if (user == null) {
+ logger.error("_inspectToken() could not find user with subject " + subject);
+ tokenInspection.setMessage("user doesn't exist");
+ return tokenInspection;
+ }
+
+
+ //Essentially we want to return jws.getBody() with an additional active: true field
+ //only under certain circumstances, the token will return active
+ boolean isAuthorizationPassed = false;
+ String errorMsg = null;
+
+ // long term token needs to be the same as the token in the database user table, if
+ // not the token might has been compromised, which will not go through the authorization check
+ boolean isLongTermTokenCompromised = false;
+ if (isLongTermToken && !token.equals(user.getToken())) {
+ // in long_term_token mode, the token needs to be exactly the same as the token in user table
+ isLongTermTokenCompromised = true;
+ logger.error("_inspectToken User " + user.getUuid() + "|" + user.getSubject()
+ + "is sending a long term token that is not matching the record in database user table.");
+ errorMsg = "Cannot find matched long term token, your token might have been refreshed.";
+ }
+
+ // we go through the authorization layer check only if we need to in order to improve the performance
+ // the logic here, if the token associated with a user, we will start the authorization check.
+ // If the current application has at least one privilege, the user must have one privilege associated to the application
+ // pass the accessRule check if there is any accessRules associated with.
+ if (application.getPrivileges() == null || application.getPrivileges().isEmpty()) {
+ // if no privileges associated
+ isAuthorizationPassed = true;
+ //we still want to log this, though.
+ logger.info("ACCESS_LOG ___ " + user.getUuid() + "," + user.getEmail() + "," + user.getName() +
+ " ___ has been granted access to execute query ___ " + inputMap.get("request") + " ___ in application ___ " + application.getName()
+ + " ___ NO APP PRIVILEGES DEFINED");
+ } else if (!isLongTermTokenCompromised
+ && user.getRoles() != null
+ // The protocol between applications and PSAMA is application will
+ // attach everything that needs to be verified in request field of inputMap
+ // besides token. So here we should attach everything in request.
+ && authorizationService.isAuthorized(application, inputMap.get("request"), user)) {
+ isAuthorizationPassed = true;
+ } else {
+ // if isLongTermTokenCompromised flag is true,
+ // the error message has already been set previously
+ if (!isLongTermTokenCompromised)
+ errorMsg = "User doesn't have enough privileges.";
+ }
+
+ if (isAuthorizationPassed) {
+ tokenInspection.addField("active", true);
+ ArrayList roles = new ArrayList();
+ for (Privilege p : user.getTotalPrivilege()) {
+ roles.add(p.getName());
+ }
+ tokenInspection.addField("roles", String.join(",", roles));
+ } else {
+ tokenInspection.setMessage(errorMsg);
+ return tokenInspection;
+ }
+
+ tokenInspection.addAllFields(jws.getBody());
+
+ // attach all privileges associated with the application to the responseMap
+ tokenInspection.addField("privileges", user.getPrivilegeNameSetByApplication(application));
+
+
+ logger.info("_inspectToken() Successfully inspect and return response map: "
+ + tokenInspection.getResponseMap().entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + " - " + entry.getValue())
+ .collect(Collectors.joining(", ")));
+ return tokenInspection;
+ }
+
+ public ResponseEntity> refreshToken(String authorizationHeader) {
+ logger.debug("RefreshToken starting...");
+
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Object principal = authentication.getPrincipal();
+ if (!(principal instanceof User)) {
+ logger.error("refreshToken() Security context didn't have a user stored.");
+ }
+
+ if (!(principal instanceof User user)) {
+ logger.error("refreshToken() Principal is not an instance of User.");
+ throw new NotAuthorizedException("User not found");
+ }
+
+ if (user.getUuid() == null) {
+ logger.error("refreshToken() Stored user doesn't have a uuid.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ user = this.userRepository.getById(user.getUuid());
+ if (user == null) {
+ logger.error("refreshToken() When retrieving current user, it returned null, the user might be removed from database");
+ throw new NotAuthorizedException("User doesn't exist anymore");
+ }
+
+ if (!user.isActive()) {
+ logger.error("refreshToken() The user has just been deactivated.");
+ throw new NotAuthorizedException("User has been deactivated.");
+ }
+
+ String subject = user.getSubject();
+ if (subject == null || subject.isEmpty()) {
+ logger.error("refreshToken() subject doesn't exist in the user.");
+ }
+
+ // parse origin token
+ Jws jws;
+ try {
+ String token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader).orElseThrow(() -> new NotAuthorizedException("Token not found"));
+ jws = parseToken(token);
+
+ } catch (NotAuthorizedException ex) {
+ return PICSUREResponse.protocolError("Cannot parse original token");
+ }
+
+ Claims claims = jws.getBody();
+
+ // just check if the subject is along with the database record,
+ // just in case something has changed in middle
+ if (StringUtils.isNotBlank(subject) && !subject.equals(claims.getSubject())) {
+ logger.error("refreshToken() user subject is not the same as the subject of the input token");
+ return PICSUREResponse.applicationError("Inner application error, try again or contact admin.");
+ }
+
+ Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + this.tokenExpirationTime);
+ String refreshedToken = JWTUtil.createJwtToken(this.clientSecret,
+ claims.getId(),
+ claims.getIssuer(),
+ claims,
+ subject,
+ this.tokenExpirationTime);
+
+ logger.debug("Finished RefreshToken and new token has been generated.");
+ return PICSUREResponse.success(Map.of(
+ "token", refreshedToken,
+ "expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString()
+ ));
+ }
+
+
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
index 4edc5afef..ad667be2d 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
@@ -1,28 +1,62 @@
package edu.harvard.hms.dbmi.avillach.auth.service.impl;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
+import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
+import edu.harvard.dbmi.avillach.util.response.PICSUREResponseOKwithMsgAndContent;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
+import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
+import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
+import javax.mail.MessagingException;
+import javax.transaction.Transactional;
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Logger;
+import java.util.*;
+import java.util.stream.Collectors;
@Service
-public class UserService {
+public class UserService extends BaseEntityService {
- private final Logger logger = Logger.getLogger(UserService.class.getName());
+ private final Logger logger = LoggerFactory.getLogger(UserService.class.getName());
private final TOSService tosService;
+ private final UserRepository userRepository;
+ private final ConnectionRepository connectionRepository;
+
+ private final RoleRepository roleRepository;
+ private final String clientSecret;
+
+ private final long tokenExpirationTime;
+ private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour TODO: Move to a global configuration or enum?
@Autowired
- public UserService(TOSService tosService) {
+ public UserService(TOSService tosService, UserRepository userRepository, ConnectionRepository connectionRepository, RoleRepository roleRepository,
+ @Value("${application.client.secret}") String clientSecret, @Value("${application.token.expiration.time}") long tokenExpirationTime) {
+ super(User.class);
this.tosService = tosService;
+ this.userRepository = userRepository;
+ this.connectionRepository = connectionRepository;
+ this.roleRepository = roleRepository;
+ this.clientSecret = clientSecret;
+ this.tokenExpirationTime = tokenExpirationTime > 0 ? tokenExpirationTime : defaultTokenExpirationTime;
}
public HashMap getUserProfileResponse(Map claims) {
@@ -34,12 +68,12 @@ public HashMap getUserProfileResponse(Map claims
logger.info("getUserProfileResponse() using claims:" + claims.toString());
String token = JWTUtil.createJwtToken(
- JAXRSConfiguration.clientSecret,
+ this.clientSecret,
"whatever",
"edu.harvard.hms.dbmi.psama",
claims,
claims.get("sub").toString(),
- JAXRSConfiguration.tokenExpirationTime
+ this.tokenExpirationTime
);
logger.info("getUserProfileResponse() PSAMA JWT token has been generated. Token:" + token);
responseMap.put("token", token);
@@ -57,11 +91,204 @@ public HashMap getUserProfileResponse(Map claims
responseMap.put("acceptedTOS", "" + acceptedTOS);
logger.info("getUserProfileResponse() expirationDate is set");
- Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + JAXRSConfiguration.tokenExpirationTime);
+ Date expirationDate = new Date(Calendar.getInstance().getTimeInMillis() + this.tokenExpirationTime);
responseMap.put("expirationDate", ZonedDateTime.ofInstant(expirationDate.toInstant(), ZoneOffset.UTC).toString());
logger.info("getUserProfileResponse() finished");
return responseMap;
}
+ public ResponseEntity> getEntityById(String userId) {
+ return getEntityById(userId, this.userRepository);
+ }
+
+ public ResponseEntity> getEntityAll() {
+ return getEntityAll(this.userRepository);
+ }
+
+ public ResponseEntity> addEntity(List users) {
+ return addEntity(users, this.userRepository);
+
+ }
+
+ /**
+ * This check is to prevent non-super-admin user to create/remove a super admin role
+ * against a user(include themselves). Only super admin user could perform such actions.
+ *
+ *
+ * if operations not related to super admin role updates, this will return true.
+ *
+ *
+ * The logic here is checking the state of the super admin role in the input and output users,
+ * if the state is changed, check if the user is a super admin to determine if the user could perform the action.
+ *
+ * @param currentUser the user trying to perform the action
+ * @param inputUser
+ * @param originalUser there could be no original user when adding a new user
+ * @return
+ */
+ private boolean allowUpdateSuperAdminRole(
+ @NotNull User currentUser,
+ @NotNull User inputUser,
+ User originalUser) {
+
+ // if current user is a super admin, this check will return true
+ for (Role role : currentUser.getRoles()) {
+ for (Privilege privilege : role.getPrivileges()) {
+ if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
+ return true;
+ }
+ }
+ }
+
+ boolean inputUserHasSuperAdmin = false;
+ boolean originalUserHasSuperAdmin = false;
+
+ for (Role role : inputUser.getRoles()) {
+ for (Privilege privilege : role.getPrivileges()) {
+ if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
+ inputUserHasSuperAdmin = true;
+ break;
+ }
+ }
+ }
+
+ if (originalUser != null) {
+ for (Role role : originalUser.getRoles()) {
+ for (Privilege privilege : role.getPrivileges()) {
+ if (privilege.getName().equals(AuthNaming.AuthRoleNaming.SUPER_ADMIN)) {
+ originalUserHasSuperAdmin = true;
+ break;
+ }
+ }
+ }
+
+ // when they equals, nothing has changed, a non super admin user could perform the action
+ return inputUserHasSuperAdmin == originalUserHasSuperAdmin;
+ } else {
+ // if inputUser has super admin, it should return false
+ return !inputUserHasSuperAdmin;
+ }
+
+ }
+
+ public ResponseEntity> addUsers(List users) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User currentUser = (User) securityContext.getAuthentication().getPrincipal();
+ if (currentUser == null || currentUser.getUuid() == null) {
+ logger.error("Security context didn't have a user stored.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ checkAssociation(users);
+ boolean allowAdd = true;
+ for (User user : users) {
+ logger.debug("Adding User " + user);
+ if (!allowUpdateSuperAdminRole(currentUser, user, null)) { // TODO: The allowUpdateSuperAdminRole is a private method
+ allowAdd = false;
+ break;
+ }
+
+ if (user.getEmail() == null) {
+ try {
+ HashMap metadata = new HashMap(new ObjectMapper().readValue(user.getGeneralMetadata(), Map.class));
+ List emailKeys = metadata.keySet().stream().filter((key) -> {
+ return key.toLowerCase().contains("email");
+ }).collect(Collectors.toList());
+ if (emailKeys.size() > 0) {
+ user.setEmail(metadata.get(emailKeys.get(0)));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ if (allowAdd) {
+ ResponseEntity> updateResponse = addEntity(users);
+ sendUserUpdateEmailsFromResponse(updateResponse);
+ return updateResponse;
+ } else {
+ logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant "
+ + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " role when adding a user.");
+ throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to add a user with a " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege associated.");
+ }
+ }
+
+ /**
+ * check all referenced field if they are already in database. If
+ * they are in database, then retrieve it by id, and attach it to
+ * user object.
+ *
+ * @param users A list of users
+ */
+ private void checkAssociation(List users) {
+ for (User user : users) {
+ if (user.getRoles() != null) {
+ Set roles = new HashSet<>();
+ user.getRoles().forEach(t -> this.roleRepository.addObjectToSet(roles, this.roleRepository, t)); // TODO: We need to fix the exception that is thrown here
+ user.setRoles(roles);
+ }
+
+ if (user.getConnection() != null) {
+ Connection connection = this.connectionRepository.getUniqueResultByColumn("id", user.getConnection().getId());
+ user.setConnection(connection);
+ }
+ }
+ }
+
+ @Transactional // TODO: Can this be moved further down the call hierarchy to improve performance?
+ public ResponseEntity> updateUser(List users) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User currentUser = (User) securityContext.getAuthentication().getPrincipal();
+ if (currentUser == null || currentUser.getUuid() == null) {
+ logger.error("Security context didn't have a user stored.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ checkAssociation(users);
+ boolean allowUpdate = true;
+ for (User user : users) {
+
+ User originalUser = this.userRepository.getById(user.getUuid());
+ if (!allowUpdateSuperAdminRole(currentUser, user, originalUser)) {
+ allowUpdate = false;
+ break;
+ }
+ }
+
+ if (allowUpdate) {
+ ResponseEntity> updateResponse = updateEntity(users, this.userRepository);
+ sendUserUpdateEmailsFromResponse(updateResponse);
+ return updateResponse;
+ } else {
+ logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant or remove "
+ + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
+ throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to update a user with changes associated to " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
+ }
+ }
+
+ private void sendUserUpdateEmailsFromResponse(ResponseEntity> updateResponse) {
+ logger.debug("Sending email");
+ try {
+ Object entity = updateResponse.getEntity(); // TODO: Determine how to replicate this given the new approach
+ if (entity != null && entity instanceof PICSUREResponseOKwithMsgAndContent) {
+ PICSUREResponseOKwithMsgAndContent okResponse = (PICSUREResponseOKwithMsgAndContent) entity;
+ List addedUsers = (List) okResponse.getContent();
+ String message = okResponse.getMessage();
+ for (User user : addedUsers) {
+ try {
+ mailService.sendUsersAccessEmail(user);
+ } catch (MessagingException e) {
+ logger.error("Failed to send email! " + e.getLocalizedMessage());
+ logger.debug("Exception Trace: ", e);
+ okResponse.setMessage(message + " WARN - could not send email to user " + user.getEmail() + " see logs for more info");
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Failed to send email - unhandled exception: ", e);
+ }
+ logger.debug("finished email sending method");
+ }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
index 80f979add..ee7683780 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
@@ -9,7 +9,6 @@
import org.springframework.beans.factory.annotation.Value;
import javax.crypto.spec.SecretKeySpec;
-import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
index f8d2fa5d5..a9a6020dc 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
@@ -1,21 +1,11 @@
package edu.harvard.hms.dbmi.avillach.auth.utils;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.type.MapType;
import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.net.ssl.*;
import javax.validation.constraints.NotNull;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
From aa8112e21b217d5db15dcb96e73002e0b784afdb Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Tue, 26 Mar 2024 11:03:15 -0400
Subject: [PATCH 009/222] WIP: Refactored UserController and
UserMetadataMappingWebController
Both classes have been refactored to separate out the controller and service.
---
.../avillach/auth/rest/UserController.java | 204 +-------------
.../UserMetadataMappingWebController.java | 75 ++---
.../impl/UserMetadataMappingService.java | 14 +
.../auth/service/impl/UserService.java | 262 +++++++++++++++---
.../avillach/Auth0MatchingServiceTest.java | 2 +-
5 files changed, 284 insertions(+), 273 deletions(-)
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
index 851120dc1..ae44f612b 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
@@ -3,8 +3,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
-import edu.harvard.dbmi.avillach.util.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.entity.*;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
@@ -102,141 +102,22 @@ public ResponseEntity> getCurrentUser(
@RequestHeader("Authorization") String authorizationHeader,
@ApiParam(required = false, value = "Attribute that represents if a long term token will attach to the response")
@RequestParam("hasToken") Boolean hasToken) {
- SecurityContext securityContext = SecurityContextHolder.getContext();
- User user = (User) securityContext.getAuthentication().getPrincipal();
- if (user == null || user.getUuid() == null) {
- logger.error("Security context didn't have a user stored.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- user = userRepo.getById(user.getUuid());
- if (user == null) {
- logger.error("When retrieving current user, it returned null");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- User.UserForDisplay userForDisplay = new User.UserForDisplay()
- .setEmail(user.getEmail())
- .setPrivileges(user.getPrivilegeNameSet())
- .setUuid(user.getUuid().toString())
- .setAcceptedTOS(this.tosService.hasUserAcceptedLatest(user.getSubject()));
-
- // currently, the queryScopes are simple combination of queryScope string together as a set.
- // We are expecting the queryScope string as plain string. If it is a JSON, we could change the
- // code to use JsonUtils.mergeTemplateMap(Map, Map)
- Set privileges = user.getTotalPrivilege();
- if (privileges != null && !privileges.isEmpty()) {
- Set scopes = new TreeSet<>();
- privileges.stream().filter(privilege -> privilege.getQueryScope() != null).forEach(privilege -> {
- try {
- Arrays.stream(objectMapper.readValue(privilege.getQueryScope(), String[].class))
- .filter(x -> x != null)
- .forEach(scopeList -> scopes.addAll(Arrays.asList(scopeList)));
- } catch (IOException e) {
- logger.error("Parsing issue for privilege " + privilege.getUuid() + " queryScope", e);
- }
- });
- userForDisplay.setQueryScopes(scopes);
- }
-
- if (hasToken != null) {
-
- if (user.getToken() != null && !user.getToken().isEmpty()) {
- userForDisplay.setToken(user.getToken());
- } else {
- user.setToken(generateUserLongTermToken(authorizationHeader));
- userRepo.merge(user);
- userForDisplay.setToken(user.getToken());
- }
- }
-
- return PICSUREResponse.success(userForDisplay);
+ return this.userService.getCurrentUser(authorizationHeader, hasToken);
}
@ApiOperation(value = "Retrieve the queryTemplate of certain application by given application Id for the currentUser ")
- @Transactional
- @GET
- @Path("/me/queryTemplate/{applicationId}")
+ @Transactional // TODO: Move this to the service layer
+ @GetMapping(path = "/me/queryTemplate/{applicationId}", produces = "application/json")
public ResponseEntity> getQueryTemplate(
@ApiParam(value = "Application Id for the returning queryTemplate")
- @PathParam("applicationId") String applicationId) {
-
- if (applicationId == null || applicationId.trim().isEmpty()) {
- logger.error("getQueryTemplate() input application UUID is null or empty.");
- throw new ProtocolException("Input application UUID is incorrect.");
- }
-
- User user = (User) securityContext.getUserPrincipal();
- if (user == null || user.getUuid() == null) {
- logger.error("Security context didn't have a user stored.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- user = userRepo.getById(user.getUuid());
- if (user == null) {
- logger.error("When retrieving current user, it returned null");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- Application application = applicationRepo.getById(UUID.fromString(applicationId));
-
- if (application == null) {
- logger.error("getQueryTemplate() cannot find corresponding application by UUID: " + applicationId);
- throw new ProtocolException("Cannot find application by input UUID: " + applicationId);
- }
-
- return PICSUREResponse.success(
- Map.of("queryTemplate", mergeTemplate(user, application)));
-
+ @PathVariable("applicationId") String applicationId) {
+ return this.userService.getQueryTemplate(applicationId);
}
@ApiOperation(value = "Retrieve the queryTemplate of default application")
- @Transactional
- @GET
- @Path("/me/queryTemplate")
+ @GetMapping(path = "/me/queryTemplate", produces = "application/json")
public ResponseEntity> getQueryTemplate() {
- return getQueryTemplate(JAXRSConfiguration.defaultApplicationUUID);
- }
-
-
- private String mergeTemplate(User user, Application application) {
- String resultJSON = null;
- Map mergedTemplateMap = null;
- for (Privilege privilege : user.getPrivilegesByApplication(application)) {
- String template = privilege.getQueryTemplate();
- logger.debug("mergeTemplate() processing template:" + template);
- if (template == null || template.trim().isEmpty()) {
- continue;
- }
- Map templateMap = null;
- try {
- templateMap = objectMapper.readValue(template, Map.class);
- } catch (IOException ex) {
- logger.error("mergeTemplate() cannot convert stored queryTemplate using Jackson, the queryTemplate is: " + template);
- throw new ApplicationException("Inner application error, please contact admin.");
- }
-
- if (templateMap == null) {
- continue;
- }
-
- if (mergedTemplateMap == null) {
- mergedTemplateMap = templateMap;
- continue;
- }
-
- mergedTemplateMap = JsonUtils.mergeTemplateMap(mergedTemplateMap, templateMap);
- }
-
- try {
- resultJSON = objectMapper.writeValueAsString(mergedTemplateMap);
- } catch (JsonProcessingException ex) {
- logger.error("mergeTemplate() cannot convert map to json string. The map mergedTemplate is: " + mergedTemplateMap);
- throw new ApplicationException("Inner application error, please contact admin.");
- }
-
- return resultJSON;
-
+ return this.userService.getDefaultQueryTemplate();
}
/**
@@ -245,76 +126,17 @@ private String mergeTemplate(User user, Application application) {
* with the query parameter ?hasToken presented,
* it will refresh the long term token.
*
- * @param httpHeaders
- * @param hasToken
- * @return
+ * @param httpHeaders the http headers
+ * @return the refreshed long term token
*/
@ApiOperation(value = "refresh the long term tokne of current user")
- @Transactional
- @GET
- @Path("/me/refresh_long_term_token")
+ @GetMapping(path = "/me/refresh_long_term_token", produces = "application/json")
public ResponseEntity> refreshUserToken(
- @RequestHeader HttpHeaders httpHeaders,
- @ApiParam(required = false, value = "A flag represents if the long term token will be returned or not")
- @QueryParam("hasToken") Boolean hasToken) {
- User user = (User) securityContext.getUserPrincipal();
- if (user == null || user.getUuid() == null) {
- logger.error("Security context didn't have a user stored.");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- user = userRepo.getById(user.getUuid());
- if (user == null) {
- logger.error("When retrieving current user, it returned null");
- return PICSUREResponse.applicationError("Inner application error, please contact admin.");
- }
-
- String longTermToken = generateUserLongTermToken(httpHeaders);
- user.setToken(longTermToken);
-
- userRepo.merge(user);
-
- return PICSUREResponse.success(Map.of("userLongTermToken", longTermToken));
+ @RequestHeader HttpHeaders httpHeaders) {
+ return this.userService.refreshUserToken(httpHeaders);
}
- /**
- * Logic here is, retrieve the subject of the user from httpHeader. Then generate a long term one
- * with LONG_TERM_TOKEN_PREFIX| in front of the subject to be able to distinguish with regular ones, since
- * long term token only generated for accessing certain things to, in some degrees, decrease the insecurity.
- *
- * @param authorizationHeader the authorization header
- * @return the long term token
- * @throws IllegalArgumentException if the authorization header is not presented
- */
- private String generateUserLongTermToken(String authorizationHeader) {
- if (!StringUtils.isNotBlank(authorizationHeader)) {
- throw new IllegalArgumentException("Authorization header is not presented.");
- }
-
- Optional token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader);
- if (token.isEmpty()) {
- throw new IllegalArgumentException("Token is not presented in the authorization header.");
- }
- Jws jws = JWTUtil.parseToken(token.get());
-
- Claims claims = jws.getBody();
- String tokenSubject = claims.getSubject();
-
- if (tokenSubject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX + "|")) {
- // considering the subject already contains a "|"
- // to prevent infinitely adding the long term token prefix
- // we will grab the real subject here
- tokenSubject = tokenSubject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
- }
-
- return JWTUtil.createJwtToken(clientSecret,
- claims.getId(),
- claims.getIssuer(),
- claims,
- AuthNaming.LONG_TERM_TOKEN_PREFIX + "|" + tokenSubject,
- longTermTokenExpirationTime);
- }
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
index 6c05afaf9..b2f692431 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
@@ -1,21 +1,17 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
-import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
-import edu.harvard.hms.dbmi.avillach.auth.repository.UserMetadataMappingRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserMetadataMappingService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
+import jakarta.annotation.security.RolesAllowed;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
-import javax.annotation.security.RolesAllowed;
-import javax.inject.Inject;
-import javax.transaction.Transactional;
-import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
import java.util.List;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
@@ -26,73 +22,56 @@
*
*/
@Api
-@Path("mapping")
-public class UserMetadataMappingWebController extends BaseEntityService{
-
- public UserMetadataMappingWebController() {
- super(UserMetadataMapping.class);
- }
-
- @Inject
- UserMetadataMappingService mappingService;
+@Controller
+@RequestMapping("/mapping")
+public class UserMetadataMappingWebController {
- @Inject
- UserMetadataMappingRepository mappingRepo;
+ private final UserMetadataMappingService mappingService;
- @Inject
- ConnectionRepository connectionRepo;
+ @Autowired
+ public UserMetadataMappingWebController(UserMetadataMappingService mappingService) {
+ this.mappingService = mappingService;
+ }
- @ApiOperation(value = "GET information of one UserMetadataMapping with the UUID, requires ADMIN or SUPER_ADMIN role")
- @GET
- @Produces("application/json")
+ @ApiOperation(value = "GET information of one UserMetadataMapping with the UUID, requires ADMIN or SUPER_ADMIN role")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- @Path("{connectionId}")
- public ResponseEntity> getMappingsForConnection(@PathParam("connectionId") String connection) {
- return Response.ok(mappingService.
- getAllMappingsForConnection(connectionRepo
- .getUniqueResultByColumn("id", connection)))
- .build();
+ @GetMapping(path = "{connectionId}", produces = "application/json")
+ public ResponseEntity> getMappingsForConnection(@PathVariable("connectionId") String connection) {
+ return this.mappingService.getAllMappingsForConnection(connection);
}
@ApiOperation(value = "GET a list of existing UserMetadataMappings, requires ADMIN or SUPER_ADMIN role")
- @GET
- @Produces("application/json")
@RolesAllowed({ADMIN, SUPER_ADMIN})
+ @GetMapping(path = "/", produces = "application/json")
public ResponseEntity> getAllMappings() {
- return Response.ok(mappingService.getAllMappings()).build();
+ List allMappings = mappingService.getAllMappings();
+ return PICSUREResponse.success(allMappings);
}
@ApiOperation(value = "POST a list of UserMetadataMappings, requires SUPER_ADMIN role")
- @Transactional
- @POST
- @Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed({SUPER_ADMIN})
- @Path("/")
- public ResponseEntity>Entity> addMapping(
+ @PostMapping(path = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> addMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping in JSON format")
List mappings) {
return mappingService.addMappings(mappings);
}
@ApiOperation(value = "Update a list of UserMetadataMappings, will only update the fields listed, requires SUPER_ADMIN role")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed({SUPER_ADMIN})
- @Path("/")
+ @PutMapping(path = "/", consumes = "application/json", produces = "application/json")
public ResponseEntity> updateMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping with fields to be updated in JSON format")
List mappings) {
- return updateEntity(mappings, mappingRepo);
+ return this.mappingService.updateEntity(mappings);
}
@ApiOperation(value = "DELETE an UserMetadataMapping by Id only if the UserMetadataMapping is not associated by others, requires SUPER_ADMIN role")
- @Transactional
- @DELETE
@RolesAllowed({SUPER_ADMIN})
- @Path("/{mappingId}")
+ @DeleteMapping(path = "/{mappingId}", produces = "application/json")
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid UserMetadataMapping Id")
- @PathParam("mappingId") final String mappingId) {
- return removeEntityById(mappingId, mappingRepo);
+ @PathVariable("mappingId") final String mappingId) {
+ return this.mappingService.removeEntityById(mappingId);
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
index 02df69e76..9d00bc476 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserMetadataMappingService.java
@@ -9,6 +9,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
+import javax.transaction.Transactional;
import java.util.List;
/**
@@ -32,6 +33,7 @@ public List getAllMappingsForConnection(Connection connecti
return userMetadataMappingRepo.findByConnection(connection);
}
+ @Transactional
public ResponseEntity> addMappings(List mappings) {
String errorMessage = "The following connectionIds do not exist:\n";
boolean error = false;
@@ -54,4 +56,16 @@ public List getAllMappings() {
return userMetadataMappingRepo.list();
}
+ public ResponseEntity> getAllMappingsForConnection(String connection) {
+ return PICSUREResponse.success(getAllMappingsForConnection(connectionRepo.getUniqueResultByColumn("id", connection)));
+ }
+
+ public ResponseEntity> updateEntity(List mappings) {
+ return this.updateEntity(mappings, userMetadataMappingRepo);
+ }
+
+ @Transactional
+ public ResponseEntity> removeEntityById(String mappingId) {
+ return this.removeEntityById(mappingId, userMetadataMappingRepo);
+ }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
index ad667be2d..f74f716e5 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
@@ -1,23 +1,26 @@
package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponseOKwithMsgAndContent;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import edu.harvard.hms.dbmi.avillach.auth.entity.*;
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
+import edu.harvard.hms.dbmi.avillach.auth.repository.ApplicationRepository;
import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository;
import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository;
import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
+import edu.harvard.hms.dbmi.avillach.auth.utils.JsonUtils;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -30,33 +33,45 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;
-import java.util.stream.Collectors;
@Service
public class UserService extends BaseEntityService {
private final Logger logger = LoggerFactory.getLogger(UserService.class.getName());
+ private final MailService mailService;
private final TOSService tosService;
private final UserRepository userRepository;
private final ConnectionRepository connectionRepository;
-
+ private final ApplicationRepository applicationRepository;
private final RoleRepository roleRepository;
private final String clientSecret;
-
private final long tokenExpirationTime;
- private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour TODO: Move to a global configuration or enum?
+ private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour
+
+ public long longTermTokenExpirationTime;
+
+ private final String applicationUUID;
+ private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
- public UserService(TOSService tosService, UserRepository userRepository, ConnectionRepository connectionRepository, RoleRepository roleRepository,
- @Value("${application.client.secret}") String clientSecret, @Value("${application.token.expiration.time}") long tokenExpirationTime) {
+ public UserService(MailService mailService, TOSService tosService, UserRepository userRepository, ConnectionRepository connectionRepository, RoleRepository roleRepository, ApplicationRepository applicationRepository,
+ @Value("${application.client.secret}") String clientSecret, @Value("${application.token.expiration.time}") long tokenExpirationTime,
+ @Value("${application.default.}") String applicationUUID, @Value("${application.long.term.token.expiration.time}") long longTermTokenExpirationTime) {
super(User.class);
+ this.mailService = mailService;
this.tosService = tosService;
this.userRepository = userRepository;
this.connectionRepository = connectionRepository;
this.roleRepository = roleRepository;
this.clientSecret = clientSecret;
this.tokenExpirationTime = tokenExpirationTime > 0 ? tokenExpirationTime : defaultTokenExpirationTime;
+ this.applicationRepository = applicationRepository;
+ this.applicationUUID = applicationUUID;
+
+ long defaultLongTermTokenExpirationTime = 1000L * 60 * 60 * 24 * 30; //
+ this.longTermTokenExpirationTime = longTermTokenExpirationTime > 0 ? longTermTokenExpirationTime : defaultLongTermTokenExpirationTime;
+
}
public HashMap getUserProfileResponse(Map claims) {
@@ -123,9 +138,9 @@ public ResponseEntity> addEntity(List users) {
* if the state is changed, check if the user is a super admin to determine if the user could perform the action.
*
* @param currentUser the user trying to perform the action
- * @param inputUser
+ * @param inputUser the user that is going to be updated
* @param originalUser there could be no original user when adding a new user
- * @return
+ * @return true if the user could perform the action, false otherwise
*/
private boolean allowUpdateSuperAdminRole(
@NotNull User currentUser,
@@ -181,38 +196,30 @@ public ResponseEntity> addUsers(List users) {
}
checkAssociation(users);
- boolean allowAdd = true;
for (User user : users) {
logger.debug("Adding User " + user);
if (!allowUpdateSuperAdminRole(currentUser, user, null)) { // TODO: The allowUpdateSuperAdminRole is a private method
- allowAdd = false;
- break;
+ logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant "
+ + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " role when adding a user.");
+ throw new IllegalArgumentException("Not allowed to add a user with a " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege associated.");
}
if (user.getEmail() == null) {
try {
HashMap metadata = new HashMap(new ObjectMapper().readValue(user.getGeneralMetadata(), Map.class));
- List emailKeys = metadata.keySet().stream().filter((key) -> {
- return key.toLowerCase().contains("email");
- }).collect(Collectors.toList());
- if (emailKeys.size() > 0) {
- user.setEmail(metadata.get(emailKeys.get(0)));
+ List emailKeys = metadata.keySet().stream().filter((key) -> key.toLowerCase().contains("email")).toList();
+ if (!emailKeys.isEmpty()) {
+ user.setEmail(metadata.get(emailKeys.getFirst()));
}
} catch (IOException e) {
- e.printStackTrace();
+ logger.error("Failed to parse metadata for email address", e);
}
}
}
- if (allowAdd) {
- ResponseEntity> updateResponse = addEntity(users);
- sendUserUpdateEmailsFromResponse(updateResponse);
- return updateResponse;
- } else {
- logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant "
- + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " role when adding a user.");
- throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to add a user with a " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege associated.");
- }
+ ResponseEntity> updateResponse = addEntity(users);
+ sendUserUpdateEmailsFromResponse(updateResponse);
+ return updateResponse;
}
/**
@@ -264,14 +271,14 @@ public ResponseEntity> updateUser(List users) {
} else {
logger.error("updateUser() user - " + currentUser.getUuid() + " - with roles [" + currentUser.getRoleString() + "] - is not allowed to grant or remove "
+ AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
- throw new ProtocolException(Response.Status.BAD_REQUEST, "Not allowed to update a user with changes associated to " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
+ throw new IllegalArgumentException("Not allowed to update a user with changes associated to " + AuthNaming.AuthRoleNaming.SUPER_ADMIN + " privilege.");
}
}
private void sendUserUpdateEmailsFromResponse(ResponseEntity> updateResponse) {
logger.debug("Sending email");
try {
- Object entity = updateResponse.getEntity(); // TODO: Determine how to replicate this given the new approach
+ Object entity = updateResponse.getBody(); // TODO: Determine how to replicate this given the new approach
if (entity != null && entity instanceof PICSUREResponseOKwithMsgAndContent) {
PICSUREResponseOKwithMsgAndContent okResponse = (PICSUREResponseOKwithMsgAndContent) entity;
List addedUsers = (List) okResponse.getContent();
@@ -291,4 +298,193 @@ private void sendUserUpdateEmailsFromResponse(ResponseEntity> updateResponse)
}
logger.debug("finished email sending method");
}
+
+ public ResponseEntity> getCurrentUser(String authorizationHeader, Boolean hasToken) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User user = (User) securityContext.getAuthentication().getPrincipal();
+ if (user == null || user.getUuid() == null) {
+ logger.error("Security context didn't have a user stored.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ user = this.userRepository.getById(user.getUuid());
+ if (user == null) {
+ logger.error("When retrieving current user, it returned null");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ User.UserForDisplay userForDisplay = new User.UserForDisplay()
+ .setEmail(user.getEmail())
+ .setPrivileges(user.getPrivilegeNameSet())
+ .setUuid(user.getUuid().toString())
+ .setAcceptedTOS(this.tosService.hasUserAcceptedLatest(user.getSubject()));
+
+ // currently, the queryScopes are simple combination of queryScope string together as a set.
+ // We are expecting the queryScope string as plain string. If it is a JSON, we could change the
+ // code to use JsonUtils.mergeTemplateMap(Map, Map)
+ Set privileges = user.getTotalPrivilege();
+ if (privileges != null && !privileges.isEmpty()) {
+ Set scopes = new TreeSet<>();
+ privileges.stream().filter(privilege -> privilege.getQueryScope() != null).forEach(privilege -> {
+ try {
+ Arrays.stream(objectMapper.readValue(privilege.getQueryScope(), String[].class))
+ .filter(x -> x != null)
+ .forEach(scopeList -> scopes.addAll(Arrays.asList(scopeList)));
+ } catch (IOException e) {
+ logger.error("Parsing issue for privilege " + privilege.getUuid() + " queryScope", e);
+ }
+ });
+ userForDisplay.setQueryScopes(scopes);
+ }
+
+ if (hasToken != null) {
+
+ if (user.getToken() != null && !user.getToken().isEmpty()) {
+ userForDisplay.setToken(user.getToken());
+ } else {
+ user.setToken(generateUserLongTermToken(authorizationHeader));
+ this.userRepository.merge(user);
+ userForDisplay.setToken(user.getToken());
+ }
+ }
+
+ return PICSUREResponse.success(userForDisplay);
+ }
+
+ public ResponseEntity> getQueryTemplate(String applicationId) {
+ if (applicationId == null || applicationId.trim().isEmpty()) {
+ logger.error("getQueryTemplate() input application UUID is null or empty.");
+ throw new IllegalArgumentException("Input application UUID is incorrect.");
+ }
+
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User user = (User) securityContext.getAuthentication().getPrincipal();
+ if (user == null || user.getUuid() == null) {
+ logger.error("Security context didn't have a user stored.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ user = this.userRepository.getById(user.getUuid());
+ if (user == null) {
+ logger.error("When retrieving current user, it returned null");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ Application application = this.applicationRepository.getById(UUID.fromString(applicationId));
+
+ if (application == null) {
+ logger.error("getQueryTemplate() cannot find corresponding application by UUID: " + applicationId);
+ throw new IllegalArgumentException("Cannot find application by input UUID: " + applicationId);
+ }
+
+ return PICSUREResponse.success(
+ Map.of("queryTemplate", mergeTemplate(user, application)));
+ }
+
+ public ResponseEntity> getDefaultQueryTemplate() {
+ return getQueryTemplate(this.applicationUUID);
+ }
+
+ private String mergeTemplate(User user, Application application) {
+ String resultJSON;
+ Map mergedTemplateMap = null;
+ for (Privilege privilege : user.getPrivilegesByApplication(application)) {
+ String template = privilege.getQueryTemplate();
+ logger.debug("mergeTemplate() processing template:" + template);
+ if (template == null || template.trim().isEmpty()) {
+ continue;
+ }
+ Map templateMap = null;
+ try {
+ templateMap = objectMapper.readValue(template, Map.class);
+ } catch (IOException ex) {
+ logger.error("mergeTemplate() cannot convert stored queryTemplate using Jackson, the queryTemplate is: " + template);
+ throw new IllegalArgumentException("Inner application error, please contact admin.");
+ }
+
+ if (templateMap == null) {
+ continue;
+ }
+
+ if (mergedTemplateMap == null) {
+ mergedTemplateMap = templateMap;
+ continue;
+ }
+
+ mergedTemplateMap = JsonUtils.mergeTemplateMap(mergedTemplateMap, templateMap);
+ }
+
+ try {
+ resultJSON = objectMapper.writeValueAsString(mergedTemplateMap);
+ } catch (JsonProcessingException ex) {
+ logger.error("mergeTemplate() cannot convert map to json string. The map mergedTemplate is: " + mergedTemplateMap);
+ throw new IllegalArgumentException("Inner application error, please contact admin.");
+ }
+
+ return resultJSON;
+
+ }
+
+ @Transactional
+ public ResponseEntity> refreshUserToken(HttpHeaders httpHeaders) {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ User user = (User) securityContext.getAuthentication().getPrincipal();
+ if (user == null || user.getUuid() == null) {
+ logger.error("Security context didn't have a user stored.");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ user = this.userRepository.getById(user.getUuid());
+ if (user == null) {
+ logger.error("When retrieving current user, it returned null");
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ String authorizationHeader = httpHeaders.getFirst("Authorization");
+ String longTermToken = generateUserLongTermToken(authorizationHeader);
+ user.setToken(longTermToken);
+
+ this.userRepository.merge(user);
+
+ return PICSUREResponse.success(Map.of("userLongTermToken", longTermToken));
+ }
+
+ /**
+ * Logic here is, retrieve the subject of the user from httpHeader. Then generate a long term one
+ * with LONG_TERM_TOKEN_PREFIX| in front of the subject to be able to distinguish with regular ones, since
+ * long term token only generated for accessing certain things to, in some degrees, decrease the insecurity.
+ *
+ * @param authorizationHeader the authorization header
+ * @return the long term token
+ * @throws IllegalArgumentException if the authorization header is not presented
+ */
+ private String generateUserLongTermToken(String authorizationHeader) {
+ if (!StringUtils.isNotBlank(authorizationHeader)) {
+ throw new IllegalArgumentException("Authorization header is not presented.");
+ }
+
+ Optional token = JWTUtil.getTokenFromAuthorizationHeader(authorizationHeader);
+ if (token.isEmpty()) {
+ throw new IllegalArgumentException("Token is not presented in the authorization header.");
+ }
+
+ Jws jws = JWTUtil.parseToken(token.get());
+
+ Claims claims = jws.getBody();
+ String tokenSubject = claims.getSubject();
+
+ if (tokenSubject.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX + "|")) {
+ // considering the subject already contains a "|"
+ // to prevent infinitely adding the long term token prefix
+ // we will grab the real subject here
+ tokenSubject = tokenSubject.substring(AuthNaming.LONG_TERM_TOKEN_PREFIX.length() + 1);
+ }
+
+ return JWTUtil.createJwtToken(clientSecret,
+ claims.getId(),
+ claims.getIssuer(),
+ claims,
+ AuthNaming.LONG_TERM_TOKEN_PREFIX + "|" + tokenSubject,
+ this.longTermTokenExpirationTime);
+ }
}
diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
index 7a40e5f07..865a8f01f 100644
--- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
+++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/Auth0MatchingServiceTest.java
@@ -57,7 +57,7 @@ public void setUp() throws Auth0Exception {
doAnswer(invocation -> (listUnmatchedByConnectionIdMock(invocation.getArgument(0)))).
when(userRepo).listUnmatchedByConnectionId(any());
doAnswer(invocation -> (getAllMappingsForConnectionMock(invocation.getArgument(0)))).
- when(mappingService).getAllMappingsForConnection(any());
+ when(mappingService).getAllMappingsForConnection((Connection) any());
//So we can check that the user is persisted
doAnswer(new Answer() {
public Void answer(InvocationOnMock invocation) {
From 5a8cc186746eacfa8813bfad335681ad3ddba778 Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Tue, 26 Mar 2024 11:03:28 -0400
Subject: [PATCH 010/222] Refactor name of application.default.uuid in JSON and
properties files
This commit removes the mention of 'application.default.application.uuid' in the JSON and properties files, and replaces it with 'application.default.uuid'. The refactoring clarifies the parameter's functionality.
---
.../additional-spring-configuration-metadata.json | 10 +++++-----
.../src/main/resources/application.properties | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 8c7e347ad..80ee8ca2e 100644
--- a/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/pic-sure-auth-services/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -30,11 +30,6 @@
"type": "java.lang.String",
"description": "Description for application.tos.enabled."
},
- {
- "name": "application.default.application.uuid",
- "type": "java.lang.String",
- "description": "Description for application.default.application.uuid."
- },
{
"name": "application.system.name",
"type": "java.lang.String",
@@ -114,5 +109,10 @@
"name": "application.long.term.token.expiration.time",
"type": "java.lang.String",
"description": "Description for application.long.term.token.expiration.time."
+ },
+ {
+ "name": "application.default.uuid",
+ "type": "java.lang.String",
+ "description": "Description for application.default.uuid."
}
] }
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/resources/application.properties b/pic-sure-auth-services/src/main/resources/application.properties
index 28d01886d..f82505076 100644
--- a/pic-sure-auth-services/src/main/resources/application.properties
+++ b/pic-sure-auth-services/src/main/resources/application.properties
@@ -27,7 +27,7 @@ application.client.secret.base64=your_base64_flag
application.user.id.claim=sub
application.auth0.host=your_auth0_host
application.tos.enabled=true
-application.default.application.uuid=your_default_application_uuid
+application.default.uuid=your_default_application_uuid
application.system.name=your_system_name
application.template.path=your_template_path
application.access.grant.email.subject=your_email_subject
From fcc15f896d908a51f2446f7f6ee7bfc5e54bb859 Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Tue, 26 Mar 2024 11:11:21 -0400
Subject: [PATCH 011/222] Update JWTFilter and remove unused imports
This commit updates JWTFilter with explicit injection of application properties and removes unused imports in AuthSecurityContext. Also, the JWTFilter has been added to SecurityConfig, enhancing security by ensuring request authenticity.
---
.../hms/dbmi/avillach/auth/filter/JWTFilter.java | 15 +++++++++------
.../auth/security/AuthSecurityContext.java | 4 ----
.../avillach/auth/security/SecurityConfig.java | 2 +-
3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
index 00eef0b01..428eceb21 100755
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
@@ -1,6 +1,5 @@
package edu.harvard.hms.dbmi.avillach.auth.filter;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
@@ -52,14 +51,18 @@ public class JWTFilter extends OncePerRequestFilter {
private final TOSService tosService;
- @Value("${application.user.id.claim}")
- private String USER_CLAIM_ID;
+ private final String userClaimId;
+
+ private final boolean tosEnabled;
@Autowired
- public JWTFilter(UserRepository userRepo, ApplicationRepository applicationRepo, TOSService tosService) {
+ public JWTFilter(UserRepository userRepo, ApplicationRepository applicationRepo, TOSService tosService,
+ @Value("${application.user.id.claim}") String userClaimId, @Value("${application.tos.enabled}") boolean tosEnabled) {
this.userRepo = userRepo;
this.applicationRepo = applicationRepo;
this.tosService = tosService;
+ this.userClaimId = userClaimId;
+ this.tosEnabled = tosEnabled;
}
/**
@@ -90,7 +93,7 @@ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServlet
// Parse the token
Jws jws = parseToken(token); // TODO: We shouldn't be implementing a method that should be in the JWTUtils class
- String userId = jws.getBody().get(this.USER_CLAIM_ID, String.class); // TODO: Update when we remove the JAXRSConfiguration class
+ String userId = jws.getBody().get(this.userClaimId, String.class); // TODO: Update when we remove the JAXRSConfiguration class
if (userId.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
// For profile information, we do indeed allow long term token
@@ -176,7 +179,7 @@ private void setSecurityContextForUser(HttpServletRequest request, HttpServletRe
throw new NotAuthorizedException("User is deactivated");
}
- if (JAXRSConfiguration.tosEnabled.startsWith("true") && tosService.getLatest() != null && !tosService.hasUserAcceptedLatest(authenticatedUser.getSubject())) {
+ if (this.tosEnabled && tosService.getLatest() != null && !tosService.hasUserAcceptedLatest(authenticatedUser.getSubject())) {
//If user has not accepted terms of service and is attempted to get information other than the terms of service, don't authenticate
try {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "User must accept terms of service");
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
index 31fb8584f..65fe5cbe0 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/AuthSecurityContext.java
@@ -1,9 +1,5 @@
//package edu.harvard.hms.dbmi.avillach.auth.security;
//
-//import edu.harvard.hms.dbmi.avillach.auth.data.entity.Application;
-//import edu.harvard.hms.dbmi.avillach.auth.data.entity.User;
-//
-//import javax.ws.rs.core.SecurityContext;
//import java.security.Principal;
//
///**
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
index 110755bb5..3ef9cd348 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/security/SecurityConfig.java
@@ -25,7 +25,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
http.authorizeHttpRequests((authz) ->
authz.anyRequest().authenticated()
)
- .addFilter()
+ .addFilter(this.jwtFilter)
.httpBasic(withDefaults());
return http.build();
From cc440c1435480248f38060af085d30d168b88149 Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Wed, 27 Mar 2024 16:50:12 -0400
Subject: [PATCH 012/222] Refactor code and add spring-boot-starter-mail
dependency
This commit mainly removes unused imports across various files, updates some classes with value injection and cleans out unnecessary comments. It also renames the 'MailService' class to 'BasicMailService' and updates the related references accordingly. The pom.xml file has been updated with the 'spring-boot-starter-mail' dependency addition. 'JWTUtil' class has been updated to include spring '@Value' for properties instead of using static variables.
---
pic-sure-auth-services/pom.xml | 9 +-
.../dbmi/avillach/auth/filter/JWTFilter.java | 2 -
.../dbmi/avillach/auth/model/AccessEmail.java | 1 -
.../avillach/auth/rest/AuthController.java | 1 -
.../avillach/auth/service/MailService.java | 14 +
.../service/impl/AuthenticationService.java | 12 +-
.../auth/service/impl/BaseEntityService.java | 23 +-
.../auth/service/impl/BasicMailService.java | 130 ++++++++++
.../impl/FENCEAuthenticationService.java | 240 ++++++++++--------
.../auth/service/impl/MailService.java | 138 ----------
.../auth/service/impl/UserService.java | 9 +-
.../hms/dbmi/avillach/auth/utils/JWTUtil.java | 33 ++-
.../dbmi/avillach/auth/utils/JsonUtils.java | 65 +----
13 files changed, 340 insertions(+), 337 deletions(-)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BasicMailService.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml
index 8d5f6a954..35eeca7da 100644
--- a/pic-sure-auth-services/pom.xml
+++ b/pic-sure-auth-services/pom.xml
@@ -78,8 +78,15 @@
org.springframework.boot
spring-boot-starter-security
- 3.2.3
+ 3.2.4
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+ 3.2.4
+
+
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
index 428eceb21..2c9a590cb 100755
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
@@ -152,8 +152,6 @@ private void setSecurityContextForApplication(HttpServletRequest request, Applic
request.setAttribute("authenticatedApplication", authenticatedApplication);
}
- // TODO: Implement the ApplicationException thrown in this method
-
/**
* Sets the security context for the given user.
* This method is responsible for validating the user claims, checking if the user is active,
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
index 90b19ebd9..35ee19aa5 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
@@ -1,6 +1,5 @@
package edu.harvard.hms.dbmi.avillach.auth.model;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
index cf891eca5..712c388eb 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
@@ -1,6 +1,5 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthenticationService;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.AuthorizationService;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.FENCEAuthenticationService;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java
new file mode 100644
index 000000000..ef6542cc6
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/MailService.java
@@ -0,0 +1,14 @@
+package edu.harvard.hms.dbmi.avillach.auth.service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.mustachejava.Mustache;
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+import jakarta.mail.MessagingException;
+
+public interface MailService {
+ void sendUsersAccessEmail(User user) throws MessagingException;
+
+ void sendDeniedAccessEmail(JsonNode userInfo) throws MessagingException;
+
+ void sendEmail(Mustache emailTemplate, String to, String subject, Object scope) throws MessagingException;
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
index 7e60a5971..7effdc603 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationService.java
@@ -40,16 +40,16 @@ public class AuthenticationService {
private final UserRepository userRepository;
- private final MailService mailService;
+ private final BasicMailService basicMailService;
private final UserService userService;
private static final int AUTH_RETRY_LIMIT = 3;
@Autowired
- public AuthenticationService(OauthUserMatchingService matchingService, UserRepository userRepository, MailService mailService, UserService userService) {
+ public AuthenticationService(OauthUserMatchingService matchingService, UserRepository userRepository, BasicMailService basicMailService, UserService userService) {
this.matchingService = matchingService;
this.userRepository = userRepository;
- this.mailService = mailService;
+ this.basicMailService = basicMailService;
this.userService = userService;
}
@@ -88,9 +88,11 @@ public ResponseEntity> getToken(Map authRequest) {
if (user == null) {
if (JAXRSConfiguration.deniedEmailEnabled.startsWith("true")) {
try {
- mailService.sendDeniedAccessEmail(userInfo);
- } catch (MessagingException e) {
+ basicMailService.sendDeniedAccessEmail(userInfo);
+ } catch (MessagingException e) { // TODO: We need to remove the javax.mail dependency
logger.warn("Failed to send user access denied email: ", e);
+ } catch (jakarta.mail.MessagingException e) {
+ throw new RuntimeException(e);
}
}
throw new NotAuthorizedException("No user matching user_id " + userId + " present in database");
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
index d9f10d0ef..50607851e 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
@@ -2,12 +2,13 @@
import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
@@ -27,7 +28,7 @@ public abstract class BaseEntityService {
protected final Class type;
private final String auditLogName;
-
+
protected BaseEntityService(Class type){
this.type = type;
@@ -36,7 +37,9 @@ protected BaseEntityService(Class type){
}
public ResponseEntity> getEntityById(String id, BaseRepository baseRepository){
- logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext)
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String principalName = securityContext.getAuthentication().getName();
+ logger.info("User: " + principalName
+ " Looking for " + type.getSimpleName() +
" by ID: " + id + "...");
@@ -50,7 +53,9 @@ public ResponseEntity> getEntityById(String id, BaseRepository baseRepository)
}
public ResponseEntity> getEntityAll(BaseRepository baseRepository){
- logger.info("User: " + JAXRSConfiguration.getPrincipalName(securityContext) +
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String principalName = securityContext.getAuthentication().getName();
+ logger.info("User: " + principalName +
" Getting all " + type.getSimpleName() +
"s...");
List ts = null;
@@ -66,7 +71,8 @@ public ResponseEntity> getEntityAll(BaseRepository baseRepository){
}
public ResponseEntity> addEntity(List entities, BaseRepository baseRepository){
- String username = JAXRSConfiguration.getPrincipalName(securityContext);
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String username = securityContext.getAuthentication().getName();
if (entities == null || entities.isEmpty())
return PICSUREResponse.protocolError("No " + type.getSimpleName().toLowerCase() +
" to be added.");
@@ -99,7 +105,8 @@ public ResponseEntity> updateEntity(List entities, BaseRepository baseRepos
return PICSUREResponse.protocolError("No " + type.getSimpleName().toLowerCase() +
" to be updated.");
- String username = JAXRSConfiguration.getPrincipalName(securityContext);
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String username = securityContext.getAuthentication().getName();
logger.info("User: " + username + " is trying to update a list of "
+ type.getSimpleName());
@@ -228,8 +235,8 @@ private boolean updateAllAttributes(T detachedT, BaseRepository baseRep
}
public ResponseEntity> removeEntityById(String id, BaseRepository baseRepository) {
-
- String username = JAXRSConfiguration.getPrincipalName(securityContext);
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ String username = securityContext.getAuthentication().getName();
logger.info("User: " + username + " is trying to REMOVE an entity: "
+ type.getSimpleName() + ", by uuid: " + id);
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BasicMailService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BasicMailService.java
new file mode 100644
index 000000000..80039c41e
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BasicMailService.java
@@ -0,0 +1,130 @@
+package edu.harvard.hms.dbmi.avillach.auth.service.impl;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.StringWriter;
+import java.util.Map;
+
+import edu.harvard.hms.dbmi.avillach.auth.model.AccessEmail;
+import edu.harvard.hms.dbmi.avillach.auth.service.MailService;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.Mustache;
+import com.github.mustachejava.MustacheFactory;
+
+import edu.harvard.hms.dbmi.avillach.auth.entity.User;
+
+/**
+ * Service class for sending email notifications.
+ */
+@Service
+public class BasicMailService implements MailService {
+ private static final Logger logger = LoggerFactory.getLogger(BasicMailService.class);
+ private static final MustacheFactory mf = new DefaultMustacheFactory();
+ private final Mustache accessTemplate = compileTemplate("accessEmail.mustache");
+ private final Mustache deniedTemplate = compileTemplate("deniedAccessEmail.mustache");
+ private final JavaMailSender mailSender;
+ private final String templatePath;
+ private final String systemName;
+ private final String accessGrantEmailSubject;
+ private final String adminUsers;
+
+
+ @Autowired
+ public BasicMailService(JavaMailSender mailSender, @Value("${application.template.path}") String templatePath, @Value("${application.system.name}") String systemName,
+ @Value("${application.access.grant.email.subject") String accessGrantEmailSubject, @Value("${application.admin.users}") String adminUsers) {
+ this.mailSender = mailSender;
+ this.templatePath = templatePath;
+ this.systemName = systemName;
+ this.accessGrantEmailSubject = accessGrantEmailSubject;
+ this.adminUsers = adminUsers;
+ }
+
+ /**
+ * Compile mustache template from templateFile
+ */
+ private Mustache compileTemplate(String templateFile) {
+ try {
+ FileReader reader = new FileReader(templatePath + templateFile);
+ return mf.compile(reader, templateFile);
+ } catch (FileNotFoundException e) {
+ logger.warn("email template not found for " + templateFile);
+ return null;
+ }
+ }
+
+ /**
+ * Send email to user about changes in user Roles
+ * @param user User object
+ */
+ @Override
+ public void sendUsersAccessEmail(User user) throws MessagingException {
+ if(accessTemplate == null) {
+ logger.debug("No template defined for new user access email, not sending");
+ }else if (StringUtils.isEmpty(user.getEmail())) {
+ logger.error("User " + (user.getSubject() != null ? user.getSubject() : "") + " has no email address.");
+ } else {
+ String subject = "Your Access To " + this.systemName;
+ if (this.accessGrantEmailSubject != null && !this.accessGrantEmailSubject.isEmpty() && !this.accessGrantEmailSubject.equals("none")){
+ subject = this.accessGrantEmailSubject;
+ }
+ sendEmail(accessTemplate, user.getEmail(),subject, new AccessEmail(user));
+ }
+ }
+
+ /**
+ * Send email to admin about user being denied access to the system
+ * @param userInfo User info object returned by authentication provider
+ */
+ @Override
+ public void sendDeniedAccessEmail(JsonNode userInfo) throws MessagingException {
+ if(deniedTemplate == null) {
+ logger.debug("No template for Access Denied email, not sending");
+ } else {
+ logger.info("Sending 'Access Denied' email to "
+ + this.adminUsers
+ + ". User: "
+ + userInfo.get("email") != null ? userInfo.get("email").asText() : userInfo.get("user_id").asText());
+ ObjectMapper mapper = new ObjectMapper();
+ Map scope = mapper.convertValue(userInfo, Map.class);
+ scope.put("systemName", this.systemName);
+ sendEmail(deniedTemplate, this.adminUsers, "User denied access to " + this.systemName, scope);
+ }
+ }
+
+ /**
+ * Generate email from template and send it.
+ * @param emailTemplate Name of the template.
+ * @param to Recipients
+ * @param subject Subject of the email
+ * @param scope Object that contains attributes for template. e.g.: Map
+ */
+ @Override
+ public void sendEmail(Mustache emailTemplate, String to, String subject, Object scope) throws MessagingException {
+ logger.debug("sendEmail(String, String, String, Object) - start");
+ if (StringUtils.isEmpty(to) || StringUtils.isEmpty(subject) || scope == null || emailTemplate == null) {
+ logger.error("One of the required parameters is null. Can't send email.");
+ return;
+ }
+
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message);
+ helper.setFrom(to);
+ helper.setSubject(subject);
+ helper.setText(emailTemplate.execute(new StringWriter(), scope).toString(), true);
+ mailSender.send(message);
+ logger.debug("sendEmail() finished");
+ }
+}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
index b3ee6a06c..2f133aefc 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/FENCEAuthenticationService.java
@@ -1,31 +1,37 @@
package edu.harvard.hms.dbmi.avillach.auth.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import edu.harvard.dbmi.avillach.util.HttpClientUtil;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.entity.*;
import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.repository.*;
+import jakarta.annotation.PostConstruct;
import org.apache.http.Header;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestClient;
+
+import
import java.io.File;
import java.io.IOException;
import java.util.*;
-import static edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration.*;
-
@Service
public class FENCEAuthenticationService {
private final Logger logger = LoggerFactory.getLogger(FENCEAuthenticationService.class);
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
private final UserRepository userRepo;
private final RoleRepository roleRepo;
@@ -44,8 +50,33 @@ public class FENCEAuthenticationService {
private Connection fenceConnection;
private Map fenceMapping;
+ // ----------------- FENCE Configuration -----------------
+ private final String idp_provider_uri;
+ private final String fence_client_id;
+ private final String fence_client_secret;
+ private final String fence_redirect_url;
+ private final String fence_consent_group_concept_path;
+ private final String fence_harmonized_concept_path;
+ private final String fence_standard_access_rules;
+
+ // ----------------- Template Path -----------------
+ private final String templatePath;
+
+ // ----------------- HTTP Client -----------------
+ private final RestClient restClient;
+
@Autowired
- public FENCEAuthenticationService(UserRepository userRepo, RoleRepository roleRepo, ConnectionRepository connectionRepo, AccessRuleRepository accessruleRepo, ApplicationRepository applicationRepo, PrivilegeRepository privilegeRepo, UserService userService) {
+ public FENCEAuthenticationService(UserRepository userRepo, RoleRepository roleRepo, ConnectionRepository connectionRepo,
+ AccessRuleRepository accessruleRepo, ApplicationRepository applicationRepo,
+ PrivilegeRepository privilegeRepo, UserService userService,
+ @Value("${application.idp.provider}") String idpProviderUri,
+ @Value("${application.fence.client.id") String fenceClientId,
+ @Value("${application.fence.client.secret}") String fenceClientSecret,
+ @Value("${application.fence.redirect.url}") String fenceRedirectUrl,
+ @Value("${application.fence.consent.group.concept.path}") String fenceConsentGroupConceptPath,
+ @Value("${application.fence.harmonized.concept.path}") String fenceHarmonizedConceptPath,
+ @Value("${application.fence.standard.access.rules}") String fenceStandardAccessRules,
+ @Value("${application.template.path}") String templatePath) {
this.userRepo = userRepo;
this.roleRepo = roleRepo;
this.connectionRepo = connectionRepo;
@@ -53,14 +84,24 @@ public FENCEAuthenticationService(UserRepository userRepo, RoleRepository roleRe
this.applicationRepo = applicationRepo;
this.privilegeRepo = privilegeRepo;
this.userService = userService;
+ idp_provider_uri = idpProviderUri;
+ fence_client_id = fenceClientId;
+ fence_client_secret = fenceClientSecret;
+ fence_redirect_url = fenceRedirectUrl;
+ fence_consent_group_concept_path = fenceConsentGroupConceptPath;
+ fence_harmonized_concept_path = fenceHarmonizedConceptPath;
+ fence_standard_access_rules = fenceStandardAccessRules;
+ this.templatePath = templatePath;
+ this.restClient = RestClient.builder()
+ .requestFactory(new HttpComponentsClientHttpRequestFactory())
+ .build();
}
- // TODO: Find and equivalent of @PostConstruct in spring
-// @PostConstruct
- public void initializeFenceService() {
- picSureApp = applicationRepo.getUniqueResultByColumn("name", "PICSURE");
- fenceConnection = connectionRepo.getUniqueResultByColumn("label", "FENCE");
- fenceMapping = getFENCEMapping();
+ @PostConstruct
+ public void initializeFenceService() {
+ picSureApp = applicationRepo.getUniqueResultByColumn("name", "PICSURE");
+ fenceConnection = connectionRepo.getUniqueResultByColumn("label", "FENCE");
+ fenceMapping = getFENCEMapping();
}
private JsonNode getFENCEUserProfile(String access_token) {
@@ -68,15 +109,15 @@ private JsonNode getFENCEUserProfile(String access_token) {
List headers = new ArrayList<>();
headers.add(new BasicHeader("Authorization", "Bearer " + access_token));
- logger.debug("getFENCEUserProfile() getting user profile from uri:"+JAXRSConfiguration.idp_provider_uri+"/user/user");
+ logger.debug("getFENCEUserProfile() getting user profile from uri:" + this.idp_provider_uri + "/user/user");
JsonNode fence_user_profile_response = HttpClientUtil.simpleGet(
- JAXRSConfiguration.idp_provider_uri+"/user/user",
- JAXRSConfiguration.client,
- JAXRSConfiguration.objectMapper,
+ this.idp_provider_uri + "/user/user",
+ this.client,
+ this.objectMapper,
headers.toArray(new Header[headers.size()])
);
- logger.debug("getFENCEUserProfile() finished, returning user profile"+fence_user_profile_response.asText());
+ logger.debug("getFENCEUserProfile() finished, returning user profile" + fence_user_profile_response.asText());
return fence_user_profile_response;
}
@@ -85,53 +126,53 @@ private JsonNode getFENCEAccessToken(String fence_code) {
List headers = new ArrayList<>();
Base64.Encoder encoder = Base64.getEncoder();
- String fence_auth_header = JAXRSConfiguration.fence_client_id+":"+JAXRSConfiguration.fence_client_secret;
+ String fence_auth_header = this.fence_client_id + ":" + this.fence_client_secret;
headers.add(new BasicHeader("Authorization",
"Basic " + encoder.encodeToString(fence_auth_header.getBytes())));
headers.add(new BasicHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"));
// Build the request body, as JSON
String query_string =
- "grant_type=authorization_code"
- + "&code=" + fence_code
- + "&redirect_uri=" + JAXRSConfiguration.fence_redirect_url;
+ "grant_type=authorization_code"
+ + "&code=" + fence_code
+ + "&redirect_uri=" + this.fence_redirect_url;
- String fence_token_url = JAXRSConfiguration.idp_provider_uri+"/user/oauth2/token";
+ String fence_token_url = this.idp_provider_uri + "/user/oauth2/token";
JsonNode resp = null;
try {
resp = HttpClientUtil.simplePost(
fence_token_url,
new StringEntity(query_string),
- JAXRSConfiguration.client,
- JAXRSConfiguration.objectMapper,
+ this.client,
+ this.objectMapper,
headers.toArray(new Header[headers.size()])
);
} catch (Exception ex) {
- logger.error("getFENCEAccessToken() failed to call FENCE token service, "+ex.getMessage());
+ logger.error("getFENCEAccessToken() failed to call FENCE token service, " + ex.getMessage());
}
- logger.debug("getFENCEAccessToken() finished. "+resp.asText());
+ logger.debug("getFENCEAccessToken() finished. " + resp.asText());
return resp;
}
// Get access_token from FENCE, based on the provided `code`
- public ResponseEntity> getFENCEProfile(Map authRequest){
+ public ResponseEntity> getFENCEProfile(Map authRequest) {
logger.debug("getFENCEProfile() starting...");
- String fence_code = authRequest.get("code");
+ String fence_code = authRequest.get("code");
JsonNode fence_user_profile = null;
// Get the Gen3/FENCE user profile. It is a JsonNode object
try {
logger.debug("getFENCEProfile() query FENCE for user profile with code");
fence_user_profile = getFENCEUserProfile(getFENCEAccessToken(fence_code).get("access_token").asText());
- logger.debug("getFENCEProfile() user profile structure:"+fence_user_profile.asText());
+ logger.debug("getFENCEProfile() user profile structure:" + fence_user_profile.asText());
logger.debug("getFENCEProfile() .username:" + fence_user_profile.get("username"));
logger.debug("getFENCEProfile() .user_id:" + fence_user_profile.get("user_id"));
logger.debug("getFENCEProfile() .email:" + fence_user_profile.get("email"));
} catch (Exception ex) {
- logger.error("getFENCEToken() could not retrieve the user profile from the auth provider, because "+ex.getMessage(), ex);
- throw new NotAuthorizedException("Could not get the user profile "+
- "from the Gen3 authentication provider."+ex.getMessage());
+ logger.error("getFENCEToken() could not retrieve the user profile from the auth provider, because " + ex.getMessage(), ex);
+ throw new NotAuthorizedException("Could not get the user profile " +
+ "from the Gen3 authentication provider." + ex.getMessage());
}
User current_user = null;
@@ -140,12 +181,12 @@ public ResponseEntity> getFENCEProfile(Map authRequest){
// in the Gen3/FENCE profile
current_user = createUserFromFENCEProfile(fence_user_profile);
logger.info("getFENCEProfile() saved details for user with e-mail:"
- +current_user.getEmail()
- +" and subject:"
- +current_user.getSubject());
+ + current_user.getEmail()
+ + " and subject:"
+ + current_user.getSubject());
} catch (Exception ex) {
- logger.error("getFENCEToken() Could not persist the user information, because "+ex.getMessage());
+ logger.error("getFENCEToken() Could not persist the user information, because " + ex.getMessage());
throw new NotAuthorizedException("The user details could not be persisted. Please contact the administrator.");
}
@@ -160,39 +201,34 @@ public ResponseEntity> getFENCEProfile(Map authRequest){
continue;
}
- logger.debug("getFENCEProfile() AccessRole:"+access_role_name);
+ logger.debug("getFENCEProfile() AccessRole:" + access_role_name);
String[] parts = access_role_name.split("\\.");
String newRoleName;
if (parts.length > 1) {
- newRoleName = "FENCE_"+parts[0]+"_"+parts[parts.length-1];
+ newRoleName = "FENCE_" + parts[0] + "_" + parts[parts.length - 1];
} else {
- newRoleName = "FENCE_"+access_role_name;
+ newRoleName = "FENCE_" + access_role_name;
}
- logger.info("getFENCEProfile() New PSAMA role name:"+newRoleName);
-
- if (upsertRole(current_user, newRoleName, "FENCE role "+newRoleName)) {
- logger.info("getFENCEProfile() Updated user role. Now it includes `"+newRoleName+"`");
- } else {
- logger.error("getFENCEProfile() could not add roles to user's profile");
- }
+ logger.info("getFENCEProfile() New PSAMA role name:" + newRoleName);
- // TODO: In case we need to do something with this part, we can uncomment it.
- //JsonNode role_object = fence_user_profile.get("project_access").get(newRoleName);
- //It is a an array of strings, like this: ["read-storage","read"]
- //logger.debug("getFENCEProfile() object:"+role_object.toString());
+ if (upsertRole(current_user, newRoleName, "FENCE role " + newRoleName)) {
+ logger.info("getFENCEProfile() Updated user role. Now it includes `" + newRoleName + "`");
+ } else {
+ logger.error("getFENCEProfile() could not add roles to user's profile");
+ }
}
try {
userRepo.changeRole(current_user, current_user.getRoles());
- logger.debug("upsertRole() updated user, who now has "+current_user.getRoles().size()+" roles.");
+ logger.debug("upsertRole() updated user, who now has " + current_user.getRoles().size() + " roles.");
} catch (Exception ex) {
- logger.error("upsertRole() Could not add roles to user, because "+ex.getMessage());
+ logger.error("upsertRole() Could not add roles to user, because " + ex.getMessage());
}
- HashMap claims = new HashMap();
+ HashMap claims = new HashMap();
claims.put("name", fence_user_profile.get("name"));
claims.put("email", current_user.getEmail());
claims.put("sub", current_user.getSubject());
- HashMap responseMap = userService.getUserProfileResponse(claims);
+ HashMap responseMap = userService.getUserProfileResponse(claims);
logger.debug("getFENCEProfile() UserProfile response object has been generated");
logger.debug("getFENCEToken() finished");
@@ -210,7 +246,7 @@ private User createUserFromFENCEProfile(JsonNode node) {
logger.debug("createUserFromFENCEProfile() starting...");
User new_user = new User();
- new_user.setSubject("fence|"+node.get("user_id").asText());
+ new_user.setSubject("fence|" + node.get("user_id").asText());
new_user.setEmail(node.get("email").asText());
new_user.setGeneralMetadata(node.toString());
// This is a hack, but someone has to do it.
@@ -232,14 +268,14 @@ private User createUserFromFENCEProfile(JsonNode node) {
/**
* Insert or Update the User object's list of Roles in the database.
*
- * @param u The User object the generated Role will be added to
- * @param roleName Name of the Role
+ * @param u The User object the generated Role will be added to
+ * @param roleName Name of the Role
* @param roleDescription Description of the Role
* @return boolean Whether the Role was successfully added to the User or not
*/
- private boolean upsertRole(User u, String roleName, String roleDescription) {
+ private boolean upsertRole(User u, String roleName, String roleDescription) {
boolean status = false;
- logger.debug("upsertRole() starting for user subject:"+u.getSubject());
+ logger.debug("upsertRole() starting for user subject:" + u.getSubject());
// Get the User's list of Roles. The first time, this will be an empty Set.
// This method is called for every Role, and the User's list of Roles will
@@ -266,7 +302,7 @@ private boolean upsertRole(User u, String roleName, String roleDescription) {
u.getRoles().add(r);
status = true;
} catch (Exception ex) {
- logger.error("upsertRole() Could not inser/update role "+roleName+" to repo, because "+ex.getMessage());
+ logger.error("upsertRole() Could not inser/update role " + roleName + " to repo, because " + ex.getMessage());
}
@@ -276,7 +312,7 @@ private boolean upsertRole(User u, String roleName, String roleDescription) {
private Set upsertPrivilege(User u, Role r) {
String roleName = r.getName();
- logger.info("upsertPrivilege() starting, adding privilege to role "+roleName);
+ logger.info("upsertPrivilege() starting, adding privilege to role " + roleName);
String[] parts = roleName.split("_");
String project_name = parts[1];
@@ -285,20 +321,22 @@ private Set upsertPrivilege(User u, Role r) {
String concept_path = fenceMapping.get(project_name);
// Get privilege and assign it to this role.
- String privilegeName = r.getName().replaceFirst("FENCE_*","PRIV_FENCE_");
- logger.info("upsertPrivilege() Looking for privilege, with name : "+privilegeName);
+ String privilegeName = r.getName().replaceFirst("FENCE_*", "PRIV_FENCE_");
+ logger.info("upsertPrivilege() Looking for privilege, with name : " + privilegeName);
Set privs = r.getPrivileges();
- if (privs == null) { privs = new HashSet();}
+ if (privs == null) {
+ privs = new HashSet();
+ }
Privilege p = privilegeRepo.getUniqueResultByColumn("name", privilegeName);
if (p != null) {
- logger.info("upsertPrivilege() Assigning privilege "+p.getName()+" to role "+r.getName());
+ logger.info("upsertPrivilege() Assigning privilege " + p.getName() + " to role " + r.getName());
privs.add(p);
} else {
logger.info("upsertPrivilege() This is a new privilege");
- logger.info("upsertPrivilege() project:"+project_name+" consent_group:"+consent_group+" concept_path:"+concept_path);
+ logger.info("upsertPrivilege() project:" + project_name + " consent_group:" + consent_group + " concept_path:" + concept_path);
// Add new privilege PRIV_FENCE_phs######_c# and PRIV_FENCE_phs######_c#_HARMONIZED
privs.add(createNewPrivilege(project_name, consent_group, concept_path, false));
@@ -314,21 +352,21 @@ private Privilege createNewPrivilege(String project_name, String consent_group,
// Build Privilege Object
try {
priv.setApplication(picSureApp);
- priv.setName("PRIV_FENCE_"+project_name+"_"+consent_group+(isHarmonized?"_HARMONIZED":""));
- priv.setDescription("FENCE privilege for "+project_name+"/"+consent_group);
+ priv.setName("PRIV_FENCE_" + project_name + "_" + consent_group + (isHarmonized ? "_HARMONIZED" : ""));
+ priv.setDescription("FENCE privilege for " + project_name + "/" + consent_group);
priv.setQueryScope(queryScopeConceptPath);
String consent_concept_path = fence_consent_group_concept_path;
// TOOD: Change this to a mustache template
String queryTemplateText = "{\"categoryFilters\": {\""
- +consent_concept_path
- +"\":\""
- +project_name+"."+consent_group
- +"\"},"
- +"\"numericFilters\":{},\"requiredFields\":[],"
- +"\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}],"
- +"\"expectedResultType\": \"COUNT\""
- +"}";
+ + consent_concept_path
+ + "\":\""
+ + project_name + "." + consent_group
+ + "\"},"
+ + "\"numericFilters\":{},\"requiredFields\":[],"
+ + "\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}],"
+ + "\"expectedResultType\": \"COUNT\""
+ + "}";
priv.setQueryTemplate(queryTemplateText);
priv.setQueryScope(queryScopeConceptPath);
@@ -337,18 +375,18 @@ private Privilege createNewPrivilege(String project_name, String consent_group,
Set accessrules = new HashSet();
accessrules.add(ar);
// Add additionanl access rules
- for(String arName: fence_standard_access_rules.split(",")) {
+ for (String arName : fence_standard_access_rules.split(",")) {
if (arName.startsWith("AR_")) {
- logger.info("Adding AccessRule "+arName+" to privilege "+priv.getName());
- accessrules.add(accessruleRepo.getUniqueResultByColumn("name",arName));
+ logger.info("Adding AccessRule " + arName + " to privilege " + priv.getName());
+ accessrules.add(accessruleRepo.getUniqueResultByColumn("name", arName));
}
}
priv.setAccessRules(accessrules);
- logger.info("createNewPrivilege() Added "+accessrules.size()+" access_rules to privilege");
+ logger.info("createNewPrivilege() Added " + accessrules.size() + " access_rules to privilege");
}
privilegeRepo.persist(priv);
- logger.info("createNewPrivilege() Added new privilege "+priv.getName()+" to DB");
+ logger.info("createNewPrivilege() Added new privilege " + priv.getName() + " to DB");
} catch (Exception ex) {
ex.printStackTrace();
logger.error("createNewPrivilege() could not save privilege");
@@ -358,24 +396,24 @@ private Privilege createNewPrivilege(String project_name, String consent_group,
private AccessRule upsertAccessRule(String project_name, String consent_group) {
logger.debug("upsertAccessRule() starting");
- String ar_name = "AR_"+project_name+"_"+consent_group;
+ String ar_name = "AR_" + project_name + "_" + consent_group;
AccessRule ar = accessruleRepo.getUniqueResultByColumn("name", ar_name);
if (ar != null) {
- logger.info("upsertAccessRule() AccessRule "+ar_name+" already exists.");
+ logger.info("upsertAccessRule() AccessRule " + ar_name + " already exists.");
return ar;
}
- logger.info("upsertAccessRule() Creating new access rule "+ar_name);
+ logger.info("upsertAccessRule() Creating new access rule " + ar_name);
ar = new AccessRule();
ar.setName(ar_name);
- ar.setDescription("FENCE AR for "+project_name+"/"+consent_group);
+ ar.setDescription("FENCE AR for " + project_name + "/" + consent_group);
StringBuilder ruleText = new StringBuilder();
ruleText.append("$..categoryFilters.['");
ruleText.append(fence_consent_group_concept_path);
ruleText.append("']");
ar.setRule(ruleText.toString());
ar.setType(AccessRule.TypeNaming.ALL_EQUALS);
- ar.setValue(project_name+"."+consent_group);
+ ar.setValue(project_name + "." + consent_group);
ar.setCheckMapKeyOnly(false);
ar.setCheckMapNode(true);
ar.setEvaluateOnlyByGates(false);
@@ -386,8 +424,8 @@ private AccessRule upsertAccessRule(String project_name, String consent_group) {
for (String accessruleName : fence_standard_access_rules.split("\\,")) {
if (accessruleName.startsWith("GATE_")) {
logger.info("upsertAccessRule() Assign gate " + accessruleName +
- " to access_rule "+ar.getName());
- gates.add(accessruleRepo.getUniqueResultByColumn("name",accessruleName));
+ " to access_rule " + ar.getName());
+ gates.add(accessruleRepo.getUniqueResultByColumn("name", accessruleName));
} else {
continue;
}
@@ -400,20 +438,20 @@ private AccessRule upsertAccessRule(String project_name, String consent_group) {
return ar;
}
- /*
- * Get the mappings of fence privileges to paths
- */
- @SuppressWarnings("unchecked")
- private Map getFENCEMapping() {
- try {
- return JAXRSConfiguration.objectMapper.readValue(
- new File(String.join(File.separator,
- new String[] {JAXRSConfiguration.templatePath ,"fence_mapping.json"}))
- , Map.class);
- } catch (IOException e) {
- logger.error("fence_mapping.json not found at "+JAXRSConfiguration.templatePath);
- }
- return Map.of();
- }
+ /*
+ * Get the mappings of fence privileges to paths
+ */
+ @SuppressWarnings("unchecked")
+ private Map getFENCEMapping() {
+ try {
+ return this.objectMapper.readValue(
+ new File(String.join(File.separator,
+ new String[]{this.templatePath, "fence_mapping.json"}))
+ , Map.class);
+ } catch (IOException e) {
+ logger.error("fence_mapping.json not found at " + this.templatePath);
+ }
+ return Map.of();
+ }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
deleted file mode 100644
index 5bf189714..000000000
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/MailService.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package edu.harvard.hms.dbmi.avillach.auth.service.impl;
-
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.StringWriter;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.mail.*;
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-
-import edu.harvard.hms.dbmi.avillach.auth.model.AccessEmail;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.mustachejava.DefaultMustacheFactory;
-import com.github.mustachejava.Mustache;
-import com.github.mustachejava.MustacheFactory;
-
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
-import edu.harvard.hms.dbmi.avillach.auth.entity.User;
-
-/**
- * Service class for sending email notifications.
- */
-@Service
-public class MailService {
- private static Logger logger = LoggerFactory.getLogger(MailService.class);
- private static MustacheFactory mf = new DefaultMustacheFactory();
-
- private Session mailSession;
-
- private static Mustache accessTemplate = compileTemplate("accessEmail.mustache");
- private static Mustache deniedTemplate = compileTemplate("deniedAccessEmail.mustache");
-
-
- public static final int SMTP_TIMEOUT_MS = 1000;
-
- public MailService(){
- //try to read this from the app container configuration
- mailSession = JAXRSConfiguration.mailSession;
- if(mailSession == null) {
- mailSession = Session.getDefaultInstance(System.getProperties());
- }
-
- // define timeout - wildfly doesn't read this from standalone (not in xml schema)
- Properties properties = mailSession.getProperties();
- properties.put("mail.smtp.connectiontimeout", SMTP_TIMEOUT_MS);
- }
-
- /**
- * Compile mustache template from templateFile
- *
- * @throws FileNotFoundException Exception thrown if templateFile is missing due to not being configured
- */
- private static Mustache compileTemplate(String templateFile) {
- try {
- FileReader reader = new FileReader(JAXRSConfiguration.templatePath + templateFile);
- return mf.compile(reader, templateFile);
- } catch (FileNotFoundException e) {
- logger.warn("email template not found for " + templateFile);
- return null;
- }
- }
-
- /**
- * Send email to user about changes in user Roles
- * @param user
- * @throws MessagingException
- * @throws AddressException
- */
- public void sendUsersAccessEmail(User user) throws AddressException, MessagingException{
- if(accessTemplate == null) {
- logger.debug("No template defined for new user access email, not sending");
- }else if (StringUtils.isEmpty(user.getEmail())) {
- logger.error("User " + (user.getSubject() != null ? user.getSubject() : "") + " has no email address.");
- } else {
- String subject = "Your Access To " + JAXRSConfiguration.systemName;
- if (JAXRSConfiguration.accessGrantEmailSubject != null && !JAXRSConfiguration.accessGrantEmailSubject.isEmpty() && !JAXRSConfiguration.accessGrantEmailSubject.equals("none")){
- subject = JAXRSConfiguration.accessGrantEmailSubject;
- }
- sendEmail(accessTemplate, user.getEmail(),subject, new AccessEmail(user));
- }
- }
-
- /**
- * Send email to admin about user being denied access to the system
- * @param userInfo User info object returned by authentication provider
- * @throws MessagingException
- * @throws AddressException
- */
- public void sendDeniedAccessEmail(JsonNode userInfo) throws AddressException, MessagingException{
- if(deniedTemplate == null) {
- logger.debug("No template for Access Denied email, not sending");
- } else {
- logger.info("Sending 'Access Denied' email to "
- + JAXRSConfiguration.adminUsers
- + ". User: "
- + userInfo.get("email") != null ? userInfo.get("email").asText() : userInfo.get("user_id").asText());
- ObjectMapper mapper = new ObjectMapper();
- Map scope = mapper.convertValue(userInfo, Map.class);
- scope.put("systemName", JAXRSConfiguration.systemName);
- sendEmail(deniedTemplate, JAXRSConfiguration.adminUsers, "User denied access to " + JAXRSConfiguration.systemName, scope);
- }
- }
-
- /**
- * Generate email from template and send it.
- * @param emailTemplate Name of the template.
- * @param to Recipients
- * @param subject Subject of the email
- * @param scope Object that contains attributes for template. e.g.: Map
- * @throws AddressException
- * @throws MessagingException
- */
- private void sendEmail(Mustache emailTemplate, String to, String subject, Object scope) throws AddressException, MessagingException {
- logger.debug("sendEmail(String, String, String, Object) - start");
- if (StringUtils.isEmpty(to) || StringUtils.isEmpty(subject) || scope == null || emailTemplate == null) {
- logger.error("One of the required parameters is null. Can't send email.");
- return;
- }
-
- Message message = new MimeMessage(mailSession);
- message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
- message.setSubject(subject);
- message.setContent(emailTemplate.execute(new StringWriter(), scope).toString(),"text/html");
-
- Transport.send(message);
- logger.debug("sendEmail() finished");
-
- }
-}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
index f74f716e5..91e1a6388 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java
@@ -2,7 +2,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
import edu.harvard.dbmi.avillach.util.response.PICSUREResponseOKwithMsgAndContent;
import edu.harvard.hms.dbmi.avillach.auth.entity.*;
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
@@ -39,7 +38,7 @@ public class UserService extends BaseEntityService {
private final Logger logger = LoggerFactory.getLogger(UserService.class.getName());
- private final MailService mailService;
+ private final BasicMailService basicMailService;
private final TOSService tosService;
private final UserRepository userRepository;
private final ConnectionRepository connectionRepository;
@@ -55,11 +54,11 @@ public class UserService extends BaseEntityService {
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
- public UserService(MailService mailService, TOSService tosService, UserRepository userRepository, ConnectionRepository connectionRepository, RoleRepository roleRepository, ApplicationRepository applicationRepository,
+ public UserService(BasicMailService basicMailService, TOSService tosService, UserRepository userRepository, ConnectionRepository connectionRepository, RoleRepository roleRepository, ApplicationRepository applicationRepository,
@Value("${application.client.secret}") String clientSecret, @Value("${application.token.expiration.time}") long tokenExpirationTime,
@Value("${application.default.}") String applicationUUID, @Value("${application.long.term.token.expiration.time}") long longTermTokenExpirationTime) {
super(User.class);
- this.mailService = mailService;
+ this.basicMailService = basicMailService;
this.tosService = tosService;
this.userRepository = userRepository;
this.connectionRepository = connectionRepository;
@@ -285,7 +284,7 @@ private void sendUserUpdateEmailsFromResponse(ResponseEntity> updateResponse)
String message = okResponse.getMessage();
for (User user : addedUsers) {
try {
- mailService.sendUsersAccessEmail(user);
+ basicMailService.sendUsersAccessEmail(user);
} catch (MessagingException e) {
logger.error("Failed to send email! " + e.getLocalizedMessage());
logger.debug("Exception Trace: ", e);
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
index ee7683780..a908d03ab 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JWTUtil.java
@@ -1,12 +1,12 @@
package edu.harvard.hms.dbmi.avillach.auth.utils;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException;
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
@@ -17,24 +17,28 @@
/**
* This class is for generating a JWT token and contains common methods for operations on JWT tokens.
- * For more information on JWT tokens, see https://github.com/hms-dbmi/jwt-creator/blob/master/src/main/java/edu/harvard/hms/dbmi/avillach/jwt/App.java
+ * For more information on JWT tokens, see ...
*/
+@Component
public class JWTUtil {
private final static Logger logger = LoggerFactory.getLogger(JWTUtil.class);
private static final long defaultTTLMillis = 1000L * 60 * 60 * 24 * 7;
@Value("${application.client.secret}")
- private static String CLIENT_SECRET;
+ private static String clientSecret;
+
+ @Value("${application.client.secret.base64}")
+ private static boolean clientSecretIsBase64;
/**
- * @param clientSecret
- * @param id
- * @param issuer
- * @param claims
- * @param subject
- * @param ttlMillis
- * @return
+ * @param clientSecret - client secret
+ * @param id - id
+ * @param issuer - issuer
+ * @param claims - claims
+ * @param subject - subject
+ * @param ttlMillis - time to live in milliseconds
+ * @return JWT token
*/
public static String createJwtToken(String clientSecret, String id, String issuer, Map claims, String subject, long ttlMillis) {
logger.debug("createJwtToken() starting...");
@@ -54,6 +58,7 @@ public static String createJwtToken(String clientSecret, String id, String issue
//We will sign our JWT with our ApiKey secret
byte[] apiKeySecretBytes = clientSecret.getBytes();
+
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//Builds the JWT and serializes it to a compact, URL-safe string
@@ -78,15 +83,15 @@ public static Jws parseToken(String token) {
Jws jws = null;
try {
- jws = Jwts.parser().setSigningKey(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
+ jws = Jwts.parser().setSigningKey(clientSecret.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
} catch (SignatureException e) {
try {
- if (JAXRSConfiguration.clientSecretIsBase64.startsWith("true")) {
+ if (clientSecretIsBase64) {
// handle if client secret is base64 encoded
- jws = Jwts.parser().setSigningKey(Base64.decodeBase64(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8))).parseClaimsJws(token);
+ jws = Jwts.parser().setSigningKey(Base64.decodeBase64(clientSecret.getBytes(StandardCharsets.UTF_8))).parseClaimsJws(token);
} else {
// handle if client secret is not base64 encoded
- jws = Jwts.parser().setSigningKey(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
+ jws = Jwts.parser().setSigningKey(clientSecret.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token);
}
} catch (JwtException | IllegalArgumentException ex) {
logger.error("parseToken() throws: " + e.getClass().getSimpleName() + ", " + e.getMessage());
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
index a9a6020dc..ecbdf0efd 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/JsonUtils.java
@@ -1,7 +1,5 @@
package edu.harvard.hms.dbmi.avillach.auth.utils;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.hms.dbmi.avillach.auth.JAXRSConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -130,69 +128,14 @@ public static Set mergeToNewSet(Object a, Object b){
}
- /*
- * Taking this method out for now, as we don't do index-based comparisons anymore. This may have been to
- * allow merging of variant info filters; I don't think that's necessary for query templates -NC 4/2020
- */
-
-// /**
-// * will merge the two elements in the same location.
-// * meaning baseList(0) will merge with incomingList(0) ...
-// * And if two same-location elements are not in the same type, only the element in baseList will be kept
-// *
-// * @param baseList
-// * @param incomingList
-// * @return
-// */
-// public static List mergeListToList(List baseList, List incomingList){
-// List mergedList = new ArrayList();
-//
-// if (incomingList.size()==0) {
-// addElementsOfListToMergedList(mergedList, baseList);
-// }else if(incomingList.get(0) instanceof String) {
-// addElementsOfListToMergedList(mergedList, incomingList);
-// addElementsOfListToMergedList(mergedList, baseList);
-// } else {
-// List sourceList = baseList.size()>incomingList.size()?baseList:incomingList;
-// List targetList = baseList.size()<=incomingList.size()?baseList:incomingList;
-// for (int i = 0 ; i < sourceList.size() ; i++) {
-// Object sourceElement = sourceList.get(i);
-// if(targetList.size()<=i) {
-// mergedList.add(sourceList.get(i));
-// }else {
-// Object targetElement = targetList.get(i);
-// if (sourceElement.getClass() == targetElement.getClass() ) {
-// if (sourceElement instanceof List){
-// mergedList.add(mergeListToList((List)sourceElement, (List)targetElement));
-// } else if (sourceElement instanceof Map){
-// mergedList.add(mergeTemplateMap((Map)sourceElement, (Map)targetElement));
-// } else {
-// logJsonTypeException(sourceElement);
-// }
-// } else {
-// mergedList.add(sourceElement);
-// }
-// }
-// }
-// }
-//
-// return mergedList;
-// }
-//
-// private static void addElementsOfListToMergedList(List mergedList, List baseList) {
-// if(baseList!=null&&baseList.size()>0) {
-// mergedList.addAll(baseList);
-// }
-// }
-
/**
* attach or merge a map to a list, the list might contain many elements that are string, list or map that are converted from JSONs.
* The logic here is first check if there is any map element that has the same structure based on the method isMapMergeable
,
* if true, merge, if false, simply attach.
*
- * @param map
- * @param list
- * @return
+ * @param map a map that is converted from a JSON.
+ * @param collection a list that contains many elements that are string, list or map that are converted from JSONs.
+ * @return a new list that contains the merged map.
*/
public static Set mergeMapToSet(Map map, Collection collection){
Set mergedSet = new HashSet(collection.size());
@@ -239,7 +182,7 @@ public static boolean isMapMergeable(Map baseMap, Map incomingMap){
private static void logJsonTypeException(Object value){
logger.error("Incoming JSON Object is a type: " + value.getClass() + ", can only merge String, List and Map!");
- throw new ApplicationException("Inner application error, please contact admin.");
+ throw new IllegalArgumentException("Inner application error, please contact admin.");
}
}
From b6f8bb426e0af037a3146cce49a6c0901ec7f6d8 Mon Sep 17 00:00:00 2001
From: GeorgeC
Date: Fri, 5 Apr 2024 15:42:14 -0400
Subject: [PATCH 013/222] Remove BaseEntityService and improve user-specific
behaviour
Removed the BaseEntityService class to streamline services. Updated several methods in the UserService to directly use the UserRepository, enhancing their reliability and readability. The AuthenticationService now also benefits from dependency injection for configuration properties, and ConnectionRepository has been incorporated.
---
pic-sure-auth-services/pom.xml | 76 +++--
.../dbmi/avillach/auth/entity/AccessRule.java | 13 +-
.../avillach/auth/entity/Application.java | 8 +-
.../dbmi/avillach/auth/entity/BaseEntity.java | 53 ++++
.../dbmi/avillach/auth/entity/Connection.java | 5 +-
.../dbmi/avillach/auth/entity/Privilege.java | 3 +-
.../hms/dbmi/avillach/auth/entity/Role.java | 9 +-
.../avillach/auth/entity/TermsOfService.java | 5 +-
.../hms/dbmi/avillach/auth/entity/User.java | 7 +-
.../auth/entity/UserMetadataMapping.java | 7 +-
.../dbmi/avillach/auth/filter/JWTFilter.java | 13 +-
.../dbmi/avillach/auth/model/AccessEmail.java | 5 +-
.../auth/repository/AccessRuleRepository.java | 9 +-
.../repository/ApplicationRepository.java | 13 +-
.../auth/repository/ConnectionRepository.java | 26 +-
.../auth/repository/PrivilegeRepository.java | 9 +-
.../auth/repository/RoleRepository.java | 9 +-
.../repository/TermsOfServiceRepository.java | 25 +-
.../UserMetadataMappingRepository.java | 12 +-
.../auth/repository/UserRepository.java | 177 +++---------
.../auth/rest/AccessRuleController.java | 35 ++-
.../auth/rest/ApplicationController.java | 30 +-
.../avillach/auth/rest/AuthController.java | 11 +-
.../auth/rest/ConnectionWebController.java | 39 ++-
.../auth/rest/PrivilegeController.java | 21 +-
.../avillach/auth/rest/RoleController.java | 32 ++-
.../avillach/auth/rest/UserController.java | 34 +--
.../UserMetadataMappingWebController.java | 59 ++--
.../auth/service/PrivilegeService.java | 42 ++-
.../avillach/auth/service/RoleService.java | 73 +++--
.../auth/service/impl/AccessRuleService.java | 27 +-
.../auth/service/impl/ApplicationService.java | 63 +++--
.../service/impl/AuthenticationService.java | 75 ++---
.../auth/service/impl/BaseEntityService.java | 266 ------------------
.../auth/service/impl/BasicMailService.java | 2 +-
.../service/impl/ConnectionWebService.java | 40 +--
.../impl/FENCEAuthenticationService.java | 175 ++++++------
.../impl/OauthUserMatchingService.java | 17 +-
.../auth/service/impl/TOSService.java | 63 +++--
.../auth/service/impl/TokenService.java | 15 +-
.../impl/UserMetadataMappingService.java | 40 +--
.../auth/service/impl/UserService.java | 136 +++++----
.../avillach/auth/utils/RestClientUtil.java | 82 ++++++
.../avillach/Auth0MatchingServiceTest.java | 10 +-
.../avillach/auth/utils/JsonUtilsTest.java | 44 +--
45 files changed, 885 insertions(+), 1030 deletions(-)
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/BaseEntity.java
delete mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/BaseEntityService.java
create mode 100644 pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/RestClientUtil.java
diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml
index 35eeca7da..cd1f5dffc 100644
--- a/pic-sure-auth-services/pom.xml
+++ b/pic-sure-auth-services/pom.xml
@@ -16,34 +16,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- edu.harvard.hms.dbmi.avillach
- pic-sure-api-data
- 2.1.0-SNAPSHOT
-
-
- org.apache.logging.log4j
- log4j-core
-
-
- org.slf4j
- slf4j-log4j12
-
-
- org.slf4j
- slf4j-api
-
-
- ch.qos.logback
- logback-classic
-
-
- org.slf4j
- slf4j-jdk14
-
-
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
@@ -86,7 +93,12 @@
spring-boot-starter-mail
3.2.4
-
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ 3.2.4
+
@@ -181,10 +193,16 @@
swagger-jaxrs2-servlet-initializer
2.0.0
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ RELEASE
+ compile
+
- edu.harvard.hms.dbmi.avillach.picsure.auth.microapp
- pic-sure-auth-microapp
- 1.0-SNAPSHOT
+ jakarta.transaction
+ jakarta.transaction-api
+ 2.0.1
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
index bbb6a98fa..314a68a50 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java
@@ -2,9 +2,17 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
-import javax.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.JoinTable;
+import jakarta.persistence.ManyToMany;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Transient;
+
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -34,7 +42,6 @@
*/
@Entity(name = "access_rule")
public class AccessRule extends BaseEntity {
-
/**
* please do not modify the existing values, in case the value has
* already saved in the database. But you can add more constant values, or
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
index 36981cfd3..b07770e6e 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Application.java
@@ -1,9 +1,11 @@
package edu.harvard.hms.dbmi.avillach.auth.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
-
-import javax.persistence.*;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
import java.security.Principal;
import java.util.Set;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/BaseEntity.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/BaseEntity.java
new file mode 100644
index 000000000..6e681ec3a
--- /dev/null
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/BaseEntity.java
@@ -0,0 +1,53 @@
+package edu.harvard.hms.dbmi.avillach.auth.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.MappedSuperclass;
+import org.hibernate.annotations.GenericGenerator;
+
+import java.util.Objects;
+import java.util.UUID;
+
+@MappedSuperclass
+public abstract class BaseEntity {
+ @Id
+ @GeneratedValue(generator = "UUID")
+ @GenericGenerator(
+ name = "UUID",
+ strategy = "org.hibernate.id.UUIDGenerator")
+ @Column(columnDefinition = "BINARY(16)")
+ protected UUID uuid;
+
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(UUID uuid) {
+ this.uuid = uuid;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (!(obj instanceof BaseEntity entity)) {
+ return false;
+ }
+ if (this.uuid == null){
+ return false;
+ }
+
+ return this.uuid.equals(entity.uuid);
+ }
+
+ @Override
+ public String toString() {
+ return this.uuid.toString();
+ }
+}
+
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
index 4c7e92a50..c4e4db8fc 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Connection.java
@@ -1,9 +1,8 @@
package edu.harvard.hms.dbmi.avillach.auth.entity;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
-import javax.persistence.Column;
-import javax.persistence.Entity;
import java.io.Serializable;
/**
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
index 7076d1b9d..4c7988b88 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Privilege.java
@@ -3,9 +3,8 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
+import jakarta.persistence.*;
-import javax.persistence.*;
import java.util.Set;
import java.util.stream.Collectors;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
index 529e74657..aa0fd91fd 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/Role.java
@@ -1,9 +1,8 @@
package edu.harvard.hms.dbmi.avillach.auth.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
+import jakarta.persistence.*;
-import javax.persistence.*;
import java.util.Set;
import java.util.stream.Collectors;
@@ -14,15 +13,15 @@
@Entity(name = "role")
public class Role extends BaseEntity {
- String name;
+ private String name;
- String description;
+ private String description;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "role_privilege",
joinColumns = {@JoinColumn(name = "role_id")},
inverseJoinColumns = {@JoinColumn(name = "privilege_id")})
- Set privileges;
+ private Set privileges;
public String getName() {
return name;
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
index f7615b68f..8843cfec6 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/TermsOfService.java
@@ -1,9 +1,8 @@
package edu.harvard.hms.dbmi.avillach.auth.entity;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
+import jakarta.persistence.Entity;
+import jakarta.persistence.PrePersist;
-import javax.persistence.Entity;
-import javax.persistence.PrePersist;
import java.util.Date;
/**
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
index 66765f564..1fc346e98 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/User.java
@@ -2,11 +2,10 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
import edu.harvard.hms.dbmi.avillach.auth.rest.UserController;
+import jakarta.persistence.*;
import org.hibernate.annotations.Type;
-import javax.persistence.*;
import java.io.Serializable;
import java.security.Principal;
import java.util.Date;
@@ -190,12 +189,12 @@ public String getPrivilegeString(){
if (totalPrivilegeSet == null)
return null;
- return totalPrivilegeSet.stream().map(p -> p.getName()).collect(Collectors.joining(","));
+ return totalPrivilegeSet.stream().map(Privilege::getName).collect(Collectors.joining(","));
}
@JsonIgnore
public String getRoleString(){
- return (roles==null)?null:roles.stream().map(r -> r.name)
+ return (roles==null)?null:roles.stream().map(Role::getName)
.collect(Collectors.joining(","));
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
index 5e44ffdfb..6d67d2ad6 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/UserMetadataMapping.java
@@ -1,8 +1,9 @@
package edu.harvard.hms.dbmi.avillach.auth.entity;
-import edu.harvard.dbmi.avillach.data.entity.BaseEntity;
-
-import javax.persistence.*;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
/**
* Model defining how to map an admin added user to an Oauth user profile.
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
index 2c9a590cb..80dd91090 100755
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java
@@ -23,6 +23,7 @@
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
+import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -92,8 +93,8 @@ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServlet
logger.debug(" token: {}", token);
// Parse the token
- Jws jws = parseToken(token); // TODO: We shouldn't be implementing a method that should be in the JWTUtils class
- String userId = jws.getBody().get(this.userClaimId, String.class); // TODO: Update when we remove the JAXRSConfiguration class
+ Jws jws = parseToken(token);
+ String userId = jws.getBody().get(this.userClaimId, String.class);
if (userId.startsWith(AuthNaming.LONG_TERM_TOKEN_PREFIX)) {
// For profile information, we do indeed allow long term token
@@ -122,14 +123,14 @@ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServlet
}
// Authenticate as Application
- Application authenticatedApplication = applicationRepo.getById(UUID.fromString(userId.split("\\|")[1]));
- if (authenticatedApplication == null) {
+ Optional authenticatedApplication = applicationRepo.findById(UUID.fromString(userId.split("\\|")[1]));
+ if (authenticatedApplication.isEmpty()) {
logger.error("Cannot find an application by userId: " + userId);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your token doesn't contain valid identical information, please contact admin.");
return;
}
- if (!authenticatedApplication.getToken().equals(token)) {
+ if (!authenticatedApplication.get().getToken().equals(token)) {
logger.error("filter() incoming application token - " + token +
" - is not the same as record, might because the token has been refreshed. Subject: " + userId);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Your token has been inactivated, please contact admin to grab you the latest one.");
@@ -137,7 +138,7 @@ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServlet
// This is the application token that is being used to authenticate the user by other applications
// Set the security context for the application
- setSecurityContextForApplication(request, authenticatedApplication);
+ setSecurityContextForApplication(request, authenticatedApplication.orElse(null));
} else {
logger.debug("UserID: {} is not a long term token and not a PSAMA application token.", userId);
// Authenticate as User
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
index 35ee19aa5..31825de94 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/model/AccessEmail.java
@@ -10,16 +10,17 @@
*/
public class AccessEmail {
- private String systemName = JAXRSConfiguration.systemName;
+ private String systemName = null;
private String documentation = null;
private String username;
private Set roles;
private boolean rolesExists;
- public AccessEmail(User u) {
+ public AccessEmail(User u, String systemName) {
this.username = u.getName();
this.roles = u.getRoles();
+ this.systemName = systemName;
}
public String getUsername() {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
index 2d9f9ec47..c668dcd57 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/AccessRuleRepository.java
@@ -1,7 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@@ -12,9 +12,8 @@
* @see AccessRule
*/
@Repository
-public class AccessRuleRepository extends BaseRepository {
+public interface AccessRuleRepository extends JpaRepository {
+
+ AccessRule findByName(String name);
- protected AccessRuleRepository() {
- super(AccessRule.class);
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
index ceb0f2960..86d82894a 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ApplicationRepository.java
@@ -1,7 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@@ -12,12 +12,9 @@
* @see Application
*/
-// TODO: A repository class is not the right place to annotate with transactional. Verify?
-// TODO: Is there a reason why we would scope this to the application?
@Repository
-public class ApplicationRepository extends BaseRepository {
+public interface ApplicationRepository extends JpaRepository {
- protected ApplicationRepository() {
- super(Application.class);
- }
-}
+ Application findByName(String name);
+
+}
\ No newline at end of file
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
index 3369bd3f8..ccd3ab5ef 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/ConnectionRepository.java
@@ -1,13 +1,9 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import javax.persistence.NoResultException;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Root;
import java.util.UUID;
/**
@@ -16,24 +12,8 @@
* @see Connection
*/
@Repository
-public class ConnectionRepository extends BaseRepository {
+public interface ConnectionRepository extends JpaRepository {
- protected ConnectionRepository() {
- super(Connection.class);
- }
+ Connection findByLabel(String label);
- public Connection findConnectionById(String connectionId) {
- CriteriaQuery query = em.getCriteriaBuilder().createQuery(Connection.class);
- Root queryRoot = query.from(Connection.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- try {
- return em.createQuery(query
- .where(
- eq(cb, queryRoot, "id", connectionId)))
- .getSingleResult();
- } catch (NoResultException e) {
- return null;
- }
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
index 7d02a05ff..c7af74330 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/PrivilegeRepository.java
@@ -1,7 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@@ -12,9 +12,8 @@
*/
@Repository
-public class PrivilegeRepository extends BaseRepository {
+public interface PrivilegeRepository extends JpaRepository {
+
+ Privilege findByName(String name);
- protected PrivilegeRepository() {
- super(Privilege.class);
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
index d2cb6ae1e..9aa53a9ad 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/RoleRepository.java
@@ -1,7 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@@ -12,9 +12,8 @@
*/
@Repository
-public class RoleRepository extends BaseRepository {
+public interface RoleRepository extends JpaRepository {
+
+ Role findByName(String name);
- protected RoleRepository() {
- super(Role.class);
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
index eb4ef256e..f18914ec5 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/TermsOfServiceRepository.java
@@ -1,12 +1,9 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Root;
import java.util.UUID;
/**
@@ -14,20 +11,12 @@
* @see TermsOfService
*/
@Repository
-public class TermsOfServiceRepository extends BaseRepository {
+public interface TermsOfServiceRepository extends JpaRepository {
- protected TermsOfServiceRepository() {
- super(TermsOfService.class);
- }
+ /**
+ * Find the latest TermsOfService by date updated.
+ * @return TermsOfService
+ */
+ TermsOfService findTopByOrderByDateUpdatedDesc();
- public TermsOfService getLatest(){
- CriteriaQuery query = cb().createQuery(TermsOfService.class);
- Root queryRoot = query.from(TermsOfService.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- return em.createQuery(query
- .orderBy(cb.desc(queryRoot.get("dateUpdated"))))
- .setMaxResults(1)
- .getSingleResult();
- }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
index fa8d10cc9..36a457d6d 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserMetadataMappingRepository.java
@@ -1,8 +1,8 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
import edu.harvard.hms.dbmi.avillach.auth.entity.UserMetadataMapping;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -14,14 +14,8 @@
*/
@Repository
-public class UserMetadataMappingRepository extends BaseRepository {
+public interface UserMetadataMappingRepository extends JpaRepository {
- protected UserMetadataMappingRepository() {
- super(UserMetadataMapping.class);
- }
-
- public List findByConnection(Connection connection) {
- return getByColumn("connection", connection);
- }
+ List findByConnection(Connection connection);
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
index 257939dc3..4fc0c6369 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/repository/UserRepository.java
@@ -1,25 +1,12 @@
package edu.harvard.hms.dbmi.avillach.auth.repository;
-import edu.harvard.dbmi.avillach.data.repository.BaseRepository;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
-import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
-import edu.harvard.hms.dbmi.avillach.auth.entity.TermsOfService;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
-import javax.persistence.NoResultException;
-import javax.persistence.NonUniqueResultException;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Root;
-import javax.persistence.criteria.Subquery;
-import java.util.Date;
import java.util.List;
-import java.util.Set;
import java.util.UUID;
-import java.util.stream.Collectors;
/**
* Provides operations for the User entity to interact with a database.
@@ -27,136 +14,36 @@
*/
@Repository
-public class UserRepository extends BaseRepository {
-
- private final static Logger logger = LoggerFactory.getLogger(UserRepository.class);
-
- protected UserRepository() {
- super(User.class);
- }
-
- public User findBySubject(String subject) {
- CriteriaQuery query = em.getCriteriaBuilder().createQuery(User.class);
- Root queryRoot = query.from(User.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- return em.createQuery(query
- .where(
- eq(cb, queryRoot, "subject", subject)))
- .getSingleResult();
- }
-
- public User findBySubjectAndConnection(String subject, String connectionId){
- CriteriaQuery query = em.getCriteriaBuilder().createQuery(User.class);
- Root queryRoot = query.from(User.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- try {
- return em.createQuery(query
- .where(
- cb.and(
- cb.equal(queryRoot.join("connection")
- .get("id"), connectionId),
- eq(cb, queryRoot, "subject", subject))))
- .getSingleResult();
- } catch (NoResultException e){
- return null;
- }
- }
-
- public List listUnmatchedByConnectionId(Connection connection) {
- CriteriaQuery query = em.getCriteriaBuilder().createQuery(User.class);
- Root queryRoot = query.from(User.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- return em.createQuery(query
- .where(
- cb.and(
- eq(cb, queryRoot, "connection", connection),
- eq(cb, queryRoot, "matched", false))))
- .getResultList();
- }
-
- /**
- *
- * @return
- */
- public User findOrCreate(User inputUser) {
- User user = null;
- String subject = inputUser.getSubject();
- try{
- user = findBySubject(subject);
- logger.info("findOrCreate(), trying to find user: {subject: " + subject+
- "}, and found a user with uuid: " + user.getUuid()
- + ", subject: " + user.getSubject());
- } catch (NoResultException e) {
- logger.debug("findOrCreate() subject " + subject +
- " could not be found by `entityManager`, going to create a new user.");
- user = createUser(inputUser);
- }catch(NonUniqueResultException e){
- logger.error("findOrCreate() " + e.getClass().getSimpleName() + ": " + e.getMessage());
- }
- return user;
- }
-
- private User createUser(User inputUser) {
- String subject = inputUser.getSubject();
- logger.debug("createUser() creating user, subject: " + subject + " ......");
- em().persist(inputUser);
-
- User result = getById(inputUser.getUuid());
- if (result != null)
- logger.info("createUser() created user, uuid: " + result.getUuid()
- + ", subject: " + subject
- + ", role: " + result.getRoleString()
- + ", privilege: "+ result.getPrivilegeString());
-
- return result;
- }
-
- public User changeRole(User user, Set roles){
- logger.info("Starting changing the role of user: " + user.getUuid()
- + ", with subject: " + user.getSubject() + ", to " + roles.stream().map(role -> role.getName()).collect(Collectors.joining(",")));
- user.setRoles(roles);
- em().merge(user);
- User updatedUser = getById(user.getUuid());
- logger.info("User: " + updatedUser.getUuid() + ", with subject: " +
- updatedUser.getSubject() + ", now has a new role: " + updatedUser.getRoleString());
- return updatedUser;
- }
-
- @Override
- public void persist(User user) {
- findOrCreate(user);
- }
-
- public User findByEmail(String email) {
- CriteriaQuery query = em.getCriteriaBuilder().createQuery(User.class);
- Root queryRoot = query.from(User.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
- return em.createQuery(query
- .where(
- eq(cb, queryRoot, "email", email)))
- .getSingleResult();
- }
-
- public boolean checkAgainstTOSDate(String userId){
- CriteriaQuery query = cb().createQuery(User.class);
- Root queryRoot = query.from(User.class);
- query.select(queryRoot);
- CriteriaBuilder cb = cb();
-
- Subquery subquery = query.subquery(Date.class);
- Root tosRoot = subquery.from(TermsOfService.class);
- subquery.select(cb.greatest(tosRoot.get("dateUpdated")));
-
- return !em.createQuery(query
- .where(
- cb.and(
- eq(cb, queryRoot, "subject", userId),
- cb.greaterThanOrEqualTo(queryRoot.get("acceptedTOS"), subquery))))
- .getResultList().isEmpty();
- }
+public interface UserRepository extends JpaRepository {
+
+ User findBySubject(String subject);
+
+ User findBySubjectAndConnection(String subject, Connection connection);
+
+ List findByConnectionAndMatched(Connection connection, boolean matched);
+
+//
+// public User changeRole(User user, Set roles){
+// logger.info("Starting changing the role of user: " + user.getUuid()
+// + ", with subject: " + user.getSubject() + ", to " + roles.stream().map(role -> role.getName()).collect(Collectors.joining(",")));
+// user.setRoles(roles);
+// em().merge(user);
+// User updatedUser = getById(user.getUuid());
+// logger.info("User: " + updatedUser.getUuid() + ", with subject: " +
+// updatedUser.getSubject() + ", now has a new role: " + updatedUser.getRoleString());
+// return updatedUser;
+// }
+//
+// @Override
+// public void persist(User user) {
+// findOrCreate(user);
+// }
+
+ /**
+ * Find a user by email.
+ * @param email the email to search for
+ * @return User
+ */
+ User findByEmail(String email);
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
index b49915b87..bd59944e9 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java
@@ -14,6 +14,7 @@
import org.springframework.web.bind.annotation.*;
import java.util.List;
+import java.util.Optional;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
@@ -21,7 +22,7 @@
/**
* Endpoint for service handling business logic for access rules.
* Note: Only users with the super admin role can access this endpoint.
- *
+ *
* Path: /accessRule
*/
@@ -39,16 +40,23 @@ public AccessRuleController(AccessRuleService accessRuleService) {
@Secured(value = {ADMIN, SUPER_ADMIN})
@GetMapping(value = "/{accessRuleId}")
public ResponseEntity> getAccessRuleById(
- @ApiParam(value="The UUID of the accessRule to fetch information about")
+ @ApiParam(value = "The UUID of the accessRule to fetch information about")
@PathVariable("accessRuleId") String accessRuleId) {
- return this.accessRuleService.getEntityById(accessRuleId);
+ Optional entityById = this.accessRuleService.getAccessRuleById(accessRuleId);
+
+ if (entityById.isEmpty()) {
+ return PICSUREResponse.error("AccessRule not found", 404);
+ }
+
+ return PICSUREResponse.success(entityById.get());
}
@ApiOperation(value = "GET a list of existing AccessRules, requires ADMIN or SUPER_ADMIN role")
@Secured({ADMIN, SUPER_ADMIN})
@GetMapping("")
public ResponseEntity> getAccessRuleAll() {
- return this.accessRuleService.getEntityAll();
+ List allAccessRules = this.accessRuleService.getAllAccessRules();
+ return PICSUREResponse.success(allAccessRules);
}
@ApiOperation(value = "POST a list of AccessRules, requires SUPER_ADMIN role")
@@ -56,8 +64,14 @@ public ResponseEntity> getAccessRuleAll() {
@PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity> addAccessRule(
@ApiParam(required = true, value = "A list of AccessRule in JSON format")
- List accessRules){
- return this.accessRuleService.addEntity(accessRules);
+ List accessRules) {
+ accessRules = this.accessRuleService.addAccessRule(accessRules);
+
+ if (accessRules.isEmpty()) {
+ return PICSUREResponse.protocolError("No access rules added", 400);
+ }
+
+ return PICSUREResponse.success(accessRules);
}
@ApiOperation(value = "Update a list of AccessRules, will only update the fields listed, requires SUPER_ADMIN role")
@@ -65,8 +79,9 @@ public ResponseEntity> addAccessRule(
@PutMapping(path = "/", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity> updateAccessRule(
@ApiParam(required = true, value = "A list of AccessRule with fields to be updated in JSON format")
- List accessRules){
- return this.accessRuleService.updateEntity(accessRules);
+ List accessRules) {
+ accessRules = this.accessRuleService.updateAccessRules(accessRules);
+ return PICSUREResponse.success(accessRules);
}
@ApiOperation(value = "DELETE an AccessRule by Id only if the accessRule is not associated by others, requires SUPER_ADMIN role")
@@ -75,13 +90,13 @@ public ResponseEntity> updateAccessRule(
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid accessRule Id")
@PathVariable("accessRuleId") final String accessRuleId) {
- return this.accessRuleService.removeEntityById(accessRuleId);
+ return PICSUREResponse.success(this.accessRuleService.removeAccessRuleById(accessRuleId));
}
@ApiOperation(value = "GET all types listed for the rule in accessRule that could be used, requires SUPER_ADMIN role")
@RolesAllowed(SUPER_ADMIN)
@GetMapping(path = "/allTypes", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
- public ResponseEntity> getAllTypes(){
+ public ResponseEntity> getAllTypes() {
return PICSUREResponse.success(AccessRule.TypeNaming.getTypeNameMap());
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
index a190ef2b0..4f82a7933 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ApplicationController.java
@@ -1,6 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import edu.harvard.hms.dbmi.avillach.auth.entity.Application;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.ApplicationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -11,8 +12,9 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
-import javax.transaction.Transactional;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
@@ -38,13 +40,19 @@ public ApplicationController(ApplicationService applicationService) {
public ResponseEntity> getApplicationById(
@ApiParam(required = true, value = "The UUID of the application to fetch information about")
@PathVariable("applicationId") String applicationId) {
- return applicationService.getEntityById(applicationId);
+ Optional entityById = applicationService.getApplicationByID(applicationId);
+
+ if (entityById.isEmpty()) {
+ return PICSUREResponse.protocolError("Application is not found by given Application ID: " + applicationId);
+ }
+
+ return PICSUREResponse.success(entityById.get());
}
@ApiOperation(value = "GET a list of existing Applications, no role restrictions")
@GetMapping
public ResponseEntity> getApplicationAll() {
- return applicationService.getEntityAll();
+ return PICSUREResponse.success(applicationService.getAllApplications());
}
@ApiOperation(value = "POST a list of Applications, requires SUPER_ADMIN role")
@@ -53,7 +61,8 @@ public ResponseEntity> getApplicationAll() {
public ResponseEntity> addApplication(
@ApiParam(required = true, value = "A list of AccessRule in JSON format")
List applications) {
- return applicationService.addNewApplications(applications);
+ applications = applicationService.addNewApplications(applications);
+ return PICSUREResponse.success(applications);
}
@ApiOperation(value = "Update a list of Applications, will only update the fields listed, requires SUPER_ADMIN role")
@@ -62,7 +71,8 @@ public ResponseEntity> addApplication(
public ResponseEntity> updateApplication(
@ApiParam(required = true, value = "A list of AccessRule with fields to be updated in JSON format")
List applications) {
- return applicationService.updateApplications(applications);
+ applications = applicationService.updateApplications(applications);
+ return PICSUREResponse.success(applications);
}
@ApiOperation(value = "Refresh a token of an application by application Id, requires SUPER_ADMIN role")
@@ -71,7 +81,8 @@ public ResponseEntity> updateApplication(
public ResponseEntity> refreshApplicationToken(
@ApiParam(required = true, value = "A valid application Id")
@PathVariable("applicationId") String applicationId) {
- return applicationService.refreshApplicationToken(applicationId);
+ String newApplicationToken = applicationService.refreshApplicationToken(applicationId);
+ return PICSUREResponse.success(Map.of("token", newApplicationToken));
}
@ApiOperation(value = "DELETE an Application by Id only if the application is not associated by others, requires SUPER_ADMIN role")
@@ -80,7 +91,12 @@ public ResponseEntity> refreshApplicationToken(
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid accessRule Id")
@PathVariable("applicationId") final String applicationId) {
- return applicationService.deleteApplicationById(applicationId);
+ try {
+ List applications = applicationService.deleteApplicationById(applicationId);
+ return PICSUREResponse.success(applications);
+ } catch (IllegalArgumentException e) {
+ return PICSUREResponse.protocolError(e.getMessage());
+ }
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
index 712c388eb..3a80c7778 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java
@@ -9,11 +9,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
+import java.io.IOException;
import java.util.Map;
@@ -32,18 +34,21 @@ public class AuthController {
public final AuthenticationService authenticationService;
public final FENCEAuthenticationService fenceAuthenticationService;
+ private final String idp_provider;
+
@Autowired
- public AuthController(AuthorizationService authorizationService, AuthenticationService authenticationService, FENCEAuthenticationService fenceAuthenticationService) {
+ public AuthController(AuthorizationService authorizationService, AuthenticationService authenticationService, FENCEAuthenticationService fenceAuthenticationService, @Value("${application.idp.provider}") String idpProvider) {
this.authorizationService = authorizationService;
this.authenticationService = authenticationService;
this.fenceAuthenticationService = fenceAuthenticationService;
+ this.idp_provider = idpProvider;
}
@ApiOperation(value = "The authentication endpoint for retrieving a valid user token")
@PostMapping(path = "/authentication", consumes = "application/json", produces = "application/json")
- public ResponseEntity> authentication(@ApiParam(required = true, value = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") Map authRequest) {
+ public ResponseEntity> authentication(@ApiParam(required = true, value = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") Map authRequest) throws IOException {
logger.debug("authentication() starting...");
- if (JAXRSConfiguration.idp_provider.equalsIgnoreCase("fence")) {
+ if (this.idp_provider.equalsIgnoreCase("fence")) {
logger.debug("authentication() FENCE authentication");
return fenceAuthenticationService.getFENCEProfile(authRequest);
} else {
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
index 923f98556..a6ccf5f36 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/ConnectionWebController.java
@@ -1,6 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.ConnectionWebService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -19,7 +20,7 @@
/**
* Endpoint for service handling business logic for connections to PSAMA.
- * Note: Only users with the super admin role can access this endpoint.
+ * Note: Only users with the super admin role can access this endpoint.
*/
@Api
@Controller
@@ -35,19 +36,25 @@ public ConnectionWebController(ConnectionWebService connectionWebSerivce) {
}
@ApiOperation(value = "GET information of one Connection with the UUID, requires ADMIN or SUPER_ADMIN role")
- @GetMapping(path ="/{connectionId}", produces = "application/json")
+ @GetMapping(path = "/{connectionId}", produces = "application/json")
@Secured({SUPER_ADMIN, ADMIN})
public ResponseEntity> getConnectionById(
- @ApiParam(required = true, value="The UUID of the Connection to fetch information about")
+ @ApiParam(required = true, value = "The UUID of the Connection to fetch information about")
@PathVariable("connectionId") String connectionId) {
- return connectionWebService.getEntityById(connectionId);
+ try {
+ Connection connectionById = connectionWebService.getConnectionById(connectionId);
+ return ResponseEntity.ok(connectionById);
+ } catch (IllegalArgumentException e) {
+ return PICSUREResponse.protocolError(e.getMessage());
+ }
}
@ApiOperation(value = "GET a list of existing Connection, requires SUPER_ADMIN or ADMIN role")
- @GetMapping(path ="/", produces = "application/json")
+ @GetMapping(path = "/", produces = "application/json")
@Secured({SUPER_ADMIN, ADMIN})
public ResponseEntity> getAllConnections() {
- return connectionWebService.getEntityAll();
+ List allConnections = connectionWebService.getAllConnections();
+ return ResponseEntity.ok(allConnections);
}
@ApiOperation(value = "POST a list of Connections, requires SUPER_ADMIN role")
@@ -56,8 +63,14 @@ public ResponseEntity> getAllConnections() {
@PostMapping(produces = "application/json", consumes = "application/json")
public ResponseEntity> addConnection(
@ApiParam(required = true, value = "A list of Connections in JSON format")
- List connections){
- return connectionWebService.addEntity(connections);
+ List connections) {
+ try {
+ connections = connectionWebService.addConnection(connections);
+ } catch (IllegalArgumentException e) {
+ return PICSUREResponse.protocolError(e.getMessage());
+ }
+
+ return PICSUREResponse.success("All connections are added.", connections);
}
@ApiOperation(value = "Update a list of Connections, will only update the fields listed, requires SUPER_ADMIN role")
@@ -65,18 +78,20 @@ public ResponseEntity> addConnection(
@PutMapping(produces = "application/json", consumes = "application/json")
public ResponseEntity> updateConnection(
@ApiParam(required = true, value = "A list of Connection with fields to be updated in JSON format")
- List connections){
- return connectionWebService.updateEntity(connections);
+ List connections) {
+ List responseEntity = connectionWebService.updateConnections(connections);
+ return ResponseEntity.ok(responseEntity);
}
@ApiOperation(value = "DELETE an Connection by Id only if the Connection is not associated by others, requires SUPER_ADMIN role")
@Transactional
@Secured({SUPER_ADMIN})
- @DeleteMapping(path ="/{connectionId}", produces = "application/json")
+ @DeleteMapping(path = "/{connectionId}", produces = "application/json")
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid connection Id")
@PathVariable("connectionId") final String connectionId) {
- return connectionWebService.removeEntityById(connectionId);
+ List connections = connectionWebService.removeConnectionById(connectionId);
+ return ResponseEntity.ok(connections);
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
index 5f1aa82bf..ec91ccf39 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/PrivilegeController.java
@@ -1,6 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.PrivilegeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -37,14 +38,21 @@ public PrivilegeController(PrivilegeService privilegeService) {
public ResponseEntity> getPrivilegeById(
@ApiParam(value="The UUID of the privilege to fetch information about")
@PathVariable("privilegeId") String privilegeId) {
- return this.privilegeService.getEntityById(privilegeId);
+ Privilege privilegeById = this.privilegeService.getPrivilegeById(privilegeId);
+
+ if (privilegeById == null) {
+ return PICSUREResponse.protocolError("Privilege not found");
+ }
+
+ return PICSUREResponse.success(privilegeById);
}
@ApiOperation(value = "GET a list of existing privileges, requires ADMIN or SUPER_ADMIN role")
@RolesAllowed({ADMIN, SUPER_ADMIN})
@GetMapping(path = "/", produces = "application/json")
public ResponseEntity> getPrivilegeAll() {
- return this.privilegeService.getEntityAll();
+ List privilegesAll = this.privilegeService.getPrivilegesAll();
+ return PICSUREResponse.success(privilegesAll);
}
@ApiOperation(value = "POST a list of privileges, requires SUPER_ADMIN role")
@@ -53,7 +61,8 @@ public ResponseEntity> getPrivilegeAll() {
public ResponseEntity> addPrivilege(
@ApiParam(required = true, value = "A list of privileges in JSON format")
List privileges){
- return this.privilegeService.addEntity(privileges);
+ privileges = this.privilegeService.addPrivileges(privileges);
+ return PICSUREResponse.success(privileges);
}
@ApiOperation(value = "Update a list of privileges, will only update the fields listed, requires SUPER_ADMIN role")
@@ -62,7 +71,8 @@ public ResponseEntity> addPrivilege(
public ResponseEntity> updatePrivilege(
@ApiParam(required = true, value = "A list of privilege with fields to be updated in JSON format")
List privileges){
- return this.privilegeService.updateEntity(privileges);
+ privileges = this.privilegeService.updatePrivileges(privileges);
+ return ResponseEntity.ok(privileges);
}
@ApiOperation(value = "DELETE an privilege by Id only if the privilege is not associated by others, requires SUPER_ADMIN role")
@@ -71,7 +81,8 @@ public ResponseEntity> updatePrivilege(
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid privilege Id")
@PathVariable("privilegeId") final String privilegeId) {
- return this.privilegeService.deletePrivilegeByPrivilegeId(privilegeId);
+ List privileges = this.privilegeService.deletePrivilegeByPrivilegeId(privilegeId);
+ return ResponseEntity.ok(privileges);
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
index 906bc0278..4b59d9ea3 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/RoleController.java
@@ -1,6 +1,7 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
+import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.service.RoleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -11,7 +12,9 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
+import java.text.MessageFormat;
import java.util.List;
+import java.util.Optional;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;
@@ -36,16 +39,17 @@ public RoleController(RoleService roleService) {
@RolesAllowed({ADMIN, SUPER_ADMIN})
@GetMapping(produces = "application/json", path = "/{roleId}")
public ResponseEntity> getRoleById(
- @ApiParam(value="The UUID of the Role to fetch information about")
+ @ApiParam(value = "The UUID of the Role to fetch information about")
@PathVariable("roleId") String roleId) {
- return this.roleService.getEntityById(roleId);
+ return this.roleService.getRoleById(roleId);
}
@ApiOperation(value = "GET a list of existing Roles, requires ADMIN or SUPER_ADMIN role")
@GetMapping(produces = "application/json")
@RolesAllowed({ADMIN, SUPER_ADMIN})
public ResponseEntity> getRoleAll() {
- return this.roleService.getEntityAll();
+ List allRoles = this.roleService.getAllRoles();
+ return PICSUREResponse.success(allRoles);
}
@ApiOperation(value = "POST a list of Roles, requires SUPER_ADMIN role")
@@ -53,8 +57,9 @@ public ResponseEntity> getRoleAll() {
@PostMapping(produces = "application/json")
public ResponseEntity> addRole(
@ApiParam(required = true, value = "A list of Roles in JSON format")
- List roles){
- return this.roleService.addEntity(roles);
+ List roles) {
+ List savedRoles = this.roleService.addRoles(roles);
+ return PICSUREResponse.success("All roles are added.", savedRoles);
}
@ApiOperation(value = "Update a list of Roles, will only update the fields listed, requires SUPER_ADMIN role")
@@ -62,8 +67,13 @@ public ResponseEntity> addRole(
@PutMapping(produces = "application/json")
public ResponseEntity> updateRole(
@ApiParam(required = true, value = "A list of Roles with fields to be updated in JSON format")
- List roles){
- return this.roleService.updateEntity(roles);
+ List roles) {
+ List updatedRoles = this.roleService.updateRoles(roles);
+ if (updatedRoles.isEmpty()) {
+ return PICSUREResponse.protocolError("No Role(s) has been updated.");
+ }
+
+ return PICSUREResponse.success("All Roles are updated.", updatedRoles);
}
@ApiOperation(value = "DELETE an Role by Id only if the Role is not associated by others, requires SUPER_ADMIN role")
@@ -72,9 +82,13 @@ public ResponseEntity> updateRole(
public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid Role Id")
@PathVariable("roleId") final String roleId) {
- return this.roleService.removeEntityById(roleId);
- }
+ Optional> roles = this.roleService.removeRoleById(roleId);
+ if (roles.isEmpty()) {
+ return PICSUREResponse.protocolError("Role not found - uuid: " + roleId);
+ }
+ return PICSUREResponse.success(MessageFormat.format("Successfully deleted role by id: {0}, listing rest of the role(s) as below", roleId), roles.get());
+ }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
index ae44f612b..a52f8fbb9 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserController.java
@@ -1,34 +1,21 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import edu.harvard.dbmi.avillach.util.exception.ApplicationException;
-import edu.harvard.dbmi.avillach.util.exception.ProtocolException;
import edu.harvard.hms.dbmi.avillach.auth.entity.*;
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.TOSService;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService;
-import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil;
-import edu.harvard.hms.dbmi.avillach.auth.utils.JsonUtils;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jws;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import jakarta.annotation.security.RolesAllowed;
-import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.transaction.Transactional;
-import java.io.IOException;
import java.util.*;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
@@ -45,12 +32,10 @@ public class UserController {
private final UserService userService;
- private final TOSService tosService;
@Autowired
- public UserController(UserService userService, TOSService tosService) {
+ public UserController(UserService userService) {
this.userService = userService;
- this.tosService = tosService;
}
@ApiOperation(value = "GET information of one user with the UUID, requires ADMIN or SUPER_ADMIN roles")
@@ -59,14 +44,16 @@ public UserController(UserService userService, TOSService tosService) {
public ResponseEntity> getUserById(
@ApiParam(required = true, value = "The UUID of the user to fetch information about")
@PathVariable("userId") String userId) {
- return this.userService.getEntityById(userId);
+ User userById = this.userService.getUserById(userId);
+ return ResponseEntity.ok(userById);
}
@ApiOperation(value = "GET a list of existing users, requires ADMIN or SUPER_ADMIN roles")
@RolesAllowed({ADMIN, SUPER_ADMIN})
@GetMapping(produces = "application/json")
public ResponseEntity> getUserAll() {
- return this.userService.getEntityAll();
+ List entityAll = this.userService.getAllUsers();
+ return ResponseEntity.ok(entityAll);
}
@ApiOperation(value = "POST a list of users, requires ADMIN role")
@@ -96,7 +83,6 @@ public ResponseEntity> updateUser(List users) {
* @return
*/
@ApiOperation(value = "Retrieve information of current user")
- @Transactional // TODO: Move this to the service layer
@GetMapping(produces = "application/json", path = "/me")
public ResponseEntity> getCurrentUser(
@RequestHeader("Authorization") String authorizationHeader,
@@ -106,12 +92,18 @@ public ResponseEntity> getCurrentUser(
}
@ApiOperation(value = "Retrieve the queryTemplate of certain application by given application Id for the currentUser ")
- @Transactional // TODO: Move this to the service layer
@GetMapping(path = "/me/queryTemplate/{applicationId}", produces = "application/json")
public ResponseEntity> getQueryTemplate(
@ApiParam(value = "Application Id for the returning queryTemplate")
@PathVariable("applicationId") String applicationId) {
- return this.userService.getQueryTemplate(applicationId);
+ Optional mergedTemplate = this.userService.getQueryTemplate(applicationId);
+
+ if (mergedTemplate.isEmpty()) {
+ logger.error("getDefaultQueryTemplate() cannot find corresponding application by UUID: {}", applicationId);
+ return PICSUREResponse.applicationError("Inner application error, please contact admin.");
+ }
+
+ return PICSUREResponse.success(Map.of("queryTemplate", mergedTemplate.orElse(null)));
}
@ApiOperation(value = "Retrieve the queryTemplate of default application")
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
index b2f692431..892be8a60 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/UserMetadataMappingWebController.java
@@ -26,52 +26,65 @@
@RequestMapping("/mapping")
public class UserMetadataMappingWebController {
- private final UserMetadataMappingService mappingService;
+ private final UserMetadataMappingService mappingService;
- @Autowired
+ @Autowired
public UserMetadataMappingWebController(UserMetadataMappingService mappingService) {
this.mappingService = mappingService;
}
@ApiOperation(value = "GET information of one UserMetadataMapping with the UUID, requires ADMIN or SUPER_ADMIN role")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- @GetMapping(path = "{connectionId}", produces = "application/json")
- public ResponseEntity> getMappingsForConnection(@PathVariable("connectionId") String connection) {
- return this.mappingService.getAllMappingsForConnection(connection);
- }
+ @GetMapping(path = "{connectionId}", produces = "application/json")
+ public ResponseEntity> getMappingsForConnection(@PathVariable("connectionId") String connection) {
+ return this.mappingService.getAllMappingsForConnection(connection);
+ }
@ApiOperation(value = "GET a list of existing UserMetadataMappings, requires ADMIN or SUPER_ADMIN role")
@RolesAllowed({ADMIN, SUPER_ADMIN})
- @GetMapping(path = "/", produces = "application/json")
- public ResponseEntity> getAllMappings() {
- List allMappings = mappingService.getAllMappings();
- return PICSUREResponse.success(allMappings);
- }
+ @GetMapping(path = "/", produces = "application/json")
+ public ResponseEntity> getAllMappings() {
+ List allMappings = mappingService.getAllMappings();
+ return PICSUREResponse.success(allMappings);
+ }
@ApiOperation(value = "POST a list of UserMetadataMappings, requires SUPER_ADMIN role")
@RolesAllowed({SUPER_ADMIN})
- @PostMapping(path = "/", consumes = "application/json", produces = "application/json")
- public ResponseEntity> addMapping(
+ @PostMapping(path = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> addMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping in JSON format")
List mappings) {
- return mappingService.addMappings(mappings);
- }
+
+ try {
+ List userMetadataMappings = mappingService.addMappings(mappings);
+ return PICSUREResponse.success(userMetadataMappings);
+ } catch (IllegalArgumentException e) {
+ return PICSUREResponse.error(e.getMessage());
+ }
+ }
@ApiOperation(value = "Update a list of UserMetadataMappings, will only update the fields listed, requires SUPER_ADMIN role")
@RolesAllowed({SUPER_ADMIN})
- @PutMapping(path = "/", consumes = "application/json", produces = "application/json")
- public ResponseEntity> updateMapping(
+ @PutMapping(path = "/", consumes = "application/json", produces = "application/json")
+ public ResponseEntity> updateMapping(
@ApiParam(required = true, value = "A list of UserMetadataMapping with fields to be updated in JSON format")
List mappings) {
- return this.mappingService.updateEntity(mappings);
- }
+ List userMetadataMappings = this.mappingService.updateUserMetadataMappings(mappings);
+
+ if (userMetadataMappings == null || userMetadataMappings.isEmpty()) {
+ return PICSUREResponse.error("No UserMetadataMapping found with the given Ids");
+ }
+
+ return PICSUREResponse.success(userMetadataMappings);
+ }
@ApiOperation(value = "DELETE an UserMetadataMapping by Id only if the UserMetadataMapping is not associated by others, requires SUPER_ADMIN role")
@RolesAllowed({SUPER_ADMIN})
- @DeleteMapping(path = "/{mappingId}", produces = "application/json")
- public ResponseEntity> removeById(
+ @DeleteMapping(path = "/{mappingId}", produces = "application/json")
+ public ResponseEntity> removeById(
@ApiParam(required = true, value = "A valid UserMetadataMapping Id")
@PathVariable("mappingId") final String mappingId) {
- return this.mappingService.removeEntityById(mappingId);
- }
+ List userMetadataMappings = this.mappingService.removeMetadataMappingByIdAndRetrieveAll(mappingId);
+ return PICSUREResponse.success(userMetadataMappings);
+ }
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
index 02253b5f4..50f871a2a 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/PrivilegeService.java
@@ -1,68 +1,66 @@
package edu.harvard.hms.dbmi.avillach.auth.service;
import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
-import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN;
@Service
-public class PrivilegeService extends BaseEntityService {
+public class PrivilegeService {
private final static Logger logger = LoggerFactory.getLogger(PrivilegeService.class.getName());
private final PrivilegeRepository privilegeRepository;
@Autowired
- protected PrivilegeService(Class type, PrivilegeRepository privilegeRepository) {
- super(type);
+ protected PrivilegeService(PrivilegeRepository privilegeRepository) {
this.privilegeRepository = privilegeRepository;
}
@Transactional
- public ResponseEntity> deletePrivilegeByPrivilegeId(String privilegeId) {
- Privilege privilege = this.privilegeRepository.getById(UUID.fromString(privilegeId));
+ public List deletePrivilegeByPrivilegeId(String privilegeId) {
+ Optional privilege = this.privilegeRepository.findById(UUID.fromString(privilegeId));
// Get security context with spring security context
SecurityContext securityContext = SecurityContextHolder.getContext();
// Get the principal name from the security context
String principalName = securityContext.getAuthentication().getName();
- if (ADMIN.equals(privilege.getName())) {
- logger.info("User: " + principalName
- + ", is trying to remove the system admin privilege: " + ADMIN);
- return PICSUREResponse.protocolError("System Admin privilege cannot be removed - uuid: " + privilege.getUuid().toString()
- + ", name: " + privilege.getName());
+ if (ADMIN.equals(privilege.get().getName())) {
+ logger.info("User: {}, is trying to remove the system admin privilege: " + ADMIN, principalName);
+ throw new RuntimeException("System Admin privilege cannot be removed - uuid: " + privilege.get().getUuid().toString()
+ + ", name: " + privilege.get().getName());
}
- return removeEntityById(privilegeId, this.privilegeRepository);
+ this.privilegeRepository.deleteById(UUID.fromString(privilegeId));
+ return this.getPrivilegesAll();
}
- public ResponseEntity> updateEntity(List privileges) {
- return updateEntity(privileges, this.privilegeRepository);
+ public List updatePrivileges(List privileges) {
+ this.privilegeRepository.saveAll(privileges);
+ return this.getPrivilegesAll();
}
- public ResponseEntity> addEntity(List privileges) {
- return addEntity(privileges, this.privilegeRepository);
+ public List addPrivileges(List privileges) {
+ return this.privilegeRepository.saveAll(privileges);
}
- public ResponseEntity> getEntityAll() {
- return getEntityAll(this.privilegeRepository);
+ public List getPrivilegesAll() {
+ return this.privilegeRepository.findAll();
}
- public ResponseEntity> getEntityById(String privilegeId) {
- return getEntityById(privilegeId, this.privilegeRepository);
+ public Privilege getPrivilegeById(String privilegeId) {
+ return this.privilegeRepository.findById(UUID.fromString(privilegeId)).orElse(null);
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java
index a531e0f6f..d0c41a9f5 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/RoleService.java
@@ -6,24 +6,20 @@
import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse;
import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository;
import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
-import edu.harvard.hms.dbmi.avillach.auth.service.impl.BaseEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
-import javax.transaction.Transactional;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@Service
-public class RoleService extends BaseEntityService {
+public class RoleService {
private final static Logger logger = Logger.getLogger(RoleService.class.getName());
private final RoleRepository roleRepository;
@@ -31,25 +27,27 @@ public class RoleService extends BaseEntityService {
private final PrivilegeRepository privilegeRepo;
@Autowired
- protected RoleService(Class type, RoleRepository roleRepository, PrivilegeRepository privilegeRepo) {
- super(type);
+ protected RoleService(RoleRepository roleRepository, PrivilegeRepository privilegeRepo) {
this.roleRepository = roleRepository;
this.privilegeRepo = privilegeRepo;
}
- public ResponseEntity> getEntityById(String roleId) {
- return getEntityById(roleId, roleRepository);
+ public ResponseEntity> getRoleById(String roleId) {
+ Optional optionalRole = roleRepository.findById(UUID.fromString(roleId));
+ if (optionalRole.isEmpty()) {
+ return PICSUREResponse.protocolError("Role is not found by given role ID: " + roleId);
+ }
+ return PICSUREResponse.success(optionalRole.get());
}
-
- public ResponseEntity> getEntityAll() {
- return getEntityAll(roleRepository);
+ public List getAllRoles() {
+ return roleRepository.findAll();
}
@Transactional
- public ResponseEntity> addEntity(List roles) {
+ public List addRoles(List roles) {
checkPrivilegeAssociation(roles);
- return addEntity(roles, roleRepository);
+ return roleRepository.saveAll(roles);
}
/**
@@ -61,8 +59,14 @@ public ResponseEntity> addEntity(List roles) {
private void checkPrivilegeAssociation(List roles) throws RuntimeException {
for (Role role: roles){
if (role.getPrivileges() != null) {
- Set privileges = new HashSet<>(); // TODO: Determine how we can fix this issue. The javax code does not work with java 21 in this case.
- role.getPrivileges().stream().forEach(p -> privilegeRepo.addObjectToSet(privileges, privilegeRepo, p));
+ Set privileges = new HashSet<>();
+ for (Privilege p : role.getPrivileges()) {
+ Optional privilege = privilegeRepo.findById(p.getUuid());
+ if (privilege.isEmpty()) {
+ throw new RuntimeException("Privilege not found - uuid: " + p.getUuid().toString());
+ }
+ privileges.add(privilege.get());
+ }
role.setPrivileges(privileges);
}
}
@@ -70,27 +74,40 @@ private void checkPrivilegeAssociation(List roles) throws RuntimeException
}
@Transactional
- public ResponseEntity> updateEntity(List roles) {
+ public List updateRoles(List roles) {
checkPrivilegeAssociation(roles);
- return updateEntity(roles, roleRepository);
+ return roleRepository.saveAll(roles);
}
@Transactional
- public ResponseEntity> removeEntityById(String roleId) {
- Role role = roleRepository.getById(UUID.fromString(roleId));
+ public Optional> removeRoleById(String roleId) {
+ Optional optionalRole = roleRepository.findById(UUID.fromString(roleId));
+
+ if (optionalRole.isEmpty()) {
+ return Optional.empty();
+ }
- // Get principal roles from security context
SecurityContext context = SecurityContextHolder.getContext();
Set roles = context.getAuthentication().getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
+ if (!SecurityRoles.contains(roles, SecurityRoles.PIC_SURE_TOP_ADMIN.getRole())){
+ logger.info("User doesn't have PIC-SURE Top Admin role, can't remove any role");
+ return Optional.empty();
+ }
+
+ roleRepository.deleteById(optionalRole.get().getUuid());
+ return Optional.of(roleRepository.findAll());
+ }
- if (SecurityRoles.contains(roles, SecurityRoles.PIC_SURE_TOP_ADMIN.getRole())){
- logger.info("User has PIC-SURE Top Admin role, can remove any role");
- return PICSUREResponse.protocolError("Default System Role cannot be removed - uuid: " + role.getUuid().toString()
- + ", name: " + role.getName());
+ public void addObjectToSet(Set roles, Role t) {
+ // check if the role exists in the database
+ Role role = roleRepository.findById(t.getUuid()).orElse(null);
+ if (role == null) {
+ throw new RuntimeException("Role not found - uuid: " + t.getUuid().toString());
}
- return removeEntityById(roleId, roleRepository);
+
+ roles.add(t);
}
}
diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java
index 625fc574b..62e079e0f 100644
--- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java
+++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java
@@ -3,33 +3,33 @@
import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
@Service
-public class AccessRuleService extends BaseEntityService{
+public class AccessRuleService {
private final AccessRuleRepository accessRuleRepo;
@Autowired
protected AccessRuleService(Class type, AccessRuleRepository accessRuleRepo) {
- super(type);
this.accessRuleRepo = accessRuleRepo;
}
- public ResponseEntity> getEntityById(String accessRuleId) {
- return getEntityById(accessRuleId, accessRuleRepo);
+ public Optional getAccessRuleById(String accessRuleId) {
+ return accessRuleRepo.findById(UUID.fromString(accessRuleId));
}
- public ResponseEntity> getEntityAll() {
- return getEntityAll(accessRuleRepo);
+ public List getAllAccessRules() {
+ return accessRuleRepo.findAll();
}
- public ResponseEntity> addEntity(List accessRules) {
+ public List addAccessRule(List accessRules) {
accessRules.forEach(accessRule -> {
if (accessRule.getEvaluateOnlyByGates() == null)
accessRule.setEvaluateOnlyByGates(false);
@@ -44,15 +44,16 @@ public ResponseEntity> addEntity(List