Skip to content

Commit 3f3c2af

Browse files
achmelotaban03
andauthored
feat: change pass via saf (#1471)
* skip zosmf login tests Signed-off-by: achmelo <a.chmelo@gmail.com> * debug logs Signed-off-by: achmelo <a.chmelo@gmail.com> * revert auth login tests Signed-off-by: achmelo <a.chmelo@gmail.com> * query logs Signed-off-by: achmelo <a.chmelo@gmail.com> * login logs Signed-off-by: achmelo <a.chmelo@gmail.com> * disable timeout Signed-off-by: achmelo <a.chmelo@gmail.com> * run authtest only Signed-off-by: achmelo <a.chmelo@gmail.com> * disable cert test Signed-off-by: achmelo <a.chmelo@gmail.com> * imports Signed-off-by: achmelo <a.chmelo@gmail.com> * try old format only Signed-off-by: achmelo <a.chmelo@gmail.com> * only inbox for validation Signed-off-by: achmelo <a.chmelo@gmail.com> * logout old path only Signed-off-by: achmelo <a.chmelo@gmail.com> * only 1 logout Signed-off-by: achmelo <a.chmelo@gmail.com> * dont verify login Signed-off-by: achmelo <a.chmelo@gmail.com> * do not run login Signed-off-by: achmelo <a.chmelo@gmail.com> * sleep after logout Signed-off-by: achmelo <a.chmelo@gmail.com> * sleep after logout Signed-off-by: achmelo <a.chmelo@gmail.com> * retry login when invalid jwt is returned Signed-off-by: achmelo <a.chmelo@gmail.com> * backoff 4s Signed-off-by: achmelo <a.chmelo@gmail.com> * return back all tests Signed-off-by: achmelo <a.chmelo@gmail.com> * enable retry Signed-off-by: achmelo <a.chmelo@gmail.com> * only auth tests Signed-off-by: achmelo <a.chmelo@gmail.com> * invalidate ltpa Signed-off-by: achmelo <a.chmelo@gmail.com> * all tests Signed-off-by: achmelo <a.chmelo@gmail.com> * cleanup Signed-off-by: achmelo <a.chmelo@gmail.com> * use cache instead of service Signed-off-by: achmelo <a.chmelo@gmail.com> * test new auth flow Signed-off-by: achmelo <a.chmelo@gmail.com> * rename test Signed-off-by: achmelo <a.chmelo@gmail.com> * cleanup Signed-off-by: achmelo <a.chmelo@gmail.com> * update password with saf Signed-off-by: achmelo <a.chmelo@gmail.com> * fix merge Signed-off-by: achmelo <a.chmelo@gmail.com> * sanitize values Signed-off-by: achmelo <a.chmelo@gmail.com> * change password method from jzos Signed-off-by: achmelo <a.chmelo@gmail.com> * fix styles Signed-off-by: achmelo <a.chmelo@gmail.com> * update usernamepassword token in unit tests Signed-off-by: achmelo <a.chmelo@gmail.com> * Add unit test Signed-off-by: at670475 <andrea.tabone@broadcom.com> * check source of password Signed-off-by: achmelo <a.chmelo@gmail.com> * Fix check in the MockPlatform and add unit tests Signed-off-by: at670475 <andrea.tabone@broadcom.com> * change password code coverage Signed-off-by: achmelo <a.chmelo@gmail.com> * styles Signed-off-by: achmelo <a.chmelo@gmail.com> * verify change password and loginrequest appearance in auth Signed-off-by: achmelo <a.chmelo@gmail.com> * Add unit test for ZosmfServiceTest Signed-off-by: at670475 <andrea.tabone@broadcom.com> * Remove unused import Signed-off-by: at670475 <andrea.tabone@broadcom.com> * address PR review comments Signed-off-by: achmelo <a.chmelo@gmail.com> Co-authored-by: Andrea Tabone <andrea.tabone@broadcom.com>
1 parent d29ed01 commit 3f3c2af

File tree

14 files changed

+356
-69
lines changed

14 files changed

+356
-69
lines changed

apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/LoginFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
8181
}
8282

8383
UsernamePasswordAuthenticationToken authentication
84-
= new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
84+
= new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest);
8585

8686
Authentication auth = null;
8787

apiml-security-common/src/main/java/org/zowe/apiml/security/common/login/LoginRequest.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,27 @@
99
*/
1010
package org.zowe.apiml.security.common.login;
1111

12-
import lombok.AllArgsConstructor;
1312
import lombok.Data;
1413
import lombok.NoArgsConstructor;
1514

1615
/**
1716
* Represents the login JSON with credentials
1817
*/
1918
@Data
20-
@AllArgsConstructor
2119
@NoArgsConstructor
2220
public class LoginRequest {
2321
private String username;
2422
private String password;
23+
private String newPassword;
24+
25+
public LoginRequest(String username, String password) {
26+
this.username = username;
27+
this.password = password;
28+
}
29+
30+
public LoginRequest(String username, String password, String newPassword) {
31+
this.username = username;
32+
this.password = password;
33+
this.newPassword = newPassword;
34+
}
2535
}

apiml-security-common/src/test/java/org/zowe/apiml/security/common/login/LoginFilterTest.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@
4646
@ExtendWith(MockitoExtension.class)
4747
class LoginFilterTest {
4848
private static final String VALID_JSON = "{\"username\": \"user\", \"password\": \"pwd\"}";
49+
private static final String JSON_WITH_NEW_PW = "{\"username\": \"user\", \"password\": \"pwd\", \"newPassword\": \"newPwd\"}";
4950
private static final String EMPTY_JSON = "{\"username\": \"\", \"password\": \"\"}";
5051
private static final String VALID_AUTH_HEADER = "Basic dXNlcjpwd2Q=";
5152
private static final String INVALID_AUTH_HEADER = "Basic dXNlcj11c2Vy";
53+
private static final String USER = "user";
54+
private static final String PASSWORD = "pwd";
55+
private static final String NEW_PASSWORD = "newPwd";
5256

5357
private MockHttpServletRequest httpServletRequest;
5458
private MockHttpServletResponse httpServletResponse;
@@ -87,7 +91,7 @@ void shouldCallAuthenticationManagerAuthenticateWithAuthHeader() throws ServletE
8791
loginFilter.attemptAuthentication(httpServletRequest, httpServletResponse);
8892

8993
UsernamePasswordAuthenticationToken authentication
90-
= new UsernamePasswordAuthenticationToken("user", "pwd");
94+
= new UsernamePasswordAuthenticationToken(USER, new LoginRequest(USER,PASSWORD));
9195
verify(authenticationManager).authenticate(authentication);
9296
}
9397

@@ -101,7 +105,21 @@ void shouldCallAuthenticationManagerAuthenticateWithJson() throws ServletExcepti
101105
loginFilter.attemptAuthentication(httpServletRequest, httpServletResponse);
102106

103107
UsernamePasswordAuthenticationToken authentication
104-
= new UsernamePasswordAuthenticationToken("user", "pwd");
108+
= new UsernamePasswordAuthenticationToken(USER, new LoginRequest(USER,PASSWORD));
109+
verify(authenticationManager).authenticate(authentication);
110+
}
111+
112+
@Test
113+
void shouldCallAuthenticationManagerAuthenticateWithNewPasswordInJson() throws ServletException {
114+
httpServletRequest = new MockHttpServletRequest();
115+
httpServletRequest.setMethod(HttpMethod.POST.name());
116+
httpServletRequest.setContent(JSON_WITH_NEW_PW.getBytes());
117+
httpServletResponse = new MockHttpServletResponse();
118+
119+
loginFilter.attemptAuthentication(httpServletRequest, httpServletResponse);
120+
121+
UsernamePasswordAuthenticationToken authentication
122+
= new UsernamePasswordAuthenticationToken(USER, new LoginRequest(USER,PASSWORD, NEW_PASSWORD));
105123
verify(authenticationManager).authenticate(authentication);
106124
}
107125

@@ -173,7 +191,7 @@ private void testFailWithResourceAccessError(RuntimeException exception, ErrorTy
173191
authenticationFailureHandler, objectMapper, authenticationManager,
174192
resourceAccessExceptionHandler);
175193

176-
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("user", "pwd");
194+
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(USER, new LoginRequest(USER,PASSWORD));
177195
when(authenticationManager.authenticate(authentication)).thenThrow(exception);
178196

179197
httpServletRequest = new MockHttpServletRequest();

gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/saf/MockPlatformUser.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ public PlatformReturned authenticate(String userid, String password) {
2727
}
2828
}
2929

30+
@Override
31+
public Object changePassword(String userid, String password, String newPassword) {
32+
if (userid.equalsIgnoreCase(VALID_USERID) && password.equalsIgnoreCase(VALID_PASSWORD) && !newPassword.equalsIgnoreCase(password)) {
33+
return null;
34+
} else {
35+
return PlatformReturned.builder().success(false).build();
36+
}
37+
}
3038
}

gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/saf/PlatformUser.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,11 @@ public interface PlatformUser {
1616
* If successful, a null object is returned. If not successful an instance of the PlatformReturned class is returned.
1717
*/
1818
Object authenticate(java.lang.String userid, java.lang.String password);
19+
20+
/**
21+
*If successful, a null object is returned. If NOT successful, an instance of the PlatformReturned class is returned
22+
* with the class variables errno, errno2 and errnoMsg set from the values returned by the OS/390 services __passwd,
23+
* strerror(errno), and __errno2().
24+
*/
25+
Object changePassword(java.lang.String userid, java.lang.String password, java.lang.String newPassword);
1926
}

gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/saf/SafPlatformUser.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,20 @@ public class SafPlatformUser implements PlatformUser {
2222
private final PlatformClassFactory platformClassFactory;
2323
private final PlatformReturnedHelper<Object> platformReturnedHelper;
2424
private final MethodHandle authenticateMethodHandle;
25+
private final MethodHandle changePasswordHandle;
2526

2627
public SafPlatformUser(PlatformClassFactory platformClassFactory)
27-
throws IllegalAccessException, ClassNotFoundException, NoSuchFieldException, NoSuchMethodException
28-
{
28+
throws IllegalAccessException, ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
2929
this.platformClassFactory = platformClassFactory;
3030
this.platformReturnedHelper = new PlatformReturnedHelper<>((Class<Object>) platformClassFactory.getPlatformReturnedClass());
3131

3232
Method method = platformClassFactory.getPlatformUserClass()
3333
.getMethod("authenticate", String.class, String.class);
3434
authenticateMethodHandle = MethodHandles.lookup().unreflect(method);
35+
36+
Method changeMethod = platformClassFactory.getPlatformUserClass()
37+
.getMethod("changePassword", String.class, String.class, String.class);
38+
changePasswordHandle = MethodHandles.lookup().unreflect(changeMethod);
3539
}
3640

3741
@Override
@@ -44,4 +48,14 @@ public PlatformReturned authenticate(String userid, String password) {
4448
}
4549
}
4650

51+
@Override
52+
public PlatformReturned changePassword(String userid, String password, String newPassword) {
53+
try {
54+
Object safReturned = changePasswordHandle.invokeWithArguments(platformClassFactory.getPlatformUser(),
55+
userid, password, newPassword);
56+
return platformReturnedHelper.convert(safReturned);
57+
} catch (Throwable throwable) {
58+
throw new AuthenticationServiceException("Error occurred while changing password.", throwable);
59+
}
60+
}
4761
}

gateway-service/src/main/java/org/zowe/apiml/gateway/security/login/saf/ZosAuthenticationProvider.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.zowe.apiml.gateway.security.login.saf;
1212

1313
import lombok.extern.slf4j.Slf4j;
14+
import org.apache.commons.lang3.StringUtils;
1415
import org.springframework.beans.factory.InitializingBean;
1516
import org.springframework.beans.factory.annotation.Value;
1617
import org.springframework.security.authentication.AuthenticationProvider;
@@ -21,6 +22,7 @@
2122
import org.zowe.apiml.gateway.security.login.LoginProvider;
2223
import org.zowe.apiml.gateway.security.service.AuthenticationService;
2324
import org.zowe.apiml.security.common.auth.saf.PlatformReturned;
25+
import org.zowe.apiml.security.common.login.LoginRequest;
2426

2527
@Component
2628
@Slf4j
@@ -39,8 +41,13 @@ public ZosAuthenticationProvider(AuthenticationService authenticationService,
3941
@Override
4042
public Authentication authenticate(Authentication authentication) {
4143
String userid = authentication.getName();
42-
String password = authentication.getCredentials().toString();
43-
PlatformReturned returned = (PlatformReturned) getPlatformUser().authenticate(userid, password);
44+
LoginRequest credentials = (LoginRequest) authentication.getCredentials();
45+
PlatformReturned returned;
46+
if (StringUtils.isNotEmpty(credentials.getNewPassword())) {
47+
returned = (PlatformReturned) getPlatformUser().changePassword(userid, credentials.getPassword(), credentials.getNewPassword());
48+
} else {
49+
returned = (PlatformReturned) getPlatformUser().authenticate(userid, credentials.getPassword());
50+
}
4451

4552
if ((returned == null) || (returned.isSuccess())) {
4653
final String domain = "security-domain";

gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/zosmf/AbstractZosmfService.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,21 @@
1616
import org.springframework.security.authentication.AuthenticationServiceException;
1717
import org.springframework.security.authentication.BadCredentialsException;
1818
import org.springframework.security.core.Authentication;
19-
import org.springframework.web.client.*;
19+
import org.springframework.web.client.HttpClientErrorException;
20+
import org.springframework.web.client.ResourceAccessException;
21+
import org.springframework.web.client.RestClientException;
22+
import org.springframework.web.client.RestTemplate;
2023
import org.zowe.apiml.message.log.ApimlLogger;
2124
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
2225
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
2326
import org.zowe.apiml.security.common.error.ServiceNotAccessibleException;
27+
import org.zowe.apiml.security.common.login.LoginRequest;
2428
import org.zowe.apiml.util.EurekaUtils;
2529

26-
import java.util.*;
30+
import java.util.Base64;
31+
import java.util.List;
32+
import java.util.Objects;
33+
import java.util.Optional;
2734
import java.util.function.Supplier;
2835

2936
@Slf4j
@@ -63,13 +70,19 @@ protected String getZosmfServiceId() {
6370

6471
/**
6572
* Methods construct the value of authentication header by credentials
73+
*
6674
* @param authentication credentials to generates header value
6775
* @return prepared header value (see header Authentication)
6876
*/
6977
protected String getAuthenticationValue(Authentication authentication) {
7078
final String user = authentication.getPrincipal().toString();
71-
final String password = authentication.getCredentials().toString();
72-
79+
final String password;
80+
if (authentication.getCredentials() instanceof LoginRequest) {
81+
LoginRequest loginRequest = (LoginRequest) authentication.getCredentials();
82+
password = loginRequest.getPassword();
83+
} else {
84+
password = (String) authentication.getCredentials();
85+
}
7386
final String credentials = user + ":" + password;
7487
return "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes());
7588
}
@@ -101,7 +114,7 @@ protected String getURI(String zosmf) {
101114
* custom one with better messages and types for subsequent treatment.
102115
*
103116
* @param url URL of invoked REST endpoint
104-
* @param re original exception
117+
* @param re original exception
105118
* @return translated exception
106119
*/
107120
protected RuntimeException handleExceptionOnCall(String url, RuntimeException re) {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.gateway.security.login.saf;
12+
13+
import org.junit.jupiter.api.BeforeAll;
14+
import org.junit.jupiter.api.Nested;
15+
import org.junit.jupiter.api.Test;
16+
import org.zowe.apiml.security.common.auth.saf.PlatformReturned;
17+
18+
import static org.junit.jupiter.api.Assertions.*;
19+
20+
class MockPlatformUserTest {
21+
22+
private static MockPlatformUser mockPlatformUser;
23+
@BeforeAll
24+
static void setup() {
25+
mockPlatformUser = new MockPlatformUser();
26+
}
27+
28+
@Nested
29+
class WhenAuthenticate {
30+
@Nested
31+
class Success {
32+
@Test
33+
void givenValidCredentials_whenAuthenticate_thenReturnNull() {
34+
assertEquals(null, mockPlatformUser.authenticate("USER", "validPassword"));
35+
}
36+
}
37+
38+
@Nested
39+
class Fails {
40+
@Test
41+
void givenInValidCredentials_whenAuthenticate_thenReturnPlatformReturnedSuccessFalse() {
42+
PlatformReturned platformReturned = PlatformReturned.builder().success(false).build();
43+
assertEquals(platformReturned, mockPlatformUser.authenticate("USER", "invalidPassword"));
44+
}
45+
}
46+
47+
}
48+
49+
@Nested
50+
class WhenChangingPassword {
51+
@Nested
52+
class Success {
53+
@Test
54+
void givenValidCredentialsAndNewValidPassword_whenChangePassword_thenReturnNull() {
55+
assertNull( mockPlatformUser.changePassword("USER", "validPassword", "newPassword"));
56+
}
57+
}
58+
59+
@Nested
60+
class Fails {
61+
@Test
62+
void givenInvalidCredentialsAndNewValidPassword_whenChangePassword_thenReturnPlatformReturnedSuccessFalse() {
63+
PlatformReturned platformReturned = PlatformReturned.builder().success(false).build();
64+
assertEquals(platformReturned, mockPlatformUser.changePassword("InvalidUser", "validPassword", "newPassword"));
65+
}
66+
67+
@Test
68+
void givenValidCredentialsAndInvalidNewPassword_whenChangePassword_thenReturnPlatformReturnedSuccessFalse() {
69+
PlatformReturned platformReturned = PlatformReturned.builder().success(false).build();
70+
assertEquals(platformReturned, mockPlatformUser.changePassword("USER", "validPassword", "validPassword"));
71+
}
72+
}
73+
}
74+
75+
76+
77+
78+
79+
80+
81+
}

gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/saf/SafPlatformUserTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,16 @@ void returnsDetailsForInvalidAuthentication() {
3939
assertFalse(returned.isSuccess());
4040
}
4141

42+
@Test
43+
void returnsNullForChangePassword() {
44+
PlatformReturned returned = safPlatformUser.changePassword(VALID_USERID, VALID_PASSWORD, "newPassword" );
45+
assertNull(returned);
46+
}
47+
48+
@Test
49+
void whenNewPasswordEqualsOld_thenReturnsNotSuccess() {
50+
PlatformReturned returned = safPlatformUser.changePassword(VALID_USERID, VALID_PASSWORD, VALID_PASSWORD );
51+
assertFalse(returned.isSuccess());
52+
}
53+
4254
}

0 commit comments

Comments
 (0)