errors = validation.getErrors();
- assertFalse(validation.isValid());
- assertEquals(1, errors.size());
- Mockito.verify(messageSource, Mockito.times(1)).getMessage(errorMessagePropertyCaptor.capture(), Mockito.any(), Mockito.any());
- String propertyName = errorMessagePropertyCaptor.getValue();
- assertEquals("member.validation.error.invalidMemberStatus", propertyName);
- }
-
- @Test
- public void testValidateMemberWithValidMemberStatus() {
- Member member = getMemberWithValidNewClientId();
- member.setStatus("Active");
- MemberValidation validation = memberValidator.validate(member, getUser());
- assertTrue(validation.isValid());
-
- member.setStatus("Deactivated");
- validation = memberValidator.validate(member, getUser());
+ validation = memberValidator.validate(member, "en");
assertTrue(validation.isValid());
}
@@ -356,12 +330,6 @@ private Member getMemberWithMissingConsortiumLead() {
return member;
}
- private User getUser() {
- User user = new User();
- user.setLangKey("en");
- return user;
- }
-
private Member getMemberWithMissingAssertionsEnabled() {
Member member = getMember();
member.setAssertionServiceEnabled(null);
diff --git a/member-service-2/src/test/resources/application.yaml b/member-service-2/src/test/resources/application.yaml
index 48122ecb2..1584763a4 100644
--- a/member-service-2/src/test/resources/application.yaml
+++ b/member-service-2/src/test/resources/application.yaml
@@ -7,6 +7,16 @@ spring:
resourceserver:
jwt:
issuer-uri: http://localhost:9000
+ client:
+ registration:
+ internal-member-client:
+ provider: dummy-provider
+ client-id: test-id
+ client-secret: test-secret
+ authorization-grant-type: client_credentials
+ provider:
+ dummy-provider:
+ token-uri: http://localhost:9000/oauth2/token
application:
encrypt:
salt: 5a8b9f1d2c6e7a403b5d8e9c0f4a1b32
diff --git a/member-service-2/src/test/resources/salesforce/members.json b/member-service-2/src/test/resources/salesforce/members.json
new file mode 100644
index 000000000..bbd70aca2
--- /dev/null
+++ b/member-service-2/src/test/resources/salesforce/members.json
@@ -0,0 +1,112 @@
+{
+ "totalSize": 5,
+ "done": true,
+ "nextRecordsUrl": null,
+ "records": [
+ {
+ "Id": "0011000001XYZ01",
+ "Consortia_Member__c": false,
+ "Consortium_Lead__c": null,
+ "Name": "Global Research University",
+ "Public_Display_Name__c": "Global Res Uni",
+ "Website": "https://www.globalresuni.edu",
+ "BillingCountry": "US",
+ "Research_Community__c": "Academic",
+ "Public_Display_Description__c": "Leading global institution.
",
+ "Logo_Description__c": "https://logos.com/gru.png",
+ "Public_Display_Email__c": "info@globalresuni.edu",
+ "Last_membership_start_date__c": "2025-01-01",
+ "Last_membership_end_date__c": "2026-12-31",
+ "Trademark_License__c": "Approved",
+ "BillingAddress": {
+ "city": "Boston",
+ "country": "US"
+ },
+ "Active_Member__c": true
+ },
+ {
+ "Id": "0011000002XYZ02",
+ "Consortia_Member__c": true,
+ "Consortium_Lead__c": "0011000001XYZ01",
+ "Name": "Consortium Sub-Member A",
+ "Public_Display_Name__c": "Sub-Member A",
+ "Website": "https://sub-a.org",
+ "BillingCountry": "UK",
+ "Research_Community__c": "Healthcare",
+ "Public_Display_Description__c": null,
+ "Logo_Description__c": null,
+ "Public_Display_Email__c": "contact@sub-a.org",
+ "Last_membership_start_date__c": "2024-06-15",
+ "Last_membership_end_date__c": "2026-06-14",
+ "Trademark_License__c": "Pending",
+ "BillingAddress": {
+ "city": "London",
+ "country": "UK"
+ },
+ "Active_Member__c": true
+ },
+ {
+ "Id": "0011000003XYZ03",
+ "Consortia_Member__c": true,
+ "Consortium_Lead__c": "0011000001XYZ01",
+ "Name": "Consortium Sub-Member B",
+ "Public_Display_Name__c": "Sub-Member B",
+ "Website": "https://sub-b.org",
+ "BillingCountry": "FR",
+ "Research_Community__c": "Government",
+ "Public_Display_Description__c": "National laboratory.
",
+ "Logo_Description__c": "https://logos.com/sub-b.png",
+ "Public_Display_Email__c": null,
+ "Last_membership_start_date__c": "2025-03-01",
+ "Last_membership_end_date__c": "2027-02-28",
+ "Trademark_License__c": "Approved",
+ "BillingAddress": {
+ "city": "Paris",
+ "country": "FR"
+ },
+ "Active_Member__c": true
+ },
+ {
+ "Id": "0011000004XYZ04",
+ "Consortia_Member__c": false,
+ "Consortium_Lead__c": null,
+ "Name": "Legacy Research Lab",
+ "Public_Display_Name__c": "Legacy Lab",
+ "Website": "https://legacylab.com",
+ "BillingCountry": "CA",
+ "Research_Community__c": "Corporate",
+ "Public_Display_Description__c": null,
+ "Logo_Description__c": null,
+ "Public_Display_Email__c": "admin@legacylab.com",
+ "Last_membership_start_date__c": "2020-01-01",
+ "Last_membership_end_date__c": "2023-12-31",
+ "Trademark_License__c": "Expired",
+ "BillingAddress": {
+ "city": "Toronto",
+ "country": "CA"
+ },
+ "Active_Member__c": false
+ },
+ {
+ "Id": "0011000005XYZ05",
+ "Consortia_Member__c": false,
+ "Consortium_Lead__c": null,
+ "Name": "New Horizon Publisher",
+ "Public_Display_Name__c": "New Horizon",
+ "Website": "https://newhorizonpub.com",
+ "BillingCountry": "AU",
+ "Research_Community__c": "Publisher",
+ "Public_Display_Description__c": "Open access publisher.
",
+ "Logo_Description__c": null,
+ "Public_Display_Email__c": "oa@newhorizonpub.com",
+ "Last_membership_start_date__c": "2026-01-01",
+ "Last_membership_end_date__c": "2026-12-31",
+ "Trademark_License__c": "Approved",
+ "BillingAddress": {
+ "city": "Sydney",
+ "country": "AU"
+ },
+ "Active_Member__c": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ui-2/src/app/member/member-detail.component.html b/ui-2/src/app/member/member-detail.component.html
index 8b2fe45f6..710729945 100644
--- a/ui-2/src/app/member/member-detail.component.html
+++ b/ui-2/src/app/member/member-detail.component.html
@@ -59,7 +59,7 @@ M
Status
- {{ member()?.status }}
+ {{ member()?.active ? 'Active' : 'Deactivated' }}
Created Date
diff --git a/ui-2/src/app/member/model/member.model.ts b/ui-2/src/app/member/model/member.model.ts
index a330f5c60..98c643280 100644
--- a/ui-2/src/app/member/model/member.model.ts
+++ b/ui-2/src/app/member/model/member.model.ts
@@ -15,7 +15,7 @@ export interface IMember {
lastModifiedBy?: string
lastModifiedDate?: Moment | undefined | null
type?: string
- status?: string
+ active?: boolean
defaultLanguage?: string
}
@@ -35,8 +35,8 @@ export class Member implements IMember {
public lastModifiedBy?: string,
public lastModifiedDate?: Moment | undefined | null,
public type?: string,
- public status?: string,
- public defaultLaungage?: string
+ public active?: boolean,
+ public defaultLanguage?: string
) {
this.isConsortiumLead = this.isConsortiumLead || false
this.superadminEnabled = this.superadminEnabled || false
diff --git a/user-service-2/src/main/java/org/orcid/mp/user/config/SecurityConfig.java b/user-service-2/src/main/java/org/orcid/mp/user/config/SecurityConfig.java
index e5f019a13..f02fad9ea 100644
--- a/user-service-2/src/main/java/org/orcid/mp/user/config/SecurityConfig.java
+++ b/user-service-2/src/main/java/org/orcid/mp/user/config/SecurityConfig.java
@@ -75,6 +75,12 @@ public class SecurityConfig {
@Value("${application.internal.assertionServiceClientSecret}")
private String internalAssertionServiceClientSecret;
+ @Value("${application.internal.memberServiceClientId}")
+ private String internalMemberServiceClientId;
+
+ @Value("${application.internal.memberServiceClientSecret}")
+ private String internalMemberServiceClientSecret;
+
@Value("${application.security.jwt.private-key}")
private RSAPrivateKey privateKey;
@@ -210,7 +216,18 @@ public RegisteredClientRepository registeredClientRepository(PasswordEncoder enc
.build())
.build();
- return new InMemoryRegisteredClientRepository(oidcClient, userServiceClient, assertionServiceClient);
+ RegisteredClient memberServiceClient = RegisteredClient.withId(UUID.randomUUID().toString())
+ .clientId(internalMemberServiceClientId)
+ .clientSecret(encoder.encode(internalMemberServiceClientSecret))
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
+ .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+ .scope("internal")
+ .tokenSettings(TokenSettings.builder()
+ .accessTokenTimeToLive(Duration.ofMinutes(5)) // Keep internal tokens short-lived
+ .build())
+ .build();
+
+ return new InMemoryRegisteredClientRepository(oidcClient, userServiceClient, assertionServiceClient, memberServiceClient);
}
@Bean
diff --git a/user-service-2/src/main/java/org/orcid/mp/user/rest/InternalResource.java b/user-service-2/src/main/java/org/orcid/mp/user/rest/InternalResource.java
index 03d5b3d41..07256ad3b 100644
--- a/user-service-2/src/main/java/org/orcid/mp/user/rest/InternalResource.java
+++ b/user-service-2/src/main/java/org/orcid/mp/user/rest/InternalResource.java
@@ -8,10 +8,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@@ -37,8 +34,8 @@ public class InternalResource {
* body the user, or with status {@code 404 (Not Found)}.
*/
@GetMapping("/{loginOrId}")
- public ResponseEntity getUserByLogin(@PathVariable String loginOrId) {
- LOG.debug("REST request to get User : {}", loginOrId);
+ public ResponseEntity getUserByLoginOrId(@PathVariable String loginOrId) {
+ LOG.debug("Internal request to get User : {}", loginOrId);
Optional user = userService.getUserByLogin(loginOrId);
if (!user.isPresent()) {
user = userService.getUser(loginOrId);
@@ -49,4 +46,23 @@ public ResponseEntity getUserByLogin(@PathVariable String loginOrId) {
return ResponseEntity.ok(userMapper.toUserDTO(user.get()));
}
+ /**
+ * {@code PUT /users/memberName/:oldMemberName/:newMemberName} : Updates memberName
+ * for existing Users.
+ *
+ * @param memberId the memberId for finding users to update
+ * @param newMemberName the new Value of the memberName to update
+ * @return the {@link ResponseEntity} with status {@code 200 (OK)}.
+ */
+ @PutMapping("/memberName/{memberId}/{newMemberName}")
+ public ResponseEntity updateUsersMemberName(@PathVariable String memberId, @PathVariable String newMemberName) {
+ LOG.info("Internal request to update users' member names id to {}", newMemberName);
+ boolean success = userService.updateUsersMemberName(memberId, newMemberName);
+ if (success) {
+ return ResponseEntity.ok().build();
+ } else {
+ return ResponseEntity.status(500).build();
+ }
+ }
+
}
diff --git a/user-service-2/src/main/java/org/orcid/mp/user/rest/UserResource.java b/user-service-2/src/main/java/org/orcid/mp/user/rest/UserResource.java
index 6f502db98..3f469a5ee 100644
--- a/user-service-2/src/main/java/org/orcid/mp/user/rest/UserResource.java
+++ b/user-service-2/src/main/java/org/orcid/mp/user/rest/UserResource.java
@@ -313,26 +313,6 @@ public ResponseEntity resendActivation(@PathVariable S
return ResponseEntity.ok(response);
}
- /**
- * {@code PUT /users/memberName/:oldMemberName/:newMemberName} : Updates memberName
- * for existing Users.
- *
- * @param memberId the memberId for finding users to update
- * @param newMemberName the new Value of the memberName to update
- * @return the {@link ResponseEntity} with status {@code 200 (OK)}.
- */
- @PutMapping("/memberName/{memberId}/{newMemberName}")
- @PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")")
- public ResponseEntity updateUsersMemberName(@PathVariable String memberId, @PathVariable String newMemberName) {
- LOG.debug("REST request to update users' member names id to {}", newMemberName);
- boolean success = userService.updateUsersMemberName(memberId, newMemberName);
- if (success) {
- return ResponseEntity.ok().headers(JHipsterAlerts.createEntityUpdateAlert(applicationName, true, "user", memberId)).build();
- } else {
- return ResponseEntity.status(500).build();
- }
- }
-
/**
* {@code GET /users/:saleforceId}/owner : get the "login" user.
*
diff --git a/user-service-2/src/main/resources/application.yml b/user-service-2/src/main/resources/application.yml
index 5afdf12b7..8393760de 100644
--- a/user-service-2/src/main/resources/application.yml
+++ b/user-service-2/src/main/resources/application.yml
@@ -92,6 +92,8 @@ application:
clientSecret: ${APPLICATION_INTERNAL_CLIENT_SECRET}
assertionServiceClientId: ${APPLICATION_INTERNAL_ASSERTION_SERVICE_ID}
assertionServiceClientSecret: ${APPLICATION_INTERNAL_ASSERTION_SERVICE_CLIENT_SECRET}
+ memberServiceClientId: ${APPLICATION_INTERNAL_MEMBER_SERVICE_ID}
+ memberServiceClientSecret: ${APPLICATION_INTERNAL_MEMBER_SERVICE_CLIENT_SECRET}
security:
issuerUrl: ${APPLICATION_SECURITY_ISSUER_URL}
jwt:
diff --git a/user-service-2/src/test/java/org/orcid/mp/user/rest/InternalResourceTest.java b/user-service-2/src/test/java/org/orcid/mp/user/rest/InternalResourceTest.java
new file mode 100644
index 000000000..c2815b139
--- /dev/null
+++ b/user-service-2/src/test/java/org/orcid/mp/user/rest/InternalResourceTest.java
@@ -0,0 +1,100 @@
+package org.orcid.mp.user.rest;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.orcid.mp.user.domain.User;
+import org.orcid.mp.user.dto.UserDTO;
+import org.orcid.mp.user.mapper.UserMapper;
+import org.orcid.mp.user.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+public class InternalResourceTest {
+
+ @Mock
+ private UserService userService;
+
+ @Mock
+ private UserMapper userMapper;
+
+ @InjectMocks
+ private InternalResource internalResource;
+
+ @BeforeEach
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Authentication authentication = Mockito.mock(Authentication.class);
+ SecurityContext securityContext = Mockito.mock(SecurityContext.class);
+ when(securityContext.getAuthentication()).thenReturn(authentication);
+ SecurityContextHolder.setContext(securityContext);
+ when(authentication.getPrincipal()).thenReturn("some@email.com");
+
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
+ }
+
+ @Test
+ void testGetUserByLoginOrId_login() {
+ when(userService.getUserByLogin(Mockito.eq("some@email.com"))).thenReturn(getUser());
+ when(userMapper.toUserDTO(Mockito.any())).thenReturn(new UserDTO());
+
+ ResponseEntity response = internalResource.getUserByLoginOrId("some@email.com");
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+
+ @Test
+ void testGetUserByLoginOrId_notFound() {
+ when(userService.getUserByLogin(Mockito.eq("some@email.com"))).thenReturn(Optional.empty());
+ when(userService.getUser(Mockito.eq("some@email.com"))).thenReturn(Optional.empty());
+
+ ResponseEntity response = internalResource.getUserByLoginOrId("some@email.com");
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ }
+
+ @Test
+ void testGetUserByLoginOrId_id() {
+ when(userService.getUserByLogin(Mockito.eq("some@email.com"))).thenReturn(Optional.empty());
+ when(userService.getUser(Mockito.eq("some@email.com"))).thenReturn(getUser());
+ when(userMapper.toUserDTO(Mockito.any())).thenReturn(new UserDTO());
+
+ ResponseEntity response = internalResource.getUserByLoginOrId("some@email.com");
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+ @Test
+ void testUpdateUsersMemberName() {
+ when(userService.updateUsersMemberName(Mockito.eq("member-id"), Mockito.eq("newName"))).thenReturn(true);
+ ResponseEntity response = internalResource.updateUsersMemberName("member-id", "newName");
+ assertTrue(response.getStatusCode().is2xxSuccessful());
+ }
+
+ private Optional getUser() {
+ User user = new User();
+ user.setEmail("some@email.com");
+ user.setLangKey("en");
+ user.setMemberId("some-memberId");
+ user.setMainContact(true);
+ return Optional.of(user);
+ }
+}
diff --git a/user-service-2/src/test/java/org/orcid/mp/user/rest/UserResourceTest.java b/user-service-2/src/test/java/org/orcid/mp/user/rest/UserResourceTest.java
index 30beaa0e9..66f5196bd 100644
--- a/user-service-2/src/test/java/org/orcid/mp/user/rest/UserResourceTest.java
+++ b/user-service-2/src/test/java/org/orcid/mp/user/rest/UserResourceTest.java
@@ -43,9 +43,6 @@ public class UserResourceTest {
@Mock
private UserValidator userValidator;
- @Mock
- private UserMapper userMapper;
-
@InjectMocks
private UserResource userResource;
@@ -64,13 +61,6 @@ public void setUp() {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}
- @Test
- void testUpdateUsersMemberName() {
- Mockito.when(userService.updateUsersMemberName(Mockito.eq("member-id"), Mockito.eq("newName"))).thenReturn(true);
- ResponseEntity response = userResource.updateUsersMemberName("member-id", "newName");
- assertTrue(response.getStatusCode().is2xxSuccessful());
- }
-
@Test
public void testResendActivation() {
Mockito.doNothing().when(userService).resendActivationEmail(Mockito.anyString());
diff --git a/user-service-2/src/test/resources/application.yml b/user-service-2/src/test/resources/application.yml
index cab884918..2ff9581ee 100644
--- a/user-service-2/src/test/resources/application.yml
+++ b/user-service-2/src/test/resources/application.yml
@@ -26,6 +26,8 @@ application:
clientSecret: userserviceSecret
assertionServiceClientId: assertionServiceClientId
assertionServiceClientSecret: assertionServiceClientSecret
+ memberServiceClientId: memberServiceClientId
+ memberServiceClientSecret: memberServiceClientSecret
encrypt:
salt: 5a8b9f1d2c6e7a403b5d8e9c0f4a1b32
key: user-service