Skip to content

Commit e03cbcb

Browse files
author
gitlab
committed
Merge branch 'ZSPHERE-175@@2' into 'zsv_5.0.0'
<feature>[identity]: add ChangeAccountType API See merge request zstackio/zstack!9378
2 parents c8113e9 + a630cfc commit e03cbcb

11 files changed

Lines changed: 409 additions & 0 deletions

File tree

conf/serviceConfig/identity.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,8 @@
7777
<message>
7878
<name>org.zstack.header.identity.role.api.APIGetRolePolicyActionsMsg</name>
7979
</message>
80+
81+
<message>
82+
<name>org.zstack.header.identity.APIChangeAccountTypeMsg</name>
83+
</message>
8084
</service>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.zstack.header.identity;
2+
3+
import org.zstack.header.message.APIEvent;
4+
import org.zstack.header.rest.RestResponse;
5+
6+
@RestResponse(allTo = "inventory")
7+
public class APIChangeAccountTypeEvent extends APIEvent {
8+
private AccountInventory inventory;
9+
10+
public APIChangeAccountTypeEvent(String apiId) {
11+
super(apiId);
12+
}
13+
14+
public APIChangeAccountTypeEvent() {
15+
super(null);
16+
}
17+
18+
public AccountInventory getInventory() {
19+
return inventory;
20+
}
21+
22+
public void setInventory(AccountInventory inventory) {
23+
this.inventory = inventory;
24+
}
25+
26+
public static APIChangeAccountTypeEvent __example__() {
27+
APIChangeAccountTypeEvent event = new APIChangeAccountTypeEvent();
28+
29+
AccountInventory inventory = new AccountInventory();
30+
inventory.setName("test");
31+
inventory.setType(AccountType.SystemAdmin.toString());
32+
inventory.setUuid(uuid());
33+
34+
event.setInventory(inventory);
35+
return event;
36+
}
37+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.zstack.header.identity
2+
3+
import org.zstack.header.identity.APIChangeAccountTypeEvent
4+
5+
doc {
6+
title "ChangeAccountType"
7+
8+
field {
9+
name "success"
10+
desc ""
11+
type "boolean"
12+
since "5.0.0"
13+
}
14+
15+
ref {
16+
name "error"
17+
path "org.zstack.header.identity.APIChangeAccountTypeEvent.error"
18+
desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null", false
19+
type "ErrorCode"
20+
since "5.0.0"
21+
clz ErrorCode.class
22+
}
23+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.zstack.header.identity;
2+
3+
import org.springframework.http.HttpMethod;
4+
import org.zstack.header.message.APIMessage;
5+
import org.zstack.header.message.APIParam;
6+
import org.zstack.header.rest.RestRequest;
7+
8+
@Action(category = AccountConstant.ACTION_CATEGORY, adminOnly = true)
9+
@RestRequest(
10+
path = "/accounts/{uuid}/actions",
11+
method = HttpMethod.PUT,
12+
isAction = true,
13+
responseClass = APIChangeAccountTypeEvent.class
14+
)
15+
public class APIChangeAccountTypeMsg extends APIMessage implements AccountMessage {
16+
@APIParam(resourceType = AccountVO.class)
17+
private String uuid;
18+
19+
@APIParam
20+
private String type;
21+
22+
public String getUuid() {
23+
return uuid;
24+
}
25+
26+
public void setUuid(String uuid) {
27+
this.uuid = uuid;
28+
}
29+
30+
31+
@Override
32+
public String getAccountUuid() {
33+
return this.getSession().getAccountUuid();
34+
}
35+
36+
public static APIChangeAccountTypeMsg __example__() {
37+
APIChangeAccountTypeMsg msg = new APIChangeAccountTypeMsg();
38+
msg.setUuid(uuid());
39+
msg.setType(AccountType.SystemAdmin.toString());
40+
return msg;
41+
}
42+
43+
public String getType() {
44+
return type;
45+
}
46+
47+
public void setType(String type) {
48+
this.type = type;
49+
}
50+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.zstack.header.identity
2+
3+
import org.zstack.header.identity.APIChangeAccountTypeEvent
4+
5+
doc {
6+
title "ChangeAccountType"
7+
8+
category "identity"
9+
10+
desc """变更账户类型(提权/降权)"""
11+
12+
rest {
13+
request {
14+
url "PUT /v1/accounts/{uuid}/actions"
15+
16+
header (Authorization: 'OAuth the-session-uuid')
17+
18+
clz APIChangeAccountTypeMsg.class
19+
20+
desc """变更账户类型,支持将普通用户提升为管理员,或将管理员降级为普通用户(暂不支持)"""
21+
22+
params {
23+
24+
column {
25+
name "uuid"
26+
enclosedIn "changeAccountType"
27+
desc "资源的UUID,唯一标示该资源"
28+
location "url"
29+
type "String"
30+
optional false
31+
since "5.0.0"
32+
}
33+
column {
34+
name "type"
35+
enclosedIn "changeAccountType"
36+
desc "账户类型,SystemAdmin表示管理员,Normal表示普通用户(Normal暂不支持,即暂不支持降权)"
37+
location "body"
38+
type "String"
39+
optional false
40+
since "5.0.0"
41+
}
42+
column {
43+
name "systemTags"
44+
enclosedIn ""
45+
desc "系统标签"
46+
location "body"
47+
type "List"
48+
optional true
49+
since "5.0.0"
50+
}
51+
column {
52+
name "userTags"
53+
enclosedIn ""
54+
desc "用户标签"
55+
location "body"
56+
type "List"
57+
optional true
58+
since "5.0.0"
59+
}
60+
}
61+
}
62+
63+
response {
64+
clz APIChangeAccountTypeEvent.class
65+
}
66+
}
67+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.zstack.header.identity;
2+
3+
import org.zstack.header.errorcode.ErrorCode;
4+
5+
/**
6+
* Extension point for account type change operations.
7+
* Plugins can implement this interface to perform custom logic
8+
* when an account type is changed (e.g., promote to admin or demote to normal).
9+
*/
10+
public interface AccountTypeChangedExtensionPoint {
11+
ErrorCode preAccountTypeChange(String accountUuid, AccountType oldType, AccountType newType);
12+
13+
void beforeAccountTypeChange(String accountUuid, AccountType oldType, AccountType newType);
14+
15+
void afterAccountTypeChange(String accountUuid, AccountType newType);
16+
}

identity/src/main/java/org/zstack/identity/AccountBase.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.zstack.header.message.APIMessage;
2828
import org.zstack.header.message.Message;
2929
import org.zstack.header.message.MessageReply;
30+
import static org.zstack.core.Platform.argerr;
3031
import org.zstack.utils.CollectionUtils;
3132
import org.zstack.utils.Utils;
3233
import org.zstack.utils.logging.CLogger;
@@ -316,11 +317,42 @@ private void handleApiMessage(APIMessage msg) {
316317
handle((APIDeleteAccountMsg) msg);
317318
} else if (msg instanceof APIGetAccountQuotaUsageMsg) {
318319
handle((APIGetAccountQuotaUsageMsg) msg);
320+
} else if (msg instanceof APIChangeAccountTypeMsg) {
321+
handle((APIChangeAccountTypeMsg) msg);
319322
} else {
320323
bus.dealWithUnknownMessage(msg);
321324
}
322325
}
323326

327+
private void handle(APIChangeAccountTypeMsg msg) {
328+
APIChangeAccountTypeEvent evt = new APIChangeAccountTypeEvent(msg.getId());
329+
String accountUuid = msg.getUuid();
330+
AccountVO account = dbf.findByUuid(accountUuid, AccountVO.class);
331+
if (account == null) {
332+
evt.setError(argerr("Cannot find account[uuid:%s]", accountUuid));
333+
bus.publish(evt);
334+
return;
335+
}
336+
AccountType originalAccountType = account.getType();
337+
AccountType targetAccountType = AccountType.valueOf(msg.getType());
338+
List<AccountTypeChangedExtensionPoint> extensions = pluginRgty.getExtensionList(AccountTypeChangedExtensionPoint.class);
339+
for (AccountTypeChangedExtensionPoint extension : extensions) {
340+
ErrorCode errorCode = extension.preAccountTypeChange(accountUuid, originalAccountType, targetAccountType);
341+
if (errorCode != null) {
342+
evt.setError(errorCode);
343+
bus.publish(evt);
344+
return;
345+
}
346+
}
347+
CollectionUtils.forEach(extensions, ext -> ext.beforeAccountTypeChange(accountUuid, originalAccountType, targetAccountType));
348+
349+
account.setType(targetAccountType);
350+
account = dbf.updateAndRefresh(account);
351+
CollectionUtils.forEach(extensions, ext -> ext.afterAccountTypeChange(accountUuid, targetAccountType));
352+
evt.setInventory(AccountInventory.valueOf(account));
353+
bus.publish(evt);
354+
}
355+
324356
private void handle(APIGetAccountQuotaUsageMsg msg) {
325357
APIGetAccountQuotaUsageReply reply = new APIGetAccountQuotaUsageReply();
326358

identity/src/main/java/org/zstack/identity/AccountManagerImpl.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,13 +971,48 @@ public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionExcepti
971971
validate((APIGetAccountQuotaUsageMsg) msg);
972972
} else if (msg instanceof APIUpdateQuotaMsg) {
973973
validate((APIUpdateQuotaMsg) msg);
974+
} else if (msg instanceof APIChangeAccountTypeMsg) {
975+
validate((APIChangeAccountTypeMsg) msg);
974976
}
975977

976978
setServiceId(msg);
977979

978980
return msg;
979981
}
980982

983+
private void validate(APIChangeAccountTypeMsg msg) {
984+
if (!AccountConstant.INITIAL_SYSTEM_ADMIN_UUID.equals(msg.getSession().getAccountUuid())) {
985+
throw new ApiMessageInterceptionException(argerr(
986+
"Only builtin admin account can change account type."
987+
));
988+
}
989+
990+
AccountVO account = dbf.findByUuid(msg.getUuid(), AccountVO.class);
991+
if (account == null) {
992+
throw new ApiMessageInterceptionException(argerr(
993+
"Cannot find account[uuid:%s]", msg.getUuid()
994+
));
995+
}
996+
997+
if (AccountConstant.INITIAL_SYSTEM_ADMIN_UUID.equals(msg.getUuid())) {
998+
throw new ApiMessageInterceptionException(argerr(
999+
"Cannot change type of builtin admin account."
1000+
));
1001+
}
1002+
1003+
if (account.getType().toString().equals(msg.getType())) {
1004+
throw new ApiMessageInterceptionException(argerr(
1005+
"Account[uuid:%s] is already %s.", msg.getUuid(), msg.getType()
1006+
));
1007+
}
1008+
1009+
if (!AccountType.SystemAdmin.toString().equals(msg.getType())) {
1010+
throw new ApiMessageInterceptionException(argerr(
1011+
"Only promoting to SystemAdmin is currently supported, got type[%s].", msg.getType()
1012+
));
1013+
}
1014+
}
1015+
9811016
private void validate(APIGetAccountQuotaUsageMsg msg) {
9821017
if (msg.getUuid() == null) {
9831018
msg.setUuid(msg.getSession().getAccountUuid());

0 commit comments

Comments
 (0)