mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Disable API Key Access for users, accounts and domains (#9741)
* cli changes to update user/account, list by apikeyaccess, domain level setting * UI changes for updating user/account and searchfilter in listview * make the api parameters and setting accessible only to root admin * revert changes to ui/package-lock.json * minor changes to description strings * UT for ApiServer and AccountManagerImpl classes * fix pre-commit failure * Added a constant for the string System * UT for searchForUsers and searchForAccounts * Fix marvin test error * Update schema to use idempotent add column * Fix `updateTemplatePermission` when the UI is set to a language other than English (#9766) * Fix updateTemplatePermission UI in non-english language * Improve fix --------- Co-authored-by: Lucas Martins <lucas.martins@scclouds.com.br> * Added user name uuid to logging * Add events when api key access is changed via api or config setting * fix the userid for api key access update event * Fix ut failure after event logging * Convert drop down to radio-button in edit user and account * Add ApiKeyAccess status in User InfoCard for Users if Api key is generated * Return apiKeyAccess in user and account response only for Root Admin * fixed noredist build failure * Show apikeyaccess on the left panel in the user view for root admins as well * don't show divider if apiKeyAccess is not shown to user * Fix events generated to set Username, Account and Domain of the caller correctly * cli changes to update user/account, list by apikeyaccess, domain level setting * UI changes for updating user/account and searchfilter in listview * make the api parameters and setting accessible only to root admin * revert changes to ui/package-lock.json * minor changes to description strings * UT for ApiServer and AccountManagerImpl classes * fix pre-commit failure * Added a constant for the string System * UT for searchForUsers and searchForAccounts * Fix marvin test error * Update schema to use idempotent add column * Added user name uuid to logging * Add events when api key access is changed via api or config setting * fix the userid for api key access update event * Fix ut failure after event logging * Convert drop down to radio-button in edit user and account * Add ApiKeyAccess status in User InfoCard for Users if Api key is generated * Return apiKeyAccess in user and account response only for Root Admin * fixed noredist build failure * Show apikeyaccess on the left panel in the user view for root admins as well * don't show divider if apiKeyAccess is not shown to user * Fix events generated to set Username, Account and Domain of the caller correctly * Added DB upgrade path from 42000 to 42010 --------- Co-authored-by: Daan Hoogland <daan@onecht.net> Co-authored-by: Lucas Martins <56271185+lucas-a-martins@users.noreply.github.com> Co-authored-by: Lucas Martins <lucas.martins@scclouds.com.br>
This commit is contained in:
parent
58138f2da3
commit
d17de834a5
@ -292,6 +292,7 @@ public class EventTypes {
|
|||||||
|
|
||||||
//register for user API and secret keys
|
//register for user API and secret keys
|
||||||
public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY";
|
public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY";
|
||||||
|
public static final String API_KEY_ACCESS_UPDATE = "API.KEY.ACCESS.UPDATE";
|
||||||
|
|
||||||
// Template Events
|
// Template Events
|
||||||
public static final String EVENT_TEMPLATE_CREATE = "TEMPLATE.CREATE";
|
public static final String EVENT_TEMPLATE_CREATE = "TEMPLATE.CREATE";
|
||||||
|
|||||||
@ -93,4 +93,8 @@ public interface Account extends ControlledEntity, InternalIdentity, Identity {
|
|||||||
|
|
||||||
boolean isDefault();
|
boolean isDefault();
|
||||||
|
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess);
|
||||||
|
|
||||||
|
public Boolean getApiKeyAccess();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package com.cloud.user;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.RoleType;
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
@ -127,9 +128,9 @@ public interface AccountService {
|
|||||||
*/
|
*/
|
||||||
UserAccount getUserAccountById(Long userId);
|
UserAccount getUserAccountById(Long userId);
|
||||||
|
|
||||||
public Map<String, String> getKeys(GetUserKeysCmd cmd);
|
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd);
|
||||||
|
|
||||||
public Map<String, String> getKeys(Long userId);
|
public Pair<Boolean, Map<String, String>> getKeys(Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists user two-factor authentication provider plugins
|
* Lists user two-factor authentication provider plugins
|
||||||
|
|||||||
@ -94,4 +94,9 @@ public interface User extends OwnedBy, InternalIdentity {
|
|||||||
public boolean isUser2faEnabled();
|
public boolean isUser2faEnabled();
|
||||||
|
|
||||||
public String getKeyFor2fa();
|
public String getKeyFor2fa();
|
||||||
|
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess);
|
||||||
|
|
||||||
|
public Boolean getApiKeyAccess();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ public class ApiConstants {
|
|||||||
public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm";
|
public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm";
|
||||||
public static final String ANNOTATION = "annotation";
|
public static final String ANNOTATION = "annotation";
|
||||||
public static final String API_KEY = "apikey";
|
public static final String API_KEY = "apikey";
|
||||||
|
public static final String API_KEY_ACCESS = "apikeyaccess";
|
||||||
public static final String ARCHIVED = "archived";
|
public static final String ARCHIVED = "archived";
|
||||||
public static final String ARCH = "arch";
|
public static final String ARCH = "arch";
|
||||||
public static final String AS_NUMBER = "asnumber";
|
public static final String AS_NUMBER = "asnumber";
|
||||||
@ -1247,4 +1248,30 @@ public class ApiConstants {
|
|||||||
public enum DomainDetails {
|
public enum DomainDetails {
|
||||||
all, resource, min;
|
all, resource, min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ApiKeyAccess {
|
||||||
|
DISABLED(false),
|
||||||
|
ENABLED(true),
|
||||||
|
INHERIT(null);
|
||||||
|
|
||||||
|
Boolean apiKeyAccess;
|
||||||
|
|
||||||
|
ApiKeyAccess(Boolean keyAccess) {
|
||||||
|
apiKeyAccess = keyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean toBoolean() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiKeyAccess fromBoolean(Boolean value) {
|
||||||
|
if (value == null) {
|
||||||
|
return INHERIT;
|
||||||
|
} else if (value) {
|
||||||
|
return ENABLED;
|
||||||
|
} else {
|
||||||
|
return DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,9 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
import org.apache.cloudstack.api.response.RoleResponse;
|
import org.apache.cloudstack.api.response.RoleResponse;
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
@ -40,8 +42,8 @@ import org.apache.cloudstack.region.RegionService;
|
|||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
@APICommand(name = "updateAccount", description = "Updates account information for the authenticated user", responseObject = AccountResponse.class, entityType = {Account.class},
|
@APICommand(name = "updateAccount", description = "Updates account information for the authenticated user", responseObject = AccountResponse.class, entityType = {Account.class},
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
||||||
public class UpdateAccountCmd extends BaseCmd {
|
public class UpdateAccountCmd extends BaseCmd implements UserCmd {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
//////////////// API parameters /////////////////////
|
//////////////// API parameters /////////////////////
|
||||||
@ -70,6 +72,9 @@ public class UpdateAccountCmd extends BaseCmd {
|
|||||||
@Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for the account used to store specific parameters")
|
@Parameter(name = ApiConstants.ACCOUNT_DETAILS, type = CommandType.MAP, description = "Details for the account used to store specific parameters")
|
||||||
private Map details;
|
private Map details;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "Determines if Api key access for this user is enabled, disabled or inherits the value from its parent, the domain level setting api.key.access", since = "4.20.1.0", authorized = {RoleType.Admin})
|
||||||
|
private String apiKeyAccess;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RegionService _regionService;
|
RegionService _regionService;
|
||||||
|
|
||||||
@ -109,6 +114,10 @@ public class UpdateAccountCmd extends BaseCmd {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////// API Implementation///////////////////
|
/////////////// API Implementation///////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -131,7 +140,7 @@ public class UpdateAccountCmd extends BaseCmd {
|
|||||||
public void execute() {
|
public void execute() {
|
||||||
Account result = _regionService.updateAccount(this);
|
Account result = _regionService.updateAccount(this);
|
||||||
if (result != null){
|
if (result != null){
|
||||||
AccountResponse response = _responseGenerator.createAccountResponse(ResponseView.Full, result);
|
AccountResponse response = _responseGenerator.createAccountResponse(getResponseView(), result);
|
||||||
response.setResponseName(getCommandName());
|
response.setResponseName(getCommandName());
|
||||||
setResponseObject(response);
|
setResponseObject(response);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package org.apache.cloudstack.api.command.admin.user;
|
|||||||
|
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
import org.apache.cloudstack.acl.RoleType;
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
@ -54,11 +55,13 @@ public class GetUserKeysCmd extends BaseCmd{
|
|||||||
else return Account.ACCOUNT_ID_SYSTEM;
|
else return Account.ACCOUNT_ID_SYSTEM;
|
||||||
}
|
}
|
||||||
public void execute(){
|
public void execute(){
|
||||||
Map<String, String> keys = _accountService.getKeys(this);
|
Pair<Boolean, Map<String, String>> keys = _accountService.getKeys(this);
|
||||||
|
|
||||||
RegisterResponse response = new RegisterResponse();
|
RegisterResponse response = new RegisterResponse();
|
||||||
if(keys != null){
|
if(keys != null){
|
||||||
response.setApiKey(keys.get("apikey"));
|
response.setApiKeyAccess(keys.first());
|
||||||
response.setSecretKey(keys.get("secretkey"));
|
response.setApiKey(keys.second().get("apikey"));
|
||||||
|
response.setSecretKey(keys.second().get("secretkey"));
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setObjectName("userkeys");
|
response.setObjectName("userkeys");
|
||||||
|
|||||||
@ -19,20 +19,23 @@ package org.apache.cloudstack.api.command.admin.user;
|
|||||||
import com.cloud.server.ResourceIcon;
|
import com.cloud.server.ResourceIcon;
|
||||||
import com.cloud.server.ResourceTag;
|
import com.cloud.server.ResourceTag;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseListAccountResourcesCmd;
|
import org.apache.cloudstack.api.BaseListAccountResourcesCmd;
|
||||||
import org.apache.cloudstack.api.Parameter;
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
import org.apache.cloudstack.api.response.UserResponse;
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@APICommand(name = "listUsers", description = "Lists user accounts", responseObject = UserResponse.class,
|
@APICommand(name = "listUsers", description = "Lists user accounts", responseObject = UserResponse.class,
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
||||||
public class ListUsersCmd extends BaseListAccountResourcesCmd {
|
public class ListUsersCmd extends BaseListAccountResourcesCmd implements UserCmd {
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -53,6 +56,9 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd {
|
|||||||
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "List user by the username")
|
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "List user by the username")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "List users by the Api key access value", since = "4.20.1.0", authorized = {RoleType.Admin})
|
||||||
|
private String apiKeyAccess;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN,
|
@Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN,
|
||||||
description = "flag to display the resource icon for users")
|
description = "flag to display the resource icon for users")
|
||||||
private Boolean showIcon;
|
private Boolean showIcon;
|
||||||
@ -77,6 +83,10 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getShowIcon() {
|
public Boolean getShowIcon() {
|
||||||
return showIcon != null ? showIcon : false;
|
return showIcon != null ? showIcon : false;
|
||||||
}
|
}
|
||||||
@ -87,7 +97,7 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
ListResponse<UserResponse> response = _queryService.searchForUsers(this);
|
ListResponse<UserResponse> response = _queryService.searchForUsers(getResponseView(), this);
|
||||||
response.setResponseName(getCommandName());
|
response.setResponseName(getCommandName());
|
||||||
this.setResponseObject(response);
|
this.setResponseObject(response);
|
||||||
if (response != null && response.getCount() > 0 && getShowIcon()) {
|
if (response != null && response.getCount() > 0 && getShowIcon()) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.admin.user;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
@ -69,6 +70,9 @@ public class UpdateUserCmd extends BaseCmd {
|
|||||||
@Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey")
|
@Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey")
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "Determines if Api key access for this user is enabled, disabled or inherits the value from its parent, the owning account", since = "4.20.1.0", authorized = {RoleType.Admin})
|
||||||
|
private String apiKeyAccess;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.TIMEZONE,
|
@Parameter(name = ApiConstants.TIMEZONE,
|
||||||
type = CommandType.STRING,
|
type = CommandType.STRING,
|
||||||
description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.")
|
description = "Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.")
|
||||||
@ -120,6 +124,10 @@ public class UpdateUserCmd extends BaseCmd {
|
|||||||
return secretKey;
|
return secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTimezone() {
|
public String getTimezone() {
|
||||||
return timezone;
|
return timezone;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
@ -70,6 +71,9 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC
|
|||||||
description = "comma separated list of account details requested, value can be a list of [ all, resource, min]")
|
description = "comma separated list of account details requested, value can be a list of [ all, resource, min]")
|
||||||
private List<String> viewDetails;
|
private List<String> viewDetails;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "List accounts by the Api key access value", since = "4.20.1.0", authorized = {RoleType.Admin})
|
||||||
|
private String apiKeyAccess;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN,
|
@Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN,
|
||||||
description = "flag to display the resource icon for accounts")
|
description = "flag to display the resource icon for accounts")
|
||||||
private Boolean showIcon;
|
private Boolean showIcon;
|
||||||
@ -120,6 +124,10 @@ public class ListAccountsCmd extends BaseListDomainResourcesCmd implements UserC
|
|||||||
return dv;
|
return dv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getShowIcon() {
|
public boolean getShowIcon() {
|
||||||
return showIcon != null ? showIcon : false;
|
return showIcon != null ? showIcon : false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -271,6 +271,10 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou
|
|||||||
@Param(description = "The tagged resource limit and count for the account", since = "4.20.0")
|
@Param(description = "The tagged resource limit and count for the account", since = "4.20.0")
|
||||||
List<TaggedResourceLimitAndCountResponse> taggedResources;
|
List<TaggedResourceLimitAndCountResponse> taggedResources;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.API_KEY_ACCESS)
|
||||||
|
@Param(description = "whether api key access is Enabled, Disabled or set to Inherit (it inherits the value from the parent)", since = "4.20.1.0")
|
||||||
|
ApiConstants.ApiKeyAccess apiKeyAccess;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getObjectId() {
|
public String getObjectId() {
|
||||||
return id;
|
return id;
|
||||||
@ -554,4 +558,8 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou
|
|||||||
public void setTaggedResourceLimitsAndCounts(List<TaggedResourceLimitAndCountResponse> taggedResourceLimitsAndCounts) {
|
public void setTaggedResourceLimitsAndCounts(List<TaggedResourceLimitAndCountResponse> taggedResourceLimitsAndCounts) {
|
||||||
this.taggedResources = taggedResourceLimitsAndCounts;
|
this.taggedResources = taggedResourceLimitsAndCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess) {
|
||||||
|
this.apiKeyAccess = ApiConstants.ApiKeyAccess.fromBoolean(apiKeyAccess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,19 +18,24 @@ package org.apache.cloudstack.api.response;
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
import com.cloud.serializer.Param;
|
import com.cloud.serializer.Param;
|
||||||
|
|
||||||
public class RegisterResponse extends BaseResponse {
|
public class RegisterResponse extends BaseResponse {
|
||||||
@SerializedName("apikey")
|
@SerializedName(ApiConstants.API_KEY)
|
||||||
@Param(description = "the api key of the registered user", isSensitive = true)
|
@Param(description = "the api key of the registered user", isSensitive = true)
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
|
|
||||||
@SerializedName("secretkey")
|
@SerializedName(ApiConstants.SECRET_KEY)
|
||||||
@Param(description = "the secret key of the registered user", isSensitive = true)
|
@Param(description = "the secret key of the registered user", isSensitive = true)
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.API_KEY_ACCESS)
|
||||||
|
@Param(description = "whether api key access is allowed or not", isSensitive = true)
|
||||||
|
private Boolean apiKeyAccess;
|
||||||
|
|
||||||
public String getApiKey() {
|
public String getApiKey() {
|
||||||
return apiKey;
|
return apiKey;
|
||||||
}
|
}
|
||||||
@ -46,4 +51,8 @@ public class RegisterResponse extends BaseResponse {
|
|||||||
public void setSecretKey(String secretKey) {
|
public void setSecretKey(String secretKey) {
|
||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess) {
|
||||||
|
this.apiKeyAccess = apiKeyAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,6 +128,10 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
|
|||||||
@Param(description = "true if user has two factor authentication is mandated", since = "4.18.0.0")
|
@Param(description = "true if user has two factor authentication is mandated", since = "4.18.0.0")
|
||||||
private Boolean is2FAmandated;
|
private Boolean is2FAmandated;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.API_KEY_ACCESS)
|
||||||
|
@Param(description = "whether api key access is Enabled, Disabled or set to Inherit (it inherits the value from the parent)", since = "4.20.1.0")
|
||||||
|
ApiConstants.ApiKeyAccess apiKeyAccess;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getObjectId() {
|
public String getObjectId() {
|
||||||
return this.getId();
|
return this.getId();
|
||||||
@ -309,4 +313,8 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
|
|||||||
public void set2FAmandated(Boolean is2FAmandated) {
|
public void set2FAmandated(Boolean is2FAmandated) {
|
||||||
this.is2FAmandated = is2FAmandated;
|
this.is2FAmandated = is2FAmandated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess) {
|
||||||
|
this.apiKeyAccess = ApiConstants.ApiKeyAccess.fromBoolean(apiKeyAccess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package org.apache.cloudstack.query;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd;
|
import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.host.ListHostTagsCmd;
|
import org.apache.cloudstack.api.command.admin.host.ListHostTagsCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
|
import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
|
||||||
@ -130,7 +131,7 @@ public interface QueryService {
|
|||||||
ConfigKey<Boolean> ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true",
|
ConfigKey<Boolean> ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true",
|
||||||
"Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global);
|
"Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
ListResponse<UserResponse> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException;
|
ListResponse<UserResponse> searchForUsers(ResponseObject.ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException;
|
||||||
|
|
||||||
ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException;
|
ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException;
|
||||||
|
|
||||||
|
|||||||
@ -88,6 +88,7 @@ import com.cloud.upgrade.dao.Upgrade41800to41810;
|
|||||||
import com.cloud.upgrade.dao.Upgrade41810to41900;
|
import com.cloud.upgrade.dao.Upgrade41810to41900;
|
||||||
import com.cloud.upgrade.dao.Upgrade41900to41910;
|
import com.cloud.upgrade.dao.Upgrade41900to41910;
|
||||||
import com.cloud.upgrade.dao.Upgrade41910to42000;
|
import com.cloud.upgrade.dao.Upgrade41910to42000;
|
||||||
|
import com.cloud.upgrade.dao.Upgrade42000to42010;
|
||||||
import com.cloud.upgrade.dao.Upgrade420to421;
|
import com.cloud.upgrade.dao.Upgrade420to421;
|
||||||
import com.cloud.upgrade.dao.Upgrade421to430;
|
import com.cloud.upgrade.dao.Upgrade421to430;
|
||||||
import com.cloud.upgrade.dao.Upgrade430to440;
|
import com.cloud.upgrade.dao.Upgrade430to440;
|
||||||
@ -230,6 +231,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
|
|||||||
.next("4.18.1.0", new Upgrade41810to41900())
|
.next("4.18.1.0", new Upgrade41810to41900())
|
||||||
.next("4.19.0.0", new Upgrade41900to41910())
|
.next("4.19.0.0", new Upgrade41900to41910())
|
||||||
.next("4.19.1.0", new Upgrade41910to42000())
|
.next("4.19.1.0", new Upgrade41910to42000())
|
||||||
|
.next("4.20.0.0", new Upgrade42000to42010())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package com.cloud.upgrade.dao;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.sql.Connection;
|
||||||
|
|
||||||
|
import com.cloud.upgrade.SystemVmTemplateRegistration;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
public class Upgrade42000to42010 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {
|
||||||
|
private SystemVmTemplateRegistration systemVmTemplateRegistration;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getUpgradableVersionRange() {
|
||||||
|
return new String[] {"4.20.0.0", "4.20.1.0"};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUpgradedVersion() {
|
||||||
|
return "4.20.1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRollingUpgrade() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream[] getPrepareScripts() {
|
||||||
|
final String scriptFile = "META-INF/db/schema-42000to42010.sql";
|
||||||
|
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
|
||||||
|
if (script == null) {
|
||||||
|
throw new CloudRuntimeException("Unable to find " + scriptFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InputStream[] {script};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performDataMigration(Connection conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream[] getCleanupScripts() {
|
||||||
|
final String scriptFile = "META-INF/db/schema-42000to42010-cleanup.sql";
|
||||||
|
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
|
||||||
|
if (script == null) {
|
||||||
|
throw new CloudRuntimeException("Unable to find " + scriptFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InputStream[] {script};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSystemVmTemplateRegistration() {
|
||||||
|
systemVmTemplateRegistration = new SystemVmTemplateRegistration("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSystemVmTemplates(Connection conn) {
|
||||||
|
logger.debug("Updating System Vm template IDs");
|
||||||
|
initSystemVmTemplateRegistration();
|
||||||
|
try {
|
||||||
|
systemVmTemplateRegistration.updateSystemVmTemplates(conn);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -77,6 +77,9 @@ public class AccountVO implements Account {
|
|||||||
@Column(name = "default")
|
@Column(name = "default")
|
||||||
boolean isDefault;
|
boolean isDefault;
|
||||||
|
|
||||||
|
@Column(name = "api_key_access")
|
||||||
|
private Boolean apiKeyAccess;
|
||||||
|
|
||||||
public AccountVO() {
|
public AccountVO() {
|
||||||
uuid = UUID.randomUUID().toString();
|
uuid = UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
@ -229,4 +232,14 @@ public class AccountVO implements Account {
|
|||||||
public String reflectionToString() {
|
public String reflectionToString() {
|
||||||
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "accountName", "domainId");
|
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "accountName", "domainId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess) {
|
||||||
|
this.apiKeyAccess = apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,6 +115,9 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||||||
@Column(name = "key_for_2fa")
|
@Column(name = "key_for_2fa")
|
||||||
private String keyFor2fa;
|
private String keyFor2fa;
|
||||||
|
|
||||||
|
@Column(name = "api_key_access")
|
||||||
|
private Boolean apiKeyAccess;
|
||||||
|
|
||||||
public UserVO() {
|
public UserVO() {
|
||||||
this.uuid = UUID.randomUUID().toString();
|
this.uuid = UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
@ -350,4 +353,13 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||||||
this.user2faProvider = user2faProvider;
|
this.user2faProvider = user2faProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApiKeyAccess(Boolean apiKeyAccess) {
|
||||||
|
this.apiKeyAccess = apiKeyAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,8 +41,8 @@ import java.util.List;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements AccountDao {
|
public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements AccountDao {
|
||||||
private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, "
|
private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, u.api_key_access, "
|
||||||
+ "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state " + "FROM `cloud`.`user` u, `cloud`.`account` a "
|
+ "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state, a.api_key_access " + "FROM `cloud`.`user` u, `cloud`.`account` a "
|
||||||
+ "WHERE u.account_id = a.id AND u.api_key = ? and u.removed IS NULL";
|
+ "WHERE u.account_id = a.id AND u.api_key = ? and u.removed IS NULL";
|
||||||
|
|
||||||
protected final SearchBuilder<AccountVO> AllFieldsSearch;
|
protected final SearchBuilder<AccountVO> AllFieldsSearch;
|
||||||
@ -148,13 +148,25 @@ public class AccountDaoImpl extends GenericDaoBase<AccountVO, Long> implements A
|
|||||||
u.setAccountId(rs.getLong(3));
|
u.setAccountId(rs.getLong(3));
|
||||||
u.setSecretKey(DBEncryptionUtil.decrypt(rs.getString(4)));
|
u.setSecretKey(DBEncryptionUtil.decrypt(rs.getString(4)));
|
||||||
u.setState(State.getValueOf(rs.getString(5)));
|
u.setState(State.getValueOf(rs.getString(5)));
|
||||||
|
boolean apiKeyAccess = rs.getBoolean(6);
|
||||||
|
if (rs.wasNull()) {
|
||||||
|
u.setApiKeyAccess(null);
|
||||||
|
} else {
|
||||||
|
u.setApiKeyAccess(apiKeyAccess);
|
||||||
|
}
|
||||||
|
|
||||||
AccountVO a = new AccountVO(rs.getLong(6));
|
AccountVO a = new AccountVO(rs.getLong(7));
|
||||||
a.setAccountName(rs.getString(7));
|
a.setAccountName(rs.getString(8));
|
||||||
a.setType(Account.Type.getFromValue(rs.getInt(8)));
|
a.setType(Account.Type.getFromValue(rs.getInt(9)));
|
||||||
a.setRoleId(rs.getLong(9));
|
a.setRoleId(rs.getLong(10));
|
||||||
a.setDomainId(rs.getLong(10));
|
a.setDomainId(rs.getLong(11));
|
||||||
a.setState(State.getValueOf(rs.getString(11)));
|
a.setState(State.getValueOf(rs.getString(12)));
|
||||||
|
apiKeyAccess = rs.getBoolean(13);
|
||||||
|
if (rs.wasNull()) {
|
||||||
|
a.setApiKeyAccess(null);
|
||||||
|
} else {
|
||||||
|
a.setApiKeyAccess(apiKeyAccess);
|
||||||
|
}
|
||||||
|
|
||||||
userAcctPair = new Pair<User, Account>(u, a);
|
userAcctPair = new Pair<User, Account>(u, a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
|
||||||
|
--;
|
||||||
|
-- Schema upgrade cleanup from 4.20.0.0 to 4.20.1.0
|
||||||
|
--;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
|
||||||
|
--;
|
||||||
|
-- Schema upgrade from 4.20.0.0 to 4.20.1.0
|
||||||
|
--;
|
||||||
|
|
||||||
|
-- Add column api_key_access to user and account tables
|
||||||
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`');
|
||||||
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" ');
|
||||||
@ -31,6 +31,7 @@ select
|
|||||||
`account`.`cleanup_needed` AS `cleanup_needed`,
|
`account`.`cleanup_needed` AS `cleanup_needed`,
|
||||||
`account`.`network_domain` AS `network_domain` ,
|
`account`.`network_domain` AS `network_domain` ,
|
||||||
`account`.`default` AS `default`,
|
`account`.`default` AS `default`,
|
||||||
|
`account`.`api_key_access` AS `api_key_access`,
|
||||||
`domain`.`id` AS `domain_id`,
|
`domain`.`id` AS `domain_id`,
|
||||||
`domain`.`uuid` AS `domain_uuid`,
|
`domain`.`uuid` AS `domain_uuid`,
|
||||||
`domain`.`name` AS `domain_name`,
|
`domain`.`name` AS `domain_name`,
|
||||||
|
|||||||
@ -39,6 +39,7 @@ select
|
|||||||
user.incorrect_login_attempts,
|
user.incorrect_login_attempts,
|
||||||
user.source,
|
user.source,
|
||||||
user.default,
|
user.default,
|
||||||
|
user.api_key_access,
|
||||||
account.id account_id,
|
account.id account_id,
|
||||||
account.uuid account_uuid,
|
account.uuid account_uuid,
|
||||||
account.account_name account_name,
|
account.account_name account_name,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ public class ConfigKey<T> {
|
|||||||
public static final String CATEGORY_ADVANCED = "Advanced";
|
public static final String CATEGORY_ADVANCED = "Advanced";
|
||||||
public static final String CATEGORY_ALERT = "Alert";
|
public static final String CATEGORY_ALERT = "Alert";
|
||||||
public static final String CATEGORY_NETWORK = "Network";
|
public static final String CATEGORY_NETWORK = "Network";
|
||||||
|
public static final String CATEGORY_SYSTEM = "System";
|
||||||
|
|
||||||
public enum Scope {
|
public enum Scope {
|
||||||
Global, Zone, Cluster, StoragePool, Account, ManagementServer, ImageStore, Domain
|
Global, Zone, Cluster, StoragePool, Account, ManagementServer, ImageStore, Domain
|
||||||
|
|||||||
@ -486,12 +486,12 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(GetUserKeysCmd cmd){
|
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(Long userId) {
|
public Pair<Boolean, Map<String, String>> getKeys(Long userId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1945,11 +1945,11 @@ public class ApiDBUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static UserResponse newUserResponse(UserAccountJoinVO usr) {
|
public static UserResponse newUserResponse(UserAccountJoinVO usr) {
|
||||||
return newUserResponse(usr, null);
|
return newUserResponse(ResponseView.Restricted, null, usr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserResponse newUserResponse(UserAccountJoinVO usr, Long domainId) {
|
public static UserResponse newUserResponse(ResponseView view, Long domainId, UserAccountJoinVO usr) {
|
||||||
UserResponse response = s_userAccountJoinDao.newUserResponse(usr);
|
UserResponse response = s_userAccountJoinDao.newUserResponse(view, usr);
|
||||||
if(!AccountManager.UseSecretKeyInResponse.value()){
|
if(!AccountManager.UseSecretKeyInResponse.value()){
|
||||||
response.setSecretKey(null);
|
response.setSecretKey(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,6 +188,7 @@ import com.cloud.utils.exception.ExceptionProxyObject;
|
|||||||
import com.cloud.utils.net.NetUtils;
|
import com.cloud.utils.net.NetUtils;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import static com.cloud.user.AccountManagerImpl.apiKeyAccess;
|
||||||
import static org.apache.cloudstack.user.UserPasswordResetManager.UserPasswordResetEnabled;
|
import static org.apache.cloudstack.user.UserPasswordResetManager.UserPasswordResetEnabled;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ -896,6 +897,34 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean verifyApiKeyAccessAllowed(User user, Account account) {
|
||||||
|
Boolean apiKeyAccessEnabled = user.getApiKeyAccess();
|
||||||
|
if (apiKeyAccessEnabled != null) {
|
||||||
|
if (Boolean.TRUE.equals(apiKeyAccessEnabled)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.info("Api-Key access is disabled for the User " + user.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiKeyAccessEnabled = account.getApiKeyAccess();
|
||||||
|
if (apiKeyAccessEnabled != null) {
|
||||||
|
if (Boolean.TRUE.equals(apiKeyAccessEnabled)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.info("Api-Key access is disabled for the Account " + account.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiKeyAccessEnabled = apiKeyAccess.valueIn(account.getDomainId());
|
||||||
|
if (Boolean.TRUE.equals(apiKeyAccessEnabled)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.info("Api-Key access is disabled by the Domain level setting api.key.access");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verifyRequest(final Map<String, Object[]> requestParameters, final Long userId, InetAddress remoteAddress) throws ServerApiException {
|
public boolean verifyRequest(final Map<String, Object[]> requestParameters, final Long userId, InetAddress remoteAddress) throws ServerApiException {
|
||||||
try {
|
try {
|
||||||
@ -1012,6 +1041,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!verifyApiKeyAccessAllowed(user, account)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!commandAvailable(remoteAddress, commandName, user)) {
|
if (!commandAvailable(remoteAddress, commandName, user)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -661,10 +661,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
* .api.command.admin.user.ListUsersCmd)
|
* .api.command.admin.user.ListUsersCmd)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListResponse<UserResponse> searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException {
|
public ListResponse<UserResponse> searchForUsers(ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException {
|
||||||
Pair<List<UserAccountJoinVO>, Integer> result = searchForUsersInternal(cmd);
|
Pair<List<UserAccountJoinVO>, Integer> result = searchForUsersInternal(cmd);
|
||||||
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
|
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
|
||||||
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(CallContext.current().getCallingAccount().getDomainId(),
|
if (CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) {
|
||||||
|
responseView = ResponseView.Full;
|
||||||
|
}
|
||||||
|
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(responseView, CallContext.current().getCallingAccount().getDomainId(),
|
||||||
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
|
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
|
||||||
response.setResponses(userResponses, result.second());
|
response.setResponses(userResponses, result.second());
|
||||||
return response;
|
return response;
|
||||||
@ -691,10 +694,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
Object state = null;
|
Object state = null;
|
||||||
String keyword = null;
|
String keyword = null;
|
||||||
|
|
||||||
Pair<List<UserAccountJoinVO>, Integer> result = getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, domainId, recursive,
|
Pair<List<UserAccountJoinVO>, Integer> result = getUserListInternal(caller, permittedAccounts, listAll, id,
|
||||||
null);
|
username, type, accountName, state, keyword, null, domainId, recursive, null);
|
||||||
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
|
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
|
||||||
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(CallContext.current().getCallingAccount().getDomainId(),
|
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(ResponseView.Restricted, CallContext.current().getCallingAccount().getDomainId(),
|
||||||
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
|
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
|
||||||
response.setResponses(userResponses, result.second());
|
response.setResponses(userResponses, result.second());
|
||||||
return response;
|
return response;
|
||||||
@ -719,6 +722,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
String accountName = cmd.getAccountName();
|
String accountName = cmd.getAccountName();
|
||||||
Object state = cmd.getState();
|
Object state = cmd.getState();
|
||||||
String keyword = cmd.getKeyword();
|
String keyword = cmd.getKeyword();
|
||||||
|
String apiKeyAccess = cmd.getApiKeyAccess();
|
||||||
|
|
||||||
Long domainId = cmd.getDomainId();
|
Long domainId = cmd.getDomainId();
|
||||||
boolean recursive = cmd.isRecursive();
|
boolean recursive = cmd.isRecursive();
|
||||||
@ -727,11 +731,11 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
|
|
||||||
Filter searchFilter = new Filter(UserAccountJoinVO.class, "id", true, startIndex, pageSizeVal);
|
Filter searchFilter = new Filter(UserAccountJoinVO.class, "id", true, startIndex, pageSizeVal);
|
||||||
|
|
||||||
return getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, domainId, recursive, searchFilter);
|
return getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, apiKeyAccess, domainId, recursive, searchFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account caller, List<Long> permittedAccounts, boolean listAll, Long id, Object username, Object type,
|
private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account caller, List<Long> permittedAccounts, boolean listAll, Long id, Object username, Object type,
|
||||||
String accountName, Object state, String keyword, Long domainId, boolean recursive, Filter searchFilter) {
|
String accountName, Object state, String keyword, String apiKeyAccess, Long domainId, boolean recursive, Filter searchFilter) {
|
||||||
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, recursive, null);
|
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, recursive, null);
|
||||||
accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
||||||
domainId = domainIdRecursiveListProject.first();
|
domainId = domainIdRecursiveListProject.first();
|
||||||
@ -757,6 +761,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
sb.and("domainId", sb.entity().getDomainId(), Op.EQ);
|
sb.and("domainId", sb.entity().getDomainId(), Op.EQ);
|
||||||
sb.and("accountName", sb.entity().getAccountName(), Op.EQ);
|
sb.and("accountName", sb.entity().getAccountName(), Op.EQ);
|
||||||
sb.and("state", sb.entity().getState(), Op.EQ);
|
sb.and("state", sb.entity().getState(), Op.EQ);
|
||||||
|
if (apiKeyAccess != null) {
|
||||||
|
sb.and("apiKeyAccess", sb.entity().getApiKeyAccess(), Op.EQ);
|
||||||
|
}
|
||||||
|
|
||||||
if ((accountName == null) && (domainId != null)) {
|
if ((accountName == null) && (domainId != null)) {
|
||||||
sb.and("domainPath", sb.entity().getDomainPath(), Op.LIKE);
|
sb.and("domainPath", sb.entity().getDomainPath(), Op.LIKE);
|
||||||
@ -811,6 +818,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
sc.setParameters("state", state);
|
sc.setParameters("state", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (apiKeyAccess != null) {
|
||||||
|
try {
|
||||||
|
ApiConstants.ApiKeyAccess access = ApiConstants.ApiKeyAccess.valueOf(apiKeyAccess.toUpperCase());
|
||||||
|
sc.setParameters("apiKeyAccess", access.toBoolean());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _userAccountJoinDao.searchAndCount(sc, searchFilter);
|
return _userAccountJoinDao.searchAndCount(sc, searchFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2897,6 +2913,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
Object state = cmd.getState();
|
Object state = cmd.getState();
|
||||||
Object isCleanupRequired = cmd.isCleanupRequired();
|
Object isCleanupRequired = cmd.isCleanupRequired();
|
||||||
Object keyword = cmd.getKeyword();
|
Object keyword = cmd.getKeyword();
|
||||||
|
String apiKeyAccess = cmd.getApiKeyAccess();
|
||||||
|
|
||||||
SearchBuilder<AccountVO> accountSearchBuilder = _accountDao.createSearchBuilder();
|
SearchBuilder<AccountVO> accountSearchBuilder = _accountDao.createSearchBuilder();
|
||||||
accountSearchBuilder.select(null, Func.DISTINCT, accountSearchBuilder.entity().getId()); // select distinct
|
accountSearchBuilder.select(null, Func.DISTINCT, accountSearchBuilder.entity().getId()); // select distinct
|
||||||
@ -2909,6 +2926,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
accountSearchBuilder.and("typeNEQ", accountSearchBuilder.entity().getType(), SearchCriteria.Op.NEQ);
|
accountSearchBuilder.and("typeNEQ", accountSearchBuilder.entity().getType(), SearchCriteria.Op.NEQ);
|
||||||
accountSearchBuilder.and("idNEQ", accountSearchBuilder.entity().getId(), SearchCriteria.Op.NEQ);
|
accountSearchBuilder.and("idNEQ", accountSearchBuilder.entity().getId(), SearchCriteria.Op.NEQ);
|
||||||
accountSearchBuilder.and("type2NEQ", accountSearchBuilder.entity().getType(), SearchCriteria.Op.NEQ);
|
accountSearchBuilder.and("type2NEQ", accountSearchBuilder.entity().getType(), SearchCriteria.Op.NEQ);
|
||||||
|
if (apiKeyAccess != null) {
|
||||||
|
accountSearchBuilder.and("apiKeyAccess", accountSearchBuilder.entity().getApiKeyAccess(), Op.EQ);
|
||||||
|
}
|
||||||
|
|
||||||
if (domainId != null && isRecursive) {
|
if (domainId != null && isRecursive) {
|
||||||
SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
|
SearchBuilder<DomainVO> domainSearch = _domainDao.createSearchBuilder();
|
||||||
@ -2972,6 +2992,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (apiKeyAccess != null) {
|
||||||
|
try {
|
||||||
|
ApiConstants.ApiKeyAccess access = ApiConstants.ApiKeyAccess.valueOf(apiKeyAccess.toUpperCase());
|
||||||
|
sc.setParameters("apiKeyAccess", access.toBoolean());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Pair<List<AccountVO>, Integer> uniqueAccountPair = _accountDao.searchAndCount(sc, searchFilter);
|
Pair<List<AccountVO>, Integer> uniqueAccountPair = _accountDao.searchAndCount(sc, searchFilter);
|
||||||
Integer count = uniqueAccountPair.second();
|
Integer count = uniqueAccountPair.second();
|
||||||
List<Long> accountIds = uniqueAccountPair.first().stream().map(AccountVO::getId).collect(Collectors.toList());
|
List<Long> accountIds = uniqueAccountPair.first().stream().map(AccountVO::getId).collect(Collectors.toList());
|
||||||
|
|||||||
@ -105,13 +105,13 @@ public class ViewResponseHelper {
|
|||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
public static List<UserResponse> createUserResponse(UserAccountJoinVO... users) {
|
public static List<UserResponse> createUserResponse(UserAccountJoinVO... users) {
|
||||||
return createUserResponse(null, users);
|
return createUserResponse(ResponseView.Restricted, null, users);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<UserResponse> createUserResponse(Long domainId, UserAccountJoinVO... users) {
|
public static List<UserResponse> createUserResponse(ResponseView responseView, Long domainId, UserAccountJoinVO... users) {
|
||||||
List<UserResponse> respList = new ArrayList<UserResponse>();
|
List<UserResponse> respList = new ArrayList<UserResponse>();
|
||||||
for (UserAccountJoinVO vt : users) {
|
for (UserAccountJoinVO vt : users) {
|
||||||
respList.add(ApiDBUtils.newUserResponse(vt, domainId));
|
respList.add(ApiDBUtils.newUserResponse(responseView, domainId, vt));
|
||||||
}
|
}
|
||||||
return respList;
|
return respList;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,9 @@ public class AccountJoinDaoImpl extends GenericDaoBase<AccountJoinVO, Long> impl
|
|||||||
accountResponse.setNetworkDomain(account.getNetworkDomain());
|
accountResponse.setNetworkDomain(account.getNetworkDomain());
|
||||||
accountResponse.setDefaultZone(account.getDataCenterUuid());
|
accountResponse.setDefaultZone(account.getDataCenterUuid());
|
||||||
accountResponse.setIsDefault(account.isDefault());
|
accountResponse.setIsDefault(account.isDefault());
|
||||||
|
if (view == ResponseView.Full) {
|
||||||
|
accountResponse.setApiKeyAccess(account.getApiKeyAccess());
|
||||||
|
}
|
||||||
|
|
||||||
// get network stat
|
// get network stat
|
||||||
accountResponse.setBytesReceived(account.getBytesReceived());
|
accountResponse.setBytesReceived(account.getBytesReceived());
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package com.cloud.api.query.dao;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
import org.apache.cloudstack.api.response.UserResponse;
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
|
|
||||||
import com.cloud.api.query.vo.UserAccountJoinVO;
|
import com.cloud.api.query.vo.UserAccountJoinVO;
|
||||||
@ -27,7 +28,7 @@ import com.cloud.utils.db.GenericDao;
|
|||||||
|
|
||||||
public interface UserAccountJoinDao extends GenericDao<UserAccountJoinVO, Long> {
|
public interface UserAccountJoinDao extends GenericDao<UserAccountJoinVO, Long> {
|
||||||
|
|
||||||
UserResponse newUserResponse(UserAccountJoinVO usr);
|
UserResponse newUserResponse(ResponseObject.ResponseView responseView, UserAccountJoinVO usr);
|
||||||
|
|
||||||
UserAccountJoinVO newUserView(User usr);
|
UserAccountJoinVO newUserView(User usr);
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import java.util.List;
|
|||||||
|
|
||||||
|
|
||||||
import com.cloud.user.AccountManagerImpl;
|
import com.cloud.user.AccountManagerImpl;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.response.UserResponse;
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
@ -52,7 +53,7 @@ public class UserAccountJoinDaoImpl extends GenericDaoBase<UserAccountJoinVO, Lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserResponse newUserResponse(UserAccountJoinVO usr) {
|
public UserResponse newUserResponse(ResponseView view, UserAccountJoinVO usr) {
|
||||||
UserResponse userResponse = new UserResponse();
|
UserResponse userResponse = new UserResponse();
|
||||||
userResponse.setAccountId(usr.getAccountUuid());
|
userResponse.setAccountId(usr.getAccountUuid());
|
||||||
userResponse.setAccountName(usr.getAccountName());
|
userResponse.setAccountName(usr.getAccountName());
|
||||||
@ -75,6 +76,9 @@ public class UserAccountJoinDaoImpl extends GenericDaoBase<UserAccountJoinVO, Lo
|
|||||||
long domainId = usr.getDomainId();
|
long domainId = usr.getDomainId();
|
||||||
boolean is2FAmandated = Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(domainId)) && Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(domainId));
|
boolean is2FAmandated = Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(domainId)) && Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(domainId));
|
||||||
userResponse.set2FAmandated(is2FAmandated);
|
userResponse.set2FAmandated(is2FAmandated);
|
||||||
|
if (view == ResponseView.Full) {
|
||||||
|
userResponse.setApiKeyAccess(usr.getApiKeyAccess());
|
||||||
|
}
|
||||||
|
|
||||||
// set async job
|
// set async job
|
||||||
if (usr.getJobId() != null) {
|
if (usr.getJobId() != null) {
|
||||||
|
|||||||
@ -189,6 +189,9 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident
|
|||||||
@Column(name = "default")
|
@Column(name = "default")
|
||||||
boolean isDefault;
|
boolean isDefault;
|
||||||
|
|
||||||
|
@Column(name = "api_key_access")
|
||||||
|
Boolean apiKeyAccess;
|
||||||
|
|
||||||
public AccountJoinVO() {
|
public AccountJoinVO() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,4 +396,8 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident
|
|||||||
public boolean isDefault() {
|
public boolean isDefault() {
|
||||||
return isDefault;
|
return isDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,6 +133,9 @@ public class UserAccountJoinVO extends BaseViewVO implements InternalIdentity, I
|
|||||||
@Column(name = "is_user_2fa_enabled")
|
@Column(name = "is_user_2fa_enabled")
|
||||||
boolean user2faEnabled;
|
boolean user2faEnabled;
|
||||||
|
|
||||||
|
@Column(name = "api_key_access")
|
||||||
|
Boolean apiKeyAccess;
|
||||||
|
|
||||||
public UserAccountJoinVO() {
|
public UserAccountJoinVO() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,4 +284,8 @@ public class UserAccountJoinVO extends BaseViewVO implements InternalIdentity, I
|
|||||||
public boolean isUser2faEnabled() {
|
public boolean isUser2faEnabled() {
|
||||||
return user2faEnabled;
|
return user2faEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getApiKeyAccess() {
|
||||||
|
return apiKeyAccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,7 @@ import org.apache.cloudstack.agent.lb.IndirectAgentLB;
|
|||||||
import org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl;
|
import org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl;
|
||||||
import org.apache.cloudstack.annotation.AnnotationService;
|
import org.apache.cloudstack.annotation.AnnotationService;
|
||||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||||
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
|
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
|
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
|
||||||
@ -310,6 +311,7 @@ import com.googlecode.ipv6.IPv6Network;
|
|||||||
import static com.cloud.configuration.Config.SecStorageAllowedInternalDownloadSites;
|
import static com.cloud.configuration.Config.SecStorageAllowedInternalDownloadSites;
|
||||||
import static com.cloud.offering.NetworkOffering.RoutingMode.Dynamic;
|
import static com.cloud.offering.NetworkOffering.RoutingMode.Dynamic;
|
||||||
import static com.cloud.offering.NetworkOffering.RoutingMode.Static;
|
import static com.cloud.offering.NetworkOffering.RoutingMode.Static;
|
||||||
|
import static org.apache.cloudstack.framework.config.ConfigKey.CATEGORY_SYSTEM;
|
||||||
|
|
||||||
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
|
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
|
||||||
public static final String PERACCOUNT = "peraccount";
|
public static final String PERACCOUNT = "peraccount";
|
||||||
@ -708,6 +710,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
value = DBEncryptionUtil.encrypt(value);
|
value = DBEncryptionUtil.encrypt(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiCommandResourceType resourceType;
|
||||||
ConfigKey.Scope scopeVal = ConfigKey.Scope.valueOf(scope);
|
ConfigKey.Scope scopeVal = ConfigKey.Scope.valueOf(scope);
|
||||||
switch (scopeVal) {
|
switch (scopeVal) {
|
||||||
case Zone:
|
case Zone:
|
||||||
@ -715,6 +718,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (zone == null) {
|
if (zone == null) {
|
||||||
throw new InvalidParameterValueException("unable to find zone by id " + resourceId);
|
throw new InvalidParameterValueException("unable to find zone by id " + resourceId);
|
||||||
}
|
}
|
||||||
|
resourceType = ApiCommandResourceType.Zone;
|
||||||
_dcDetailsDao.addDetail(resourceId, name, value, true);
|
_dcDetailsDao.addDetail(resourceId, name, value, true);
|
||||||
break;
|
break;
|
||||||
case Cluster:
|
case Cluster:
|
||||||
@ -722,6 +726,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (cluster == null) {
|
if (cluster == null) {
|
||||||
throw new InvalidParameterValueException("unable to find cluster by id " + resourceId);
|
throw new InvalidParameterValueException("unable to find cluster by id " + resourceId);
|
||||||
}
|
}
|
||||||
|
resourceType = ApiCommandResourceType.Cluster;
|
||||||
String newName = name;
|
String newName = name;
|
||||||
if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) {
|
if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) {
|
||||||
newName = "cpuOvercommitRatio";
|
newName = "cpuOvercommitRatio";
|
||||||
@ -744,6 +749,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (pool == null) {
|
if (pool == null) {
|
||||||
throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId);
|
throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId);
|
||||||
}
|
}
|
||||||
|
resourceType = ApiCommandResourceType.StoragePool;
|
||||||
if(name.equals(CapacityManager.StorageOverprovisioningFactor.key())) {
|
if(name.equals(CapacityManager.StorageOverprovisioningFactor.key())) {
|
||||||
if(!pool.getPoolType().supportsOverProvisioning() ) {
|
if(!pool.getPoolType().supportsOverProvisioning() ) {
|
||||||
throw new InvalidParameterValueException("Unable to update storage pool with id " + resourceId + ". Overprovision not supported for " + pool.getPoolType());
|
throw new InvalidParameterValueException("Unable to update storage pool with id " + resourceId + ". Overprovision not supported for " + pool.getPoolType());
|
||||||
@ -765,6 +771,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (account == null) {
|
if (account == null) {
|
||||||
throw new InvalidParameterValueException("unable to find account by id " + resourceId);
|
throw new InvalidParameterValueException("unable to find account by id " + resourceId);
|
||||||
}
|
}
|
||||||
|
resourceType = ApiCommandResourceType.Account;
|
||||||
AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(resourceId, name);
|
AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(resourceId, name);
|
||||||
if (accountDetailVO == null) {
|
if (accountDetailVO == null) {
|
||||||
accountDetailVO = new AccountDetailVO(resourceId, name, value);
|
accountDetailVO = new AccountDetailVO(resourceId, name, value);
|
||||||
@ -778,6 +785,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
case ImageStore:
|
case ImageStore:
|
||||||
final ImageStoreVO imgStore = _imageStoreDao.findById(resourceId);
|
final ImageStoreVO imgStore = _imageStoreDao.findById(resourceId);
|
||||||
Preconditions.checkState(imgStore != null);
|
Preconditions.checkState(imgStore != null);
|
||||||
|
resourceType = ApiCommandResourceType.ImageStore;
|
||||||
_imageStoreDetailsDao.addDetail(resourceId, name, value, true);
|
_imageStoreDetailsDao.addDetail(resourceId, name, value, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -786,6 +794,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (domain == null) {
|
if (domain == null) {
|
||||||
throw new InvalidParameterValueException("unable to find domain by id " + resourceId);
|
throw new InvalidParameterValueException("unable to find domain by id " + resourceId);
|
||||||
}
|
}
|
||||||
|
resourceType = ApiCommandResourceType.Domain;
|
||||||
DomainDetailVO domainDetailVO = _domainDetailsDao.findDetail(resourceId, name);
|
DomainDetailVO domainDetailVO = _domainDetailsDao.findDetail(resourceId, name);
|
||||||
if (domainDetailVO == null) {
|
if (domainDetailVO == null) {
|
||||||
domainDetailVO = new DomainDetailVO(resourceId, name, value);
|
domainDetailVO = new DomainDetailVO(resourceId, name, value);
|
||||||
@ -800,6 +809,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
throw new InvalidParameterValueException("Scope provided is invalid");
|
throw new InvalidParameterValueException("Scope provided is invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallContext.current().setEventResourceType(resourceType);
|
||||||
|
CallContext.current().setEventResourceId(resourceId);
|
||||||
|
CallContext.current().setEventDetails(String.format(" Name: %s, New Value: %s, Scope: %s", name, value, scope));
|
||||||
|
|
||||||
_configDepot.invalidateConfigCache(name, scopeVal, resourceId);
|
_configDepot.invalidateConfigCache(name, scopeVal, resourceId);
|
||||||
return valueEncrypted ? DBEncryptionUtil.decrypt(value) : value;
|
return valueEncrypted ? DBEncryptionUtil.decrypt(value) : value;
|
||||||
}
|
}
|
||||||
@ -957,6 +970,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
category = config.getCategory();
|
category = config.getCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CATEGORY_SYSTEM.equals(category) && !_accountMgr.isRootAdmin(caller.getId())) {
|
||||||
|
logger.warn("Only Root Admin is allowed to edit the configuration " + name);
|
||||||
|
throw new CloudRuntimeException("Only Root Admin is allowed to edit this configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return _configDao.findByName(name);
|
return _configDao.findByName(name);
|
||||||
}
|
}
|
||||||
@ -1008,7 +1026,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
if (value.isEmpty() || value.equals("null")) {
|
if (value.isEmpty() || value.equals("null")) {
|
||||||
value = (id == null) ? null : "";
|
value = (id == null) ? null : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
final String updatedValue = updateConfiguration(userId, name, category, value, scope, id);
|
final String updatedValue = updateConfiguration(userId, name, category, value, scope, id);
|
||||||
if (value == null && updatedValue == null || updatedValue.equalsIgnoreCase(value)) {
|
if (value == null && updatedValue == null || updatedValue.equalsIgnoreCase(value)) {
|
||||||
return _configDao.findByName(name);
|
return _configDao.findByName(name);
|
||||||
|
|||||||
@ -373,6 +373,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
"totp",
|
"totp",
|
||||||
"The default user two factor authentication provider. Eg. totp, staticpin", true, ConfigKey.Scope.Domain);
|
"The default user two factor authentication provider. Eg. totp, staticpin", true, ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
|
public static final ConfigKey<Boolean> apiKeyAccess = new ConfigKey<>(ConfigKey.CATEGORY_SYSTEM, Boolean.class,
|
||||||
|
"api.key.access",
|
||||||
|
"true",
|
||||||
|
"Determines whether API (api-key/secret-key) access is allowed or not. Editable only by Root Admin.",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
protected AccountManagerImpl() {
|
protected AccountManagerImpl() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -1463,6 +1470,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
logger.debug("Updating user with Id: " + user.getUuid());
|
logger.debug("Updating user with Id: " + user.getUuid());
|
||||||
|
|
||||||
validateAndUpdateApiAndSecretKeyIfNeeded(updateUserCmd, user);
|
validateAndUpdateApiAndSecretKeyIfNeeded(updateUserCmd, user);
|
||||||
|
validateAndUpdateUserApiKeyAccess(updateUserCmd, user);
|
||||||
Account account = retrieveAndValidateAccount(user);
|
Account account = retrieveAndValidateAccount(user);
|
||||||
|
|
||||||
validateAndUpdateFirstNameIfNeeded(updateUserCmd, user);
|
validateAndUpdateFirstNameIfNeeded(updateUserCmd, user);
|
||||||
@ -1682,6 +1690,38 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
user.setSecretKey(secretKey);
|
user.setSecretKey(secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void validateAndUpdateUserApiKeyAccess(UpdateUserCmd updateUserCmd, UserVO user) {
|
||||||
|
if (updateUserCmd.getApiKeyAccess() != null) {
|
||||||
|
try {
|
||||||
|
ApiConstants.ApiKeyAccess access = ApiConstants.ApiKeyAccess.valueOf(updateUserCmd.getApiKeyAccess().toUpperCase());
|
||||||
|
user.setApiKeyAccess(access.toBoolean());
|
||||||
|
Long callingUserId = CallContext.current().getCallingUserId();
|
||||||
|
Account callingAccount = CallContext.current().getCallingAccount();
|
||||||
|
ActionEventUtils.onActionEvent(callingUserId, callingAccount.getAccountId(), callingAccount.getDomainId(),
|
||||||
|
EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the User to " + access.toString(),
|
||||||
|
user.getId(), ApiCommandResourceType.User.toString());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateAndUpdateAccountApiKeyAccess(UpdateAccountCmd updateAccountCmd, AccountVO account) {
|
||||||
|
if (updateAccountCmd.getApiKeyAccess() != null) {
|
||||||
|
try {
|
||||||
|
ApiConstants.ApiKeyAccess access = ApiConstants.ApiKeyAccess.valueOf(updateAccountCmd.getApiKeyAccess().toUpperCase());
|
||||||
|
account.setApiKeyAccess(access.toBoolean());
|
||||||
|
Long callingUserId = CallContext.current().getCallingUserId();
|
||||||
|
Account callingAccount = CallContext.current().getCallingAccount();
|
||||||
|
ActionEventUtils.onActionEvent(callingUserId, callingAccount.getAccountId(), callingAccount.getDomainId(),
|
||||||
|
EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the Account to " + access.toString(),
|
||||||
|
account.getId(), ApiCommandResourceType.Account.toString());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for a user with the given userId. If no user is found we throw an {@link InvalidParameterValueException}.
|
* Searches for a user with the given userId. If no user is found we throw an {@link InvalidParameterValueException}.
|
||||||
*/
|
*/
|
||||||
@ -2048,6 +2088,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
Account caller = getCurrentCallingAccount();
|
Account caller = getCurrentCallingAccount();
|
||||||
checkAccess(caller, _domainMgr.getDomain(account.getDomainId()));
|
checkAccess(caller, _domainMgr.getDomain(account.getDomainId()));
|
||||||
|
|
||||||
|
validateAndUpdateAccountApiKeyAccess(cmd, acctForUpdate);
|
||||||
|
|
||||||
if(newAccountName != null) {
|
if(newAccountName != null) {
|
||||||
|
|
||||||
if (newAccountName.isEmpty()) {
|
if (newAccountName.isEmpty()) {
|
||||||
@ -2794,18 +2836,18 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(GetUserKeysCmd cmd) {
|
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd) {
|
||||||
final long userId = cmd.getID();
|
final long userId = cmd.getID();
|
||||||
return getKeys(userId);
|
return getKeys(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(Long userId) {
|
public Pair<Boolean, Map<String, String>> getKeys(Long userId) {
|
||||||
User user = getActiveUser(userId);
|
User user = getActiveUser(userId);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new InvalidParameterValueException("Unable to find user by id");
|
throw new InvalidParameterValueException("Unable to find user by id");
|
||||||
}
|
}
|
||||||
final ControlledEntity account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user.
|
final Account account = getAccount(getUserAccountById(userId).getAccountId()); //Extracting the Account from the userID of the requested user.
|
||||||
User caller = CallContext.current().getCallingUser();
|
User caller = CallContext.current().getCallingUser();
|
||||||
preventRootDomainAdminAccessToRootAdminKeys(caller, account);
|
preventRootDomainAdminAccessToRootAdminKeys(caller, account);
|
||||||
checkAccess(caller, account);
|
checkAccess(caller, account);
|
||||||
@ -2814,7 +2856,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
keys.put("apikey", user.getApiKey());
|
keys.put("apikey", user.getApiKey());
|
||||||
keys.put("secretkey", user.getSecretKey());
|
keys.put("secretkey", user.getSecretKey());
|
||||||
|
|
||||||
return keys;
|
Boolean apiKeyAccess = user.getApiKeyAccess();
|
||||||
|
if (apiKeyAccess == null) {
|
||||||
|
apiKeyAccess = account.getApiKeyAccess();
|
||||||
|
if (apiKeyAccess == null) {
|
||||||
|
apiKeyAccess = AccountManagerImpl.apiKeyAccess.valueIn(account.getDomainId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<Boolean, Map<String, String>>(apiKeyAccess, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void preventRootDomainAdminAccessToRootAdminKeys(User caller, ControlledEntity account) {
|
protected void preventRootDomainAdminAccessToRootAdminKeys(User caller, ControlledEntity account) {
|
||||||
@ -3320,7 +3370,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
@Override
|
@Override
|
||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
|
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
|
||||||
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer};
|
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess};
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
|
public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
package com.cloud.api;
|
package com.cloud.api;
|
||||||
|
|
||||||
import com.cloud.domain.Domain;
|
import com.cloud.domain.Domain;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.User;
|
||||||
import com.cloud.user.UserAccount;
|
import com.cloud.user.UserAccount;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
@ -147,4 +149,31 @@ public class ApiServerTest {
|
|||||||
Mockito.when(domain.getState()).thenReturn(Domain.State.Inactive);
|
Mockito.when(domain.getState()).thenReturn(Domain.State.Inactive);
|
||||||
apiServer.forgotPassword(userAccount, domain);
|
apiServer.forgotPassword(userAccount, domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyApiKeyAccessAllowed() {
|
||||||
|
Long domainId = 1L;
|
||||||
|
User user = Mockito.mock(User.class);
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
|
||||||
|
Mockito.when(user.getApiKeyAccess()).thenReturn(true);
|
||||||
|
Assert.assertEquals(true, apiServer.verifyApiKeyAccessAllowed(user, account));
|
||||||
|
Mockito.verify(account, Mockito.never()).getApiKeyAccess();
|
||||||
|
|
||||||
|
Mockito.when(user.getApiKeyAccess()).thenReturn(false);
|
||||||
|
Assert.assertEquals(false, apiServer.verifyApiKeyAccessAllowed(user, account));
|
||||||
|
Mockito.verify(account, Mockito.never()).getApiKeyAccess();
|
||||||
|
|
||||||
|
Mockito.when(user.getApiKeyAccess()).thenReturn(null);
|
||||||
|
Mockito.when(account.getApiKeyAccess()).thenReturn(true);
|
||||||
|
Assert.assertEquals(true, apiServer.verifyApiKeyAccessAllowed(user, account));
|
||||||
|
|
||||||
|
Mockito.when(user.getApiKeyAccess()).thenReturn(null);
|
||||||
|
Mockito.when(account.getApiKeyAccess()).thenReturn(false);
|
||||||
|
Assert.assertEquals(false, apiServer.verifyApiKeyAccessAllowed(user, account));
|
||||||
|
|
||||||
|
Mockito.when(user.getApiKeyAccess()).thenReturn(null);
|
||||||
|
Mockito.when(account.getApiKeyAccess()).thenReturn(null);
|
||||||
|
Assert.assertEquals(true, apiServer.verifyApiKeyAccessAllowed(user, account));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,18 @@
|
|||||||
|
|
||||||
package com.cloud.api.query;
|
package com.cloud.api.query;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiDBUtils;
|
||||||
import com.cloud.api.query.dao.TemplateJoinDao;
|
import com.cloud.api.query.dao.TemplateJoinDao;
|
||||||
|
import com.cloud.api.query.dao.UserAccountJoinDao;
|
||||||
import com.cloud.api.query.dao.UserVmJoinDao;
|
import com.cloud.api.query.dao.UserVmJoinDao;
|
||||||
import com.cloud.api.query.vo.EventJoinVO;
|
import com.cloud.api.query.vo.EventJoinVO;
|
||||||
import com.cloud.api.query.vo.TemplateJoinVO;
|
import com.cloud.api.query.vo.TemplateJoinVO;
|
||||||
|
import com.cloud.api.query.vo.UserAccountJoinVO;
|
||||||
import com.cloud.api.query.vo.UserVmJoinVO;
|
import com.cloud.api.query.vo.UserVmJoinVO;
|
||||||
import com.cloud.dc.ClusterVO;
|
import com.cloud.dc.ClusterVO;
|
||||||
import com.cloud.dc.dao.ClusterDao;
|
import com.cloud.dc.dao.ClusterDao;
|
||||||
|
import com.cloud.domain.DomainVO;
|
||||||
|
import com.cloud.domain.dao.DomainDao;
|
||||||
import com.cloud.event.EventVO;
|
import com.cloud.event.EventVO;
|
||||||
import com.cloud.event.dao.EventDao;
|
import com.cloud.event.dao.EventDao;
|
||||||
import com.cloud.event.dao.EventJoinDao;
|
import com.cloud.event.dao.EventJoinDao;
|
||||||
@ -45,6 +50,7 @@ import com.cloud.user.AccountManager;
|
|||||||
import com.cloud.user.AccountVO;
|
import com.cloud.user.AccountVO;
|
||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
import com.cloud.user.UserVO;
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.user.dao.AccountDao;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.db.EntityManager;
|
import com.cloud.utils.db.EntityManager;
|
||||||
import com.cloud.utils.db.Filter;
|
import com.cloud.utils.db.Filter;
|
||||||
@ -56,8 +62,11 @@ import com.cloud.vm.dao.VMInstanceDao;
|
|||||||
|
|
||||||
import org.apache.cloudstack.acl.SecurityChecker;
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
import org.apache.cloudstack.api.command.admin.storage.ListObjectStoragePoolsCmd;
|
import org.apache.cloudstack.api.command.admin.storage.ListObjectStoragePoolsCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.user.ListUsersCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.vm.ListAffectedVmsForStorageScopeChangeCmd;
|
import org.apache.cloudstack.api.command.admin.vm.ListAffectedVmsForStorageScopeChangeCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
|
||||||
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
|
import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd;
|
||||||
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
|
import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
|
||||||
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
||||||
@ -65,6 +74,7 @@ import org.apache.cloudstack.api.response.DetailOptionsResponse;
|
|||||||
import org.apache.cloudstack.api.response.EventResponse;
|
import org.apache.cloudstack.api.response.EventResponse;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
import org.apache.cloudstack.api.response.ObjectStoreResponse;
|
import org.apache.cloudstack.api.response.ObjectStoreResponse;
|
||||||
|
import org.apache.cloudstack.api.response.UserResponse;
|
||||||
import org.apache.cloudstack.api.response.VirtualMachineResponse;
|
import org.apache.cloudstack.api.response.VirtualMachineResponse;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao;
|
||||||
@ -150,6 +160,15 @@ public class QueryManagerImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
UserVmJoinDao userVmJoinDao;
|
UserVmJoinDao userVmJoinDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
UserAccountJoinDao userAccountJoinDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DomainDao domainDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AccountDao accountDao;
|
||||||
|
|
||||||
private AccountVO account;
|
private AccountVO account;
|
||||||
private UserVO user;
|
private UserVO user;
|
||||||
|
|
||||||
@ -477,4 +496,79 @@ public class QueryManagerImplTest {
|
|||||||
Assert.assertEquals(response.getResponses().get(0).getId(), instanceUuid);
|
Assert.assertEquals(response.getResponses().get(0).getId(), instanceUuid);
|
||||||
Assert.assertEquals(response.getResponses().get(0).getName(), vmName);
|
Assert.assertEquals(response.getResponses().get(0).getName(), vmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchForUsers() {
|
||||||
|
ListUsersCmd cmd = Mockito.mock(ListUsersCmd.class);
|
||||||
|
String username = "Admin";
|
||||||
|
String accountName = "Admin";
|
||||||
|
Account.Type accountType = Account.Type.ADMIN;
|
||||||
|
Long domainId = 1L;
|
||||||
|
String apiKeyAccess = "Disabled";
|
||||||
|
Mockito.when(cmd.getUsername()).thenReturn(username);
|
||||||
|
Mockito.when(cmd.getAccountName()).thenReturn(accountName);
|
||||||
|
Mockito.when(cmd.getAccountType()).thenReturn(accountType);
|
||||||
|
Mockito.when(cmd.getDomainId()).thenReturn(domainId);
|
||||||
|
Mockito.when(cmd.getApiKeyAccess()).thenReturn(apiKeyAccess);
|
||||||
|
|
||||||
|
UserAccountJoinVO user = new UserAccountJoinVO();
|
||||||
|
DomainVO domain = Mockito.mock(DomainVO.class);
|
||||||
|
SearchBuilder<UserAccountJoinVO> sb = Mockito.mock(SearchBuilder.class);
|
||||||
|
SearchCriteria<UserAccountJoinVO> sc = Mockito.mock(SearchCriteria.class);
|
||||||
|
List<UserAccountJoinVO> users = new ArrayList<>();
|
||||||
|
Pair<List<UserAccountJoinVO>, Integer> result = new Pair<>(users, 0);
|
||||||
|
UserResponse response = Mockito.mock(UserResponse.class);
|
||||||
|
|
||||||
|
Mockito.when(userAccountJoinDao.createSearchBuilder()).thenReturn(sb);
|
||||||
|
Mockito.when(sb.entity()).thenReturn(user);
|
||||||
|
Mockito.when(sb.create()).thenReturn(sc);
|
||||||
|
Mockito.when(userAccountJoinDao.searchAndCount(any(SearchCriteria.class), any(Filter.class))).thenReturn(result);
|
||||||
|
|
||||||
|
queryManager.searchForUsers(ResponseObject.ResponseView.Restricted, cmd);
|
||||||
|
|
||||||
|
Mockito.verify(sc).setParameters("username", username);
|
||||||
|
Mockito.verify(sc).setParameters("accountName", accountName);
|
||||||
|
Mockito.verify(sc).setParameters("type", accountType);
|
||||||
|
Mockito.verify(sc).setParameters("domainId", domainId);
|
||||||
|
Mockito.verify(sc).setParameters("apiKeyAccess", false);
|
||||||
|
Mockito.verify(userAccountJoinDao, Mockito.times(1)).searchAndCount(
|
||||||
|
any(SearchCriteria.class), any(Filter.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchForAccounts() {
|
||||||
|
ListAccountsCmd cmd = Mockito.mock(ListAccountsCmd.class);
|
||||||
|
Long domainId = 1L;
|
||||||
|
String accountName = "Admin";
|
||||||
|
Account.Type accountType = Account.Type.ADMIN;
|
||||||
|
String apiKeyAccess = "Enabled";
|
||||||
|
Mockito.when(cmd.getId()).thenReturn(null);
|
||||||
|
Mockito.when(cmd.getDomainId()).thenReturn(domainId);
|
||||||
|
Mockito.when(cmd.getSearchName()).thenReturn(accountName);
|
||||||
|
Mockito.when(cmd.getAccountType()).thenReturn(accountType);
|
||||||
|
Mockito.when(cmd.getApiKeyAccess()).thenReturn(apiKeyAccess);
|
||||||
|
|
||||||
|
DomainVO domain = Mockito.mock(DomainVO.class);
|
||||||
|
SearchBuilder<AccountVO> sb = Mockito.mock(SearchBuilder.class);
|
||||||
|
SearchCriteria<AccountVO> sc = Mockito.mock(SearchCriteria.class);
|
||||||
|
Pair<List<AccountVO>, Integer> uniqueAccountPair = new Pair<>(new ArrayList<>(), 0);
|
||||||
|
Mockito.when(domainDao.findById(domainId)).thenReturn(domain);
|
||||||
|
Mockito.doNothing().when(accountManager).checkAccess(account, domain);
|
||||||
|
|
||||||
|
Mockito.when(accountDao.createSearchBuilder()).thenReturn(sb);
|
||||||
|
Mockito.when(sb.entity()).thenReturn(account);
|
||||||
|
Mockito.when(sb.create()).thenReturn(sc);
|
||||||
|
Mockito.when(accountDao.searchAndCount(any(SearchCriteria.class), any(Filter.class))).thenReturn(uniqueAccountPair);
|
||||||
|
|
||||||
|
try (MockedStatic<ApiDBUtils> apiDBUtilsMocked = Mockito.mockStatic(ApiDBUtils.class)) {
|
||||||
|
queryManager.searchForAccounts(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mockito.verify(sc).setParameters("domainId", domainId);
|
||||||
|
Mockito.verify(sc).setParameters("accountName", accountName);
|
||||||
|
Mockito.verify(sc).setParameters("type", accountType);
|
||||||
|
Mockito.verify(sc).setParameters("apiKeyAccess", true);
|
||||||
|
Mockito.verify(accountDao, Mockito.times(1)).searchAndCount(
|
||||||
|
any(SearchCriteria.class), any(Filter.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,9 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.cloud.event.ActionEventUtils;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
|
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
@ -90,6 +92,9 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
@Mock
|
@Mock
|
||||||
private UpdateUserCmd UpdateUserCmdMock;
|
private UpdateUserCmd UpdateUserCmdMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UpdateAccountCmd UpdateAccountCmdMock;
|
||||||
|
|
||||||
private long userVoIdMock = 111l;
|
private long userVoIdMock = 111l;
|
||||||
@Mock
|
@Mock
|
||||||
private UserVO userVoMock;
|
private UserVO userVoMock;
|
||||||
@ -507,6 +512,46 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.verify(userVoMock).setSecretKey(secretKey);
|
Mockito.verify(userVoMock).setSecretKey(secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateAndUpdatUserApiKeyAccess() {
|
||||||
|
Mockito.doReturn("Enabled").when(UpdateUserCmdMock).getApiKeyAccess();
|
||||||
|
try (MockedStatic<ActionEventUtils> eventUtils = Mockito.mockStatic(ActionEventUtils.class)) {
|
||||||
|
Mockito.when(ActionEventUtils.onActionEvent(Mockito.anyLong(), Mockito.anyLong(),
|
||||||
|
Mockito.anyLong(),
|
||||||
|
Mockito.anyString(), Mockito.anyString(),
|
||||||
|
Mockito.anyLong(), Mockito.anyString())).thenReturn(1L);
|
||||||
|
accountManagerImpl.validateAndUpdateUserApiKeyAccess(UpdateUserCmdMock, userVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mockito.verify(userVoMock).setApiKeyAccess(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void validateAndUpdatUserApiKeyAccessInvalidParameter() {
|
||||||
|
Mockito.doReturn("False").when(UpdateUserCmdMock).getApiKeyAccess();
|
||||||
|
accountManagerImpl.validateAndUpdateUserApiKeyAccess(UpdateUserCmdMock, userVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateAndUpdatAccountApiKeyAccess() {
|
||||||
|
Mockito.doReturn("Inherit").when(UpdateAccountCmdMock).getApiKeyAccess();
|
||||||
|
try (MockedStatic<ActionEventUtils> eventUtils = Mockito.mockStatic(ActionEventUtils.class)) {
|
||||||
|
Mockito.when(ActionEventUtils.onActionEvent(Mockito.anyLong(), Mockito.anyLong(),
|
||||||
|
Mockito.anyLong(),
|
||||||
|
Mockito.anyString(), Mockito.anyString(),
|
||||||
|
Mockito.anyLong(), Mockito.anyString())).thenReturn(1L);
|
||||||
|
accountManagerImpl.validateAndUpdateAccountApiKeyAccess(UpdateAccountCmdMock, accountVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mockito.verify(accountVoMock).setApiKeyAccess(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void validateAndUpdatAccountApiKeyAccessInvalidParameter() {
|
||||||
|
Mockito.doReturn("False").when(UpdateAccountCmdMock).getApiKeyAccess();
|
||||||
|
accountManagerImpl.validateAndUpdateAccountApiKeyAccess(UpdateAccountCmdMock, accountVoMock);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = CloudRuntimeException.class)
|
@Test(expected = CloudRuntimeException.class)
|
||||||
public void retrieveAndValidateAccountTestAccountNotFound() {
|
public void retrieveAndValidateAccountTestAccountNotFound() {
|
||||||
Mockito.doReturn(accountMockId).when(userVoMock).getAccountId();
|
Mockito.doReturn(accountMockId).when(userVoMock).getAccountId();
|
||||||
|
|||||||
@ -450,12 +450,12 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(GetUserKeysCmd cmd) {
|
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getKeys(Long userId) {
|
public Pair<Boolean, Map<String, String>> getKeys(Long userId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
"label.accesskey": "Access key",
|
"label.accesskey": "Access key",
|
||||||
"label.access.key": "Access key",
|
"label.access.key": "Access key",
|
||||||
"label.secret.key": "Secret key",
|
"label.secret.key": "Secret key",
|
||||||
|
"label.apikeyaccess": "Api Key Access",
|
||||||
"label.account": "Account",
|
"label.account": "Account",
|
||||||
"label.account.and.security.group": "Account - security group",
|
"label.account.and.security.group": "Account - security group",
|
||||||
"label.account.id": "Account ID",
|
"label.account.id": "Account ID",
|
||||||
@ -882,6 +883,7 @@
|
|||||||
"label.edge": "Edge",
|
"label.edge": "Edge",
|
||||||
"label.edge.zone": "Edge Zone",
|
"label.edge.zone": "Edge Zone",
|
||||||
"label.edit": "Edit",
|
"label.edit": "Edit",
|
||||||
|
"label.edit.account": "Edit Account",
|
||||||
"label.edit.acl.list": "Edit ACL list",
|
"label.edit.acl.list": "Edit ACL list",
|
||||||
"label.edit.acl.rule": "Edit ACL rule",
|
"label.edit.acl.rule": "Edit ACL rule",
|
||||||
"label.edit.autoscale.vmprofile": "Edit AutoScale Instance Profile",
|
"label.edit.autoscale.vmprofile": "Edit AutoScale Instance Profile",
|
||||||
@ -3549,6 +3551,7 @@
|
|||||||
"message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster",
|
"message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster",
|
||||||
"message.success.unmanage.instance": "Successfully unmanaged Instance",
|
"message.success.unmanage.instance": "Successfully unmanaged Instance",
|
||||||
"message.success.unmanage.volume": "Successfully unmanaged Volume",
|
"message.success.unmanage.volume": "Successfully unmanaged Volume",
|
||||||
|
"message.success.update.account": "Successfully updated Account",
|
||||||
"message.success.update.bgp.peer": "Successfully updated BGP peer",
|
"message.success.update.bgp.peer": "Successfully updated BGP peer",
|
||||||
"message.success.update.bucket": "Successfully updated bucket",
|
"message.success.update.bucket": "Successfully updated bucket",
|
||||||
"message.success.update.condition": "Successfully updated condition",
|
"message.success.update.condition": "Successfully updated condition",
|
||||||
|
|||||||
@ -733,8 +733,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="account-center-tags" v-if="showKeys">
|
<div class="account-center-tags" v-if="showKeys || resource.apikeyaccess">
|
||||||
<a-divider/>
|
<a-divider/>
|
||||||
|
</div>
|
||||||
|
<div class="account-center-tags" v-if="resource.apikeyaccess && resource.account">
|
||||||
|
<div class="resource-detail-item">
|
||||||
|
<div class="resource-detail-item__label">{{ $t('label.apikeyaccess') }}</div>
|
||||||
|
<div class="resource-detail-item__details">
|
||||||
|
<status class="status" :text="resource.apikeyaccess" displayText/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="account-center-tags" v-if="showKeys">
|
||||||
<div class="user-keys">
|
<div class="user-keys">
|
||||||
<key-outlined />
|
<key-outlined />
|
||||||
<strong>
|
<strong>
|
||||||
@ -1083,6 +1093,9 @@ export default {
|
|||||||
api('getUserKeys', { id: this.resource.id }).then(json => {
|
api('getUserKeys', { id: this.resource.id }).then(json => {
|
||||||
this.showKeys = true
|
this.showKeys = true
|
||||||
this.newResource.secretkey = json.getuserkeysresponse.userkeys.secretkey
|
this.newResource.secretkey = json.getuserkeysresponse.userkeys.secretkey
|
||||||
|
if (!this.isAdmin()) {
|
||||||
|
this.newResource.apikeyaccess = json.getuserkeysresponse.userkeys.apikeyaccess ? 'Enabled' : 'Disabled'
|
||||||
|
}
|
||||||
this.$emit('change-resource', this.newResource)
|
this.$emit('change-resource', this.newResource)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -1113,6 +1126,9 @@ export default {
|
|||||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
||||||
(this.resource.project && this.resource.projectid === this.$store.getters.project.id)
|
(this.resource.project && this.resource.projectid === this.$store.getters.project.id)
|
||||||
},
|
},
|
||||||
|
isAdmin () {
|
||||||
|
return ['Admin'].includes(this.$store.getters.userInfo.roletype)
|
||||||
|
},
|
||||||
showInput () {
|
showInput () {
|
||||||
this.inputVisible = true
|
this.inputVisible = true
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
|
|||||||
@ -318,7 +318,7 @@ export default {
|
|||||||
type = 'list'
|
type = 'list'
|
||||||
} else if (item === 'tags') {
|
} else if (item === 'tags') {
|
||||||
type = 'tag'
|
type = 'tag'
|
||||||
} else if (item === 'resourcetype') {
|
} else if (['resourcetype', 'apikeyaccess'].includes(item)) {
|
||||||
type = 'autocomplete'
|
type = 'autocomplete'
|
||||||
} else if (item === 'isencrypted') {
|
} else if (item === 'isencrypted') {
|
||||||
type = 'boolean'
|
type = 'boolean'
|
||||||
@ -431,6 +431,17 @@ export default {
|
|||||||
]
|
]
|
||||||
this.fields[resourceTypeIndex].loading = false
|
this.fields[resourceTypeIndex].loading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('apikeyaccess')) {
|
||||||
|
const apiKeyAccessIndex = this.fields.findIndex(item => item.name === 'apikeyaccess')
|
||||||
|
this.fields[apiKeyAccessIndex].loading = true
|
||||||
|
this.fields[apiKeyAccessIndex].opts = [
|
||||||
|
{ value: 'Disabled' },
|
||||||
|
{ value: 'Enabled' },
|
||||||
|
{ value: 'Inherit' }
|
||||||
|
]
|
||||||
|
this.fields[apiKeyAccessIndex].loading = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async fetchDynamicFieldData (arrayField, searchKeyword) {
|
async fetchDynamicFieldData (arrayField, searchKeyword) {
|
||||||
const promises = []
|
const promises = []
|
||||||
|
|||||||
@ -24,9 +24,15 @@ export default {
|
|||||||
icon: 'team-outlined',
|
icon: 'team-outlined',
|
||||||
docHelp: 'adminguide/accounts.html',
|
docHelp: 'adminguide/accounts.html',
|
||||||
permission: ['listAccounts'],
|
permission: ['listAccounts'],
|
||||||
searchFilters: ['name', 'accounttype', 'domainid'],
|
searchFilters: () => {
|
||||||
|
var filters = ['name', 'accounttype', 'domainid']
|
||||||
|
if (store.getters.userInfo.roletype === 'Admin') {
|
||||||
|
filters.push('apikeyaccess')
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
},
|
||||||
columns: ['name', 'state', 'rolename', 'roletype', 'domainpath'],
|
columns: ['name', 'state', 'rolename', 'roletype', 'domainpath'],
|
||||||
details: ['name', 'id', 'rolename', 'roletype', 'domainpath', 'networkdomain', 'iptotal', 'vmtotal', 'volumetotal', 'receivedbytes', 'sentbytes', 'created'],
|
details: ['name', 'id', 'rolename', 'roletype', 'domainpath', 'networkdomain', 'apikeyaccess', 'iptotal', 'vmtotal', 'volumetotal', 'receivedbytes', 'sentbytes', 'created'],
|
||||||
related: [{
|
related: [{
|
||||||
name: 'accountuser',
|
name: 'accountuser',
|
||||||
title: 'label.users',
|
title: 'label.users',
|
||||||
@ -116,15 +122,8 @@ export default {
|
|||||||
icon: 'edit-outlined',
|
icon: 'edit-outlined',
|
||||||
label: 'label.action.edit.account',
|
label: 'label.action.edit.account',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['newname', 'account', 'domainid', 'networkdomain', 'roleid'],
|
popup: true,
|
||||||
mapping: {
|
component: shallowRef(defineAsyncComponent(() => import('@/views/iam/EditAccount.vue')))
|
||||||
account: {
|
|
||||||
value: (record) => { return record.name }
|
|
||||||
},
|
|
||||||
domainid: {
|
|
||||||
value: (record) => { return record.domainid }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'updateResourceCount',
|
api: 'updateResourceCount',
|
||||||
|
|||||||
@ -25,6 +25,13 @@ export default {
|
|||||||
docHelp: 'adminguide/accounts.html#users',
|
docHelp: 'adminguide/accounts.html#users',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
permission: ['listUsers'],
|
permission: ['listUsers'],
|
||||||
|
searchFilters: () => {
|
||||||
|
var filters = []
|
||||||
|
if (store.getters.userInfo.roletype === 'Admin') {
|
||||||
|
filters.push('apikeyaccess')
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
},
|
||||||
columns: ['username', 'state', 'firstname', 'lastname', 'email', 'account', 'domain'],
|
columns: ['username', 'state', 'firstname', 'lastname', 'email', 'account', 'domain'],
|
||||||
details: ['username', 'id', 'firstname', 'lastname', 'email', 'usersource', 'timezone', 'rolename', 'roletype', 'is2faenabled', 'account', 'domain', 'created'],
|
details: ['username', 'id', 'firstname', 'lastname', 'email', 'usersource', 'timezone', 'rolename', 'roletype', 'is2faenabled', 'account', 'domain', 'created'],
|
||||||
tabs: [
|
tabs: [
|
||||||
|
|||||||
190
ui/src/views/iam/EditAccount.vue
Normal file
190
ui/src/views/iam/EditAccount.vue
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:loading="loading"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="handleSubmit">
|
||||||
|
<a-form-item ref="newname" name="newname">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.newname')" :tooltip="apiParams.newname.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.newname"
|
||||||
|
:placeholder="apiParams.newname.description" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="networkdomain" name="networkdomain">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.networkdomain')" :tooltip="apiParams.networkdomain.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.networkdomain"
|
||||||
|
:placeholder="apiParams.networkdomain.description" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="roleid" name="roleid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.role')" :tooltip="apiParams.roleid.description"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.roleid"
|
||||||
|
:loading="roleLoading"
|
||||||
|
:placeholder="apiParams.roleid.description"
|
||||||
|
v-focus="true"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}">
|
||||||
|
<a-select-option v-for="role in roles" :key="role.id" :value="role.id">{{ role.name }}</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-if="isRootAdmin" ref="apikeyaccess" name="apikeyaccess">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.apikeyaccess')" :tooltip="apiParams.apikeyaccess.description"/>
|
||||||
|
</template>
|
||||||
|
<a-radio-group v-model:value="form.apikeyaccess" buttonStyle="solid">
|
||||||
|
<a-radio-button value="ENABLED">Enabled</a-radio-button>
|
||||||
|
<a-radio-button value="INHERIT">Inherit</a-radio-button>
|
||||||
|
<a-radio-button value="DISABLED">Disabled</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EditAccount',
|
||||||
|
components: {
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
roleLoading: false,
|
||||||
|
roles: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('updateAccount')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isRootAdmin () {
|
||||||
|
return this.$store.getters.userInfo?.roletype === 'Admin'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({})
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.account = this.resource.name
|
||||||
|
this.domainId = this.resource.domainid
|
||||||
|
this.form.apikeyaccess = this.resource.apikeyaccess
|
||||||
|
this.fetchRoles()
|
||||||
|
},
|
||||||
|
isValidValueForKey (obj, key) {
|
||||||
|
return key in obj && obj[key] != null
|
||||||
|
},
|
||||||
|
fetchRoles () {
|
||||||
|
this.roleLoading = true
|
||||||
|
const params = {}
|
||||||
|
params.state = 'enabled'
|
||||||
|
api('listRoles', params).then(response => {
|
||||||
|
this.roles = response.listrolesresponse.role || []
|
||||||
|
this.form.roleid = this.resource.roleid
|
||||||
|
}).finally(() => {
|
||||||
|
this.roleLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(() => {
|
||||||
|
const values = toRaw(this.form)
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
const params = {
|
||||||
|
newname: values.newname,
|
||||||
|
networkdomain: values.networkdomain,
|
||||||
|
roleid: values.roleid,
|
||||||
|
apikeyaccess: values.apikeyaccess,
|
||||||
|
account: this.account,
|
||||||
|
domainid: this.domainId
|
||||||
|
}
|
||||||
|
if (this.isValidValueForKey(values, 'networkdomain') && values.networkdomain.length > 0) {
|
||||||
|
params.networkdomain = values.networkdomain
|
||||||
|
}
|
||||||
|
|
||||||
|
api('updateAccount', params).then(response => {
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.edit.account'),
|
||||||
|
description: `${this.$t('message.success.update.account')} ${params.account}`
|
||||||
|
})
|
||||||
|
this.closeAction()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notification.error({
|
||||||
|
message: this.$t('message.request.failed'),
|
||||||
|
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -81,6 +81,16 @@
|
|||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item v-if="isRootAdmin" ref="apikeyaccess" name="apikeyaccess">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.apikeyaccess')" :tooltip="apiParams.apikeyaccess.description"/>
|
||||||
|
</template>
|
||||||
|
<a-radio-group v-model:value="form.apikeyaccess" buttonStyle="solid">
|
||||||
|
<a-radio-button value="ENABLED">Enabled</a-radio-button>
|
||||||
|
<a-radio-button value="INHERIT">Inherit</a-radio-button>
|
||||||
|
<a-radio-button value="DISABLED">Disabled</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
<div :span="24" class="action-button">
|
<div :span="24" class="action-button">
|
||||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
@ -128,6 +138,11 @@ export default {
|
|||||||
this.initForm()
|
this.initForm()
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isRootAdmin () {
|
||||||
|
return this.$store.getters.userInfo?.roletype === 'Admin'
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initForm () {
|
initForm () {
|
||||||
this.formRef = ref()
|
this.formRef = ref()
|
||||||
@ -187,7 +202,8 @@ export default {
|
|||||||
username: values.username,
|
username: values.username,
|
||||||
email: values.email,
|
email: values.email,
|
||||||
firstname: values.firstname,
|
firstname: values.firstname,
|
||||||
lastname: values.lastname
|
lastname: values.lastname,
|
||||||
|
apikeyaccess: values.apikeyaccess
|
||||||
}
|
}
|
||||||
if (this.isValidValueForKey(values, 'timezone') && values.timezone.length > 0) {
|
if (this.isValidValueForKey(values, 'timezone') && values.timezone.length > 0) {
|
||||||
params.timezone = values.timezone
|
params.timezone = values.timezone
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user