Copilot commented on code in PR #13953:
URL: https://github.com/apache/cloudstack/pull/13953#discussion_r4322841388


##########
plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java:
##########
@@ -446,26 +455,52 @@ public boolean isUserAuthorized(Long userId, String 
entityId) {
     @Override
     public boolean authorizeUser(Long userId, String entityId, boolean enable) 
{
         UserVO user = _userDao.getUser(userId);
-        if (user != null) {
-            if (enable) {
-                user.setExternalEntity(entityId);
-                user.setSource(User.Source.SAML2);
-            } else {
-                boolean enableLoginAfterSAMLDisable =  
SAML2AuthManager.EnableLoginAfterSAMLDisable.value();
-                if (user.getSource().equals(User.Source.SAML2)) {
-                    if(enableLoginAfterSAMLDisable) {
-                        user.setSource(User.Source.UNKNOWN);
-                    } else {
-                        user.setSource(User.Source.SAML2DISABLED);
-                    }
-                } else {
-                    return false;
-                }
+        if (user == null) {
+            return false;
+        }
+        if (enable) {
+            enableSAMLForUser(user, entityId);
+        } else if (!disableSAMLForUser(user)) {
+            return false;
+        }
+        _userDao.update(user.getId(), user);
+        return true;
+    }
+
+    private void enableSAMLForUser(UserVO user, String entityId) {
+        if (user.getSource() != null && 
!User.Source.SAML2.equals(user.getSource()) && 
!User.Source.SAML2DISABLED.equals(user.getSource())) {
+            userDetailsDao.addDetail(user.getId(), PRE_SAML_SOURCE_DETAIL_KEY, 
user.getSource().toString(), false);
+        }
+        user.setExternalEntity(entityId);
+        user.setSource(User.Source.SAML2);
+    }
+
+    private boolean disableSAMLForUser(UserVO user) {
+        if (!user.getSource().equals(User.Source.SAML2)) {
+            return false;
+        }

Review Comment:
   `user.getSource().equals(...)` will throw a `NullPointerException` if the 
user's source is null (your tests already indicate null sources are possible). 
Use a null-safe comparison (e.g., `User.Source.SAML2.equals(user.getSource())`) 
and treat null as "not currently SAML" (return false) to keep behavior 
consistent and avoid NPEs.



##########
plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java:
##########
@@ -446,26 +455,52 @@ public boolean isUserAuthorized(Long userId, String 
entityId) {
     @Override
     public boolean authorizeUser(Long userId, String entityId, boolean enable) 
{
         UserVO user = _userDao.getUser(userId);
-        if (user != null) {
-            if (enable) {
-                user.setExternalEntity(entityId);
-                user.setSource(User.Source.SAML2);
-            } else {
-                boolean enableLoginAfterSAMLDisable =  
SAML2AuthManager.EnableLoginAfterSAMLDisable.value();
-                if (user.getSource().equals(User.Source.SAML2)) {
-                    if(enableLoginAfterSAMLDisable) {
-                        user.setSource(User.Source.UNKNOWN);
-                    } else {
-                        user.setSource(User.Source.SAML2DISABLED);
-                    }
-                } else {
-                    return false;
-                }
+        if (user == null) {
+            return false;
+        }
+        if (enable) {
+            enableSAMLForUser(user, entityId);
+        } else if (!disableSAMLForUser(user)) {
+            return false;
+        }
+        _userDao.update(user.getId(), user);
+        return true;
+    }
+
+    private void enableSAMLForUser(UserVO user, String entityId) {
+        if (user.getSource() != null && 
!User.Source.SAML2.equals(user.getSource()) && 
!User.Source.SAML2DISABLED.equals(user.getSource())) {
+            userDetailsDao.addDetail(user.getId(), PRE_SAML_SOURCE_DETAIL_KEY, 
user.getSource().toString(), false);
+        }

Review Comment:
   The value is persisted using `user.getSource().toString()` but later parsed 
with `User.Source.valueOf(...)`, which expects the enum constant name. Persist 
`user.getSource().name()` instead to guarantee round-tripping even if 
`toString()` is overridden or changed in the future.



##########
plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAML2AuthManagerImplTest.java:
##########
@@ -80,6 +97,39 @@ public void setUp() throws NoSuchFieldException, 
IllegalAccessException {
         Mockito.doReturn(true).when(saml2AuthManager).isSAMLPluginEnabled();
     }
 
+    @After
+    public void tearDown() {
+        resetConfigKeyValue(SAML2AuthManager.EnableLoginAfterSAMLDisable);
+    }
+
+    private static <T> void setConfigKeyValue(ConfigKey<T> configKey, T value) 
{
+        try {
+            Field valueField = ConfigKey.class.getDeclaredField("_value");
+            valueField.setAccessible(true);
+            valueField.set(configKey, value);
+
+            Field dynamicField = 
ConfigKey.class.getDeclaredField("_isDynamic");
+            dynamicField.setAccessible(true);
+            dynamicField.setBoolean(configKey, false);
+        } catch (IllegalAccessException | NoSuchFieldException e) {
+            throw new RuntimeException("Failed to set ConfigKey value", e);
+        }
+    }
+
+    private static void resetConfigKeyValue(ConfigKey<?> configKey) {
+        try {
+            Field valueField = ConfigKey.class.getDeclaredField("_value");
+            valueField.setAccessible(true);
+            valueField.set(configKey, null);
+
+            Field dynamicField = 
ConfigKey.class.getDeclaredField("_isDynamic");
+            dynamicField.setAccessible(true);
+            dynamicField.setBoolean(configKey, true);
+        } catch (IllegalAccessException | NoSuchFieldException e) {
+            throw new RuntimeException("Failed to reset ConfigKey value", e);
+        }
+    }

Review Comment:
   These helpers mutate global static `ConfigKey` internals and “reset” by 
forcing `_value=null` and `_isDynamic=true`, which may not restore the previous 
state if something else set it earlier in the same JVM. Capturing the original 
`_value` and `_isDynamic` before mutation and restoring those exact values in 
`tearDown()` will make the tests less order-dependent and reduce cross-test 
interference.



##########
plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java:
##########
@@ -115,6 +121,9 @@ public String getSAMLIdentityProviderMetadataURL(){
     @Inject
     private UserDao _userDao;
 
+    @Inject
+    private UserDetailsDao userDetailsDao;

Review Comment:
   Field naming is inconsistent with the surrounding injected DAOs (e.g., 
`_userDao`, `_domainMgr`). Renaming to match the established convention (e.g., 
`_userDetailsDao`) would improve consistency and reduce cognitive overhead when 
scanning the class.



##########
plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAML2AuthManagerImplTest.java:
##########
@@ -117,7 +167,142 @@ public void testAuthorizeUser() {
         Mockito.verify(userDao, 
Mockito.atLeastOnce()).update(Mockito.anyLong(), Mockito.any(user.getClass()));
     }
 
+    @Test
+    public void testAuthorizeUserStoresPreSamlSourceOnEnable() {
+        UserVO user = new UserVO(200L);
+        user.setUsername("someuser");
+        user.setSource(User.Source.LDAP);
+        when(userDao.getUser(Mockito.anyLong())).thenReturn(user);
+
+        saml2AuthManager.authorizeUser(200L, "someID", true);
+
+        verify(userDetailsDao).addDetail(200L, "PreSamlSource", "LDAP", false);
+        assertEquals(User.Source.SAML2, user.getSource());
+    }
+
+    @Test
+    public void 
testAuthorizeUserDoesNotRestorePreSamlSourceWhenAlreadyAuthorized() {

Review Comment:
   This test calls `authorizeUser(..., enable=true)` and asserts that no detail 
is added; it’s not restoring anything. Consider renaming to reflect what it 
verifies (e.g., “DoesNotStorePreSamlSourceWhenAlreadyAuthorized/AlreadySAML”) 
to avoid confusion for future readers.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to