diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java index cc154ed964b3..f9d218dca2ee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmd.java @@ -37,6 +37,7 @@ import com.cloud.user.Account; import com.cloud.user.UserAccount; +import com.cloud.utils.PasswordGenerator; @APICommand(name = "createAccount", description = "Creates an account", responseObject = AccountResponse.class, entityType = {Account.class}, @@ -75,8 +76,8 @@ public class CreateAccountCmd extends BaseCmd { @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, - required = true, - description = "Clear text password (Default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.") + description = "Clear text password (Default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section. " + + "If omitted, a random password is generated, e.g. for an account that will only ever authenticate externally via SAML/LDAP.") private String password; @Parameter(name = ApiConstants.TIMEZONE, @@ -191,10 +192,13 @@ public void execute() { /** * TODO: this should be done through a validator. for now replicating the validation logic in create account and user + * + *

A blank password generates a random one instead of failing, since an account that will + * only ever authenticate externally (SAML/LDAP) has no need for the admin to set one. */ private void validateParams() { - if(StringUtils.isEmpty(getPassword())) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Empty passwords are not allowed"); + if (StringUtils.isEmpty(getPassword())) { + password = PasswordGenerator.generateRandomPassword(12); } if (getAccountType() == null && (getRoleId() == null || getRoleId() < 1L)) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Neither account type and role ID are not provided"); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java index 365646de7a33..b8aa857d87d9 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/account/CreateAccountCmdTest.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.account; import org.apache.cloudstack.acl.RoleService; -import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.context.CallContext; import org.apache.logging.log4j.Logger; @@ -81,28 +80,26 @@ public void testExecuteWithNotBlankPassword() { } @Test - public void testExecuteWithNullPassword() { + public void testExecuteWithNullPasswordGeneratesOne() { ReflectionTestUtils.setField(createAccountCmd, "password", null); try { createAccountCmd.execute(); - Assert.fail("should throw exception for a null password"); } catch (ServerApiException e) { - Assert.assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode()); - Assert.assertEquals("Empty passwords are not allowed", e.getMessage()); + Assert.assertTrue("Received exception as the mock accountService createUserAccount returns null user", true); } - Mockito.verify(accountService, Mockito.never()).createUserAccount(createAccountCmd); + Assert.assertNotNull("a password should be generated for accounts that authenticate externally", createAccountCmd.getPassword()); + Mockito.verify(accountService, Mockito.times(1)).createUserAccount(createAccountCmd); } @Test - public void testExecuteWithEmptyPassword() { + public void testExecuteWithEmptyPasswordGeneratesOne() { ReflectionTestUtils.setField(createAccountCmd, "password", ""); try { createAccountCmd.execute(); - Assert.fail("should throw exception for a empty password"); } catch (ServerApiException e) { - Assert.assertEquals(ApiErrorCode.PARAM_ERROR, e.getErrorCode()); - Assert.assertEquals("Empty passwords are not allowed", e.getMessage()); + Assert.assertTrue("Received exception as the mock accountService createUserAccount returns null user", true); } - Mockito.verify(accountService, Mockito.never()).createUserAccount(createAccountCmd); + Assert.assertNotNull("a password should be generated for accounts that authenticate externally", createAccountCmd.getPassword()); + Mockito.verify(accountService, Mockito.times(1)).createUserAccount(createAccountCmd); } } diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index f57460efa482..3be5b7d280b2 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -3982,6 +3982,7 @@ "message.restart.vpc": "Please confirm that you want to restart the VPC.", "message.restart.vpc.remark": "Please confirm that you want to restart the VPC

Remark: making a non-redundant VPC redundant will force a clean up. The Networks will not be available for a couple of minutes.

", "message.running.custom.action": "Running action", +"message.saml.account.no.password": "This account will authenticate via SAML SSO, so no password is needed — one will be generated automatically.", "message.scale.processing": "Scale in progress", "message.scaledown.policies": "Please add at least a ScaleDown policy. The AutoScale Group will be scaled down when all conditions in a ScaleDown policy are matched. ScaleDown policies will be checked after ScaleUp policies.", "message.scaledown.policy.continue": "Please add at least condition to ScaleDown policy to continue", diff --git a/ui/src/views/iam/AddAccount.vue b/ui/src/views/iam/AddAccount.vue index a211fd2d8c3d..f95836362d43 100644 --- a/ui/src/views/iam/AddAccount.vue +++ b/ui/src/views/iam/AddAccount.vue @@ -53,7 +53,7 @@ v-model:value="form.username" :placeholder="apiParams.username.description" /> - +