Skip to content

Commit 553e419

Browse files
authored
Merge branch 'main' into nas-backup-failed
2 parents 3ed133c + c2c855b commit 553e419

31 files changed

Lines changed: 2043 additions & 151 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,8 +1343,10 @@ public class ApiConstants {
13431343
public static final String VNF_CONFIGURE_MANAGEMENT = "vnfconfiguremanagement";
13441344
public static final String VNF_CIDR_LIST = "vnfcidrlist";
13451345

1346+
public static final String AUTHORIZE_URL = "authorizeurl";
13461347
public static final String CLIENT_ID = "clientid";
13471348
public static final String REDIRECT_URI = "redirecturi";
1349+
public static final String TOKEN_URL = "tokenurl";
13481350

13491351
public static final String IS_TAG_A_RULE = "istagarule";
13501352

api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public class RegisterUserKeysCmd extends BaseAsyncCmd {
5050
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "API key pair name.")
5151
private String name;
5252

53-
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "API key pair description.")
53+
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "API key pair description.", length = 1024)
5454
private String description;
5555

5656
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date of the API key pair. " +
@@ -138,6 +138,9 @@ public List<Map<String, Object>> getRules() {
138138

139139
String description = detail.get(ApiConstants.DESCRIPTION);
140140
if (StringUtils.isNotEmpty(description)) {
141+
if (description.length() > 255) {
142+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Rule description cannot be longer than 255 characters.");
143+
}
141144
ruleDetails.put(ApiConstants.DESCRIPTION, description);
142145
}
143146

engine/schema/src/main/java/org/apache/cloudstack/acl/ApiKeyPairVO.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public class ApiKeyPairVO implements ApiKeyPair {
7070
@Temporal(value = TemporalType.TIMESTAMP)
7171
private Date created = Date.from(Instant.now());
7272

73-
@Column(name = "description")
73+
@Column(name = "description", length = 1024)
7474
private String description = "";
7575

7676
@Column(name = "api_key", nullable = false)

engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ CREATE TABLE IF NOT EXISTS `cloud`.`api_keypair` (
7373
`user_id` bigint(20) unsigned NOT NULL,
7474
`start_date` datetime,
7575
`end_date` datetime,
76-
`description` varchar(100),
76+
`description` varchar(1024),
7777
`api_key` varchar(255) NOT NULL,
7878
`secret_key` varchar(255) NOT NULL,
7979
`created` datetime NOT NULL,
@@ -107,17 +107,25 @@ WHERE user.api_key IS NOT NULL AND user.secret_key IS NOT NULL;
107107
-- Drop API keys from user table
108108
ALTER TABLE `cloud`.`user` DROP COLUMN api_key, DROP COLUMN secret_key;
109109

110-
-- Grant access to the "deleteUserKeys" API to the "User", "Domain Admin" and "Resource Admin" roles, similarly to the "registerUserKeys" API
110+
-- Grant access to the "deleteUserKeys" and "listUserKeyRules" APIs to the "User", "Domain Admin" and "Resource Admin" roles, similarly to the "registerUserKeys" API
111111
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('User', 'deleteUserKeys', 'ALLOW');
112112
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Domain Admin', 'deleteUserKeys', 'ALLOW');
113113
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'deleteUserKeys', 'ALLOW');
114114

115+
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('User', 'listUserKeyRules', 'ALLOW');
116+
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Domain Admin', 'listUserKeyRules', 'ALLOW');
117+
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'listUserKeyRules', 'ALLOW');
118+
115119
-- Add conserve mode for VPC offerings
116120
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tinyint(1) unsigned NULL DEFAULT 0 COMMENT ''True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers'' ');
117121

118122
--- Disable/enable NICs
119123
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
120124

125+
--- Add URLs for OAuth provider
126+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.oauth_provider','authorize_url', 'VARCHAR(255) DEFAULT NULL COMMENT ''Authorize URL for OAuth initialization'' ');
127+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.oauth_provider','token_url', 'VARCHAR(255) DEFAULT NULL COMMENT ''Token URL for OAuth finalization'' ');
128+
121129
--- Quota tariff/usage mapping
122130
CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
123131
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

plugins/user-authenticators/oauth2/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
<artifactId>cloud-framework-config</artifactId>
3939
<version>${project.version}</version>
4040
</dependency>
41+
<dependency>
42+
<groupId>org.apache.cxf</groupId>
43+
<artifactId>cxf-rt-rs-security-jose</artifactId>
44+
<version>${cs.cxf.version}</version>
45+
</dependency>
4146
<dependency>
4247
<groupId>com.google.apis</groupId>
4348
<artifactId>google-api-services-docs</artifactId>

plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818
//
1919
package org.apache.cloudstack.oauth2;
2020

21-
import com.cloud.user.dao.UserDao;
22-
import com.cloud.utils.component.Manager;
23-
import com.cloud.utils.component.ManagerBase;
24-
import com.cloud.utils.exception.CloudRuntimeException;
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
import javax.inject.Inject;
28+
2529
import org.apache.cloudstack.auth.UserOAuth2Authenticator;
2630
import org.apache.cloudstack.framework.config.ConfigKey;
2731
import org.apache.cloudstack.framework.config.Configurable;
@@ -35,16 +39,11 @@
3539
import org.apache.cloudstack.oauth2.vo.OauthProviderVO;
3640
import org.apache.commons.lang3.StringUtils;
3741

38-
import javax.inject.Inject;
39-
import java.util.ArrayList;
40-
import java.util.Collections;
41-
import java.util.HashMap;
42-
import java.util.List;
43-
import java.util.Map;
42+
import com.cloud.utils.component.Manager;
43+
import com.cloud.utils.component.ManagerBase;
44+
import com.cloud.utils.exception.CloudRuntimeException;
4445

4546
public class OAuth2AuthManagerImpl extends ManagerBase implements OAuth2AuthManager, Manager, Configurable {
46-
@Inject
47-
private UserDao _userDao;
4847

4948
@Inject
5049
protected OauthProviderDao _oauthProviderDao;
@@ -55,7 +54,7 @@ public class OAuth2AuthManagerImpl extends ManagerBase implements OAuth2AuthMana
5554

5655
@Override
5756
public List<Class<?>> getAuthCommands() {
58-
List<Class<?>> cmdList = new ArrayList<Class<?>>();
57+
List<Class<?>> cmdList = new ArrayList<>();
5958
cmdList.add(OauthLoginAPIAuthenticatorCmd.class);
6059
cmdList.add(ListOAuthProvidersCmd.class);
6160
cmdList.add(VerifyOAuthCodeAndGetUserCmd.class);
@@ -84,7 +83,7 @@ public boolean stop() {
8483

8584
@Override
8685
public List<Class<?>> getCommands() {
87-
List<Class<?>> cmdList = new ArrayList<Class<?>>();
86+
List<Class<?>> cmdList = new ArrayList<>();
8887
cmdList.add(RegisterOAuthProviderCmd.class);
8988
cmdList.add(DeleteOAuthProviderCmd.class);
9089
cmdList.add(UpdateOAuthProviderCmd.class);
@@ -127,9 +126,7 @@ protected void initializeUserOAuth2AuthenticationProvidersMap() {
127126
@Override
128127
public String verifyCodeAndFetchEmail(String code, String provider) {
129128
UserOAuth2Authenticator authenticator = getUserOAuth2AuthenticationProvider(provider);
130-
String email = authenticator.verifyCodeAndFetchEmail(code);
131-
132-
return email;
129+
return authenticator.verifyCodeAndFetchEmail(code);
133130
}
134131

135132
@Override
@@ -139,6 +136,8 @@ public OauthProviderVO registerOauthProvider(RegisterOAuthProviderCmd cmd) {
139136
String clientId = StringUtils.trim(cmd.getClientId());
140137
String redirectUri = StringUtils.trim(cmd.getRedirectUri());
141138
String secretKey = StringUtils.trim(cmd.getSecretKey());
139+
String authorizeUrl = StringUtils.trim(cmd.getAuthorizeUrl());
140+
String tokenUrl = StringUtils.trim(cmd.getTokenUrl());
142141

143142
if (!isOAuthPluginEnabled()) {
144143
throw new CloudRuntimeException("OAuth is not enabled, please enable to register");
@@ -148,7 +147,7 @@ public OauthProviderVO registerOauthProvider(RegisterOAuthProviderCmd cmd) {
148147
throw new CloudRuntimeException(String.format("Provider with the name %s is already registered", provider));
149148
}
150149

151-
return saveOauthProvider(provider, description, clientId, secretKey, redirectUri);
150+
return saveOauthProvider(provider, description, clientId, secretKey, redirectUri, authorizeUrl, tokenUrl);
152151
}
153152

154153
@Override
@@ -171,6 +170,8 @@ public OauthProviderVO updateOauthProvider(UpdateOAuthProviderCmd cmd) {
171170
String clientId = StringUtils.trim(cmd.getClientId());
172171
String redirectUri = StringUtils.trim(cmd.getRedirectUri());
173172
String secretKey = StringUtils.trim(cmd.getSecretKey());
173+
String authorizeUrl = StringUtils.trim(cmd.getAuthorizeUrl());
174+
String tokenUrl = StringUtils.trim(cmd.getTokenUrl());
174175
Boolean enabled = cmd.getEnabled();
175176

176177
OauthProviderVO providerVO = _oauthProviderDao.findById(id);
@@ -190,6 +191,12 @@ public OauthProviderVO updateOauthProvider(UpdateOAuthProviderCmd cmd) {
190191
if (StringUtils.isNotEmpty(secretKey)) {
191192
providerVO.setSecretKey(secretKey);
192193
}
194+
if (StringUtils.isNotEmpty(authorizeUrl)) {
195+
providerVO.setAuthorizeUrl(authorizeUrl);
196+
}
197+
if (StringUtils.isNotEmpty(tokenUrl)) {
198+
providerVO.setTokenUrl(tokenUrl);
199+
}
193200
if (enabled != null) {
194201
providerVO.setEnabled(enabled);
195202
}
@@ -199,14 +206,16 @@ public OauthProviderVO updateOauthProvider(UpdateOAuthProviderCmd cmd) {
199206
return _oauthProviderDao.findById(id);
200207
}
201208

202-
private OauthProviderVO saveOauthProvider(String provider, String description, String clientId, String secretKey, String redirectUri) {
209+
private OauthProviderVO saveOauthProvider(String provider, String description, String clientId, String secretKey, String redirectUri, String authorizeUrl, String tokenUrl) {
203210
final OauthProviderVO oauthProviderVO = new OauthProviderVO();
204211

205212
oauthProviderVO.setProvider(provider);
206213
oauthProviderVO.setDescription(description);
207214
oauthProviderVO.setClientId(clientId);
208215
oauthProviderVO.setSecretKey(secretKey);
209216
oauthProviderVO.setRedirectUri(redirectUri);
217+
oauthProviderVO.setAuthorizeUrl(authorizeUrl);
218+
oauthProviderVO.setTokenUrl(tokenUrl);
210219
oauthProviderVO.setEnabled(true);
211220

212221
_oauthProviderDao.persist(oauthProviderVO);

plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/ListOAuthProvidersCmd.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24-
import com.cloud.api.response.ApiResponseSerializer;
25-
import com.cloud.user.Account;
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
import javax.servlet.http.HttpSession;
27+
2628
import org.apache.cloudstack.acl.RoleType;
2729
import org.apache.cloudstack.api.APICommand;
2830
import org.apache.cloudstack.api.ApiConstants;
@@ -40,9 +42,8 @@
4042
import org.apache.cloudstack.oauth2.vo.OauthProviderVO;
4143
import org.apache.commons.lang.ArrayUtils;
4244

43-
import javax.servlet.http.HttpServletRequest;
44-
import javax.servlet.http.HttpServletResponse;
45-
import javax.servlet.http.HttpSession;
45+
import com.cloud.api.response.ApiResponseSerializer;
46+
import com.cloud.user.Account;
4647

4748
@APICommand(name = "listOauthProvider", description = "List OAuth providers registered", responseObject = OauthProviderResponse.class, entityType = {},
4849
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
@@ -108,7 +109,7 @@ public String authenticate(String command, Map<String, Object[]> params, HttpSes
108109
List<OauthProviderResponse> responses = new ArrayList<>();
109110
for (OauthProviderVO result : resultList) {
110111
OauthProviderResponse r = new OauthProviderResponse(result.getUuid(), result.getProvider(),
111-
result.getDescription(), result.getClientId(), result.getSecretKey(), result.getRedirectUri());
112+
result.getDescription(), result.getClientId(), result.getSecretKey(), result.getRedirectUri(), result.getAuthorizeUrl(), result.getTokenUrl());
112113
if (OAuth2AuthManager.OAuth2IsPluginEnabled.value() && authenticatorPluginNames.contains(result.getProvider()) && result.isEnabled()) {
113114
r.setEnabled(true);
114115
} else {

plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/RegisterOAuthProviderCmd.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,29 @@
1414
// limitations under the License.
1515
package org.apache.cloudstack.oauth2.api.command;
1616

17+
import java.util.Collection;
18+
import java.util.Map;
19+
1720
import javax.inject.Inject;
1821
import javax.persistence.EntityExistsException;
1922

20-
import org.apache.cloudstack.api.response.SuccessResponse;
21-
import org.apache.cloudstack.oauth2.OAuth2AuthManager;
22-
import org.apache.cloudstack.oauth2.api.response.OauthProviderResponse;
23-
import org.apache.cloudstack.oauth2.vo.OauthProviderVO;
24-
import org.apache.commons.collections.MapUtils;
2523
import org.apache.cloudstack.api.APICommand;
2624
import org.apache.cloudstack.api.ApiConstants;
25+
import org.apache.cloudstack.api.ApiErrorCode;
2726
import org.apache.cloudstack.api.BaseCmd;
2827
import org.apache.cloudstack.api.Parameter;
2928
import org.apache.cloudstack.api.ServerApiException;
29+
import org.apache.cloudstack.api.response.SuccessResponse;
3030
import org.apache.cloudstack.context.CallContext;
31+
import org.apache.cloudstack.oauth2.OAuth2AuthManager;
32+
import org.apache.cloudstack.oauth2.api.response.OauthProviderResponse;
33+
import org.apache.cloudstack.oauth2.keycloak.KeycloakOAuth2Provider;
34+
import org.apache.cloudstack.oauth2.vo.OauthProviderVO;
35+
import org.apache.commons.collections.MapUtils;
36+
import org.apache.commons.lang3.StringUtils;
3137

3238
import com.cloud.exception.ConcurrentOperationException;
3339

34-
import java.util.Collection;
35-
import java.util.Map;
36-
3740
@APICommand(name = "registerOauthProvider", responseObject = SuccessResponse.class, description = "Register the OAuth2 provider in CloudStack", since = "4.19.0")
3841
public class RegisterOAuthProviderCmd extends BaseCmd {
3942

@@ -56,6 +59,12 @@ public class RegisterOAuthProviderCmd extends BaseCmd {
5659
@Parameter(name = ApiConstants.REDIRECT_URI, type = CommandType.STRING, description = "Redirect URI pre-registered in the specific OAuth provider", required = true)
5760
private String redirectUri;
5861

62+
@Parameter(name = ApiConstants.AUTHORIZE_URL, type = CommandType.STRING, description = "Authorize URL for OAuth initialization (only required for keycloak provider)")
63+
private String authorizeUrl;
64+
65+
@Parameter(name = ApiConstants.TOKEN_URL, type = CommandType.STRING, description = "Token URL for OAuth finalization (only required for keycloak provider)")
66+
private String tokenUrl;
67+
5968
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP,
6069
description = "Any OAuth provider details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].clientsecret=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")
6170
protected Map details;
@@ -85,6 +94,14 @@ public String getRedirectUri() {
8594
return redirectUri;
8695
}
8796

97+
public String getAuthorizeUrl() {
98+
return authorizeUrl;
99+
}
100+
101+
public String getTokenUrl() {
102+
return tokenUrl;
103+
}
104+
88105
public Map getDetails() {
89106
if (MapUtils.isEmpty(details)) {
90107
return null;
@@ -98,10 +115,20 @@ public Map getDetails() {
98115

99116
@Override
100117
public void execute() throws ServerApiException, ConcurrentOperationException, EntityExistsException {
118+
if (StringUtils.equals(KeycloakOAuth2Provider.KEYCLOAK_PROVIDER, getProvider())) {
119+
if (StringUtils.isBlank(getAuthorizeUrl())) {
120+
throw new ServerApiException(ApiErrorCode.BAD_REQUEST, "Parameter authorizeurl is mandatory for keycloak OAuth Provider");
121+
}
122+
if (StringUtils.isBlank(getTokenUrl())) {
123+
throw new ServerApiException(ApiErrorCode.BAD_REQUEST, "Parameter tokenurl is mandatory for keycloak OAuth Provider");
124+
}
125+
}
126+
101127
OauthProviderVO provider = _oauth2mgr.registerOauthProvider(this);
102128

103129
OauthProviderResponse response = new OauthProviderResponse(provider.getUuid(), provider.getProvider(),
104-
provider.getDescription(), provider.getClientId(), provider.getSecretKey(), provider.getRedirectUri());
130+
provider.getDescription(), provider.getClientId(), provider.getSecretKey(), provider.getRedirectUri(),
131+
provider.getAuthorizeUrl(), provider.getTokenUrl());
105132
response.setResponseName(getCommandName());
106133
response.setObjectName(ApiConstants.OAUTH_PROVIDER);
107134
setResponseObject(response);

0 commit comments

Comments
 (0)