forked from apache/cloudstack
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathDefaultLoginAPIAuthenticatorCmd.java
More file actions
195 lines (172 loc) · 9.14 KB
/
DefaultLoginAPIAuthenticatorCmd.java
File metadata and controls
195 lines (172 loc) · 9.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// 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.api.auth;
import com.cloud.api.ApiServlet;
import com.cloud.domain.Domain;
import com.cloud.user.User;
import com.cloud.user.UserAccount;
import org.apache.cloudstack.api.ApiServerService;
import com.cloud.api.response.ApiResponseSerializer;
import com.cloud.exception.CloudAuthenticationException;
import com.cloud.user.Account;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.auth.APIAuthenticationType;
import org.apache.cloudstack.api.auth.APIAuthenticator;
import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
import org.apache.cloudstack.api.response.LoginCmdResponse;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
import java.net.InetAddress;
@APICommand(name = "login", description = "Logs a user into the CloudStack. A successful login attempt will generate a JSESSIONID cookie value that can be passed in subsequent Query command calls until the \"logout\" command has been issued or the session has expired.", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {})
public class DefaultLoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator {
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "Username", required = true)
private String username;
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "Hashed password (Default is MD5). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.", required = true)
private String password;
@Parameter(name = ApiConstants.DOMAIN, type = CommandType.STRING, description = "Path of the domain that the user belongs to. Example: domain=/com/cloud/internal. If no domain is passed in, the ROOT (/) domain is assumed.")
private String domain;
@Parameter(name = ApiConstants.DOMAIN__ID, type = CommandType.LONG, description = "The id of the domain that the user belongs to. If both domain and domainId are passed in, \"domainId\" parameter takes precedence.")
private Long domainId;
@Inject
ApiServerService _apiServer;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getDomainName() {
return domain;
}
public Long getDomainId() {
return domainId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public long getEntityOwnerId() {
return Account.Type.NORMAL.ordinal();
}
@Override
public void execute() throws ServerApiException {
// We should never reach here
throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly");
}
@Override
public String authenticate(String command, Map<String, Object[]> params, HttpSession session, InetAddress remoteAddress, String responseType, StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException {
// Disallow non POST requests
if (HTTPMethod.valueOf(req.getMethod()) != HTTPMethod.POST) {
throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "Please use HTTP POST to authenticate using this API");
}
// FIXME: ported from ApiServlet, refactor and cleanup
final String[] username = (String[])params.get(ApiConstants.USERNAME);
final String[] password = (String[])params.get(ApiConstants.PASSWORD);
final String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
Long domainId = null;
if (domainIdArr != null && domainIdArr.length > 0) {
try {
//check if UUID is passed in for domain
domainId = _apiServer.fetchDomainId(domainIdArr[0]);
if (domainId == null) {
domainId = Long.parseLong(domainIdArr[0]);
}
auditTrailSb.append(" domainid=" + domainId);// building the params for POST call
} catch (final NumberFormatException e) {
logger.warn("Invalid domain id entered by user");
auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "Invalid domain id entered, please enter a valid one");
throw new ServerApiException(ApiErrorCode.UNAUTHORIZED,
_apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid domain id entered, please enter a valid one", params,
responseType));
}
}
String domain = null;
final String[] domainName = (String[])params.get(ApiConstants.DOMAIN);
domain = getDomainName(auditTrailSb, domainName, domain);
String serializedResponse = null;
if (username != null) {
final String pwd = ((password == null) ? null : password[0]);
try {
final Domain userDomain = _domainService.findDomainByIdOrPath(domainId, domain);
if (userDomain != null) {
domainId = userDomain.getId();
} else {
throw new CloudAuthenticationException("Unable to find the domain from the path " + domain);
}
final UserAccount userAccount = _accountService.getActiveUserAccount(username[0], domainId);
if (userAccount != null && User.Source.SAML2 == userAccount.getSource()) {
throw new CloudAuthenticationException("User is not allowed CloudStack login");
}
return ApiResponseSerializer.toSerializedString(_apiServer.loginUser(session, username[0], pwd, domainId, domain, remoteAddress, params),
responseType);
} catch (final CloudAuthenticationException ex) {
ApiServlet.invalidateHttpSession(session, "fall through to API key,");
// TODO: fall through to API key, or just fail here w/ auth error? (HTTP 401)
String msg = String.format("%s", ex.getMessage() != null ?
ex.getMessage() :
"failed to authenticate user, check if username/password are correct");
auditTrailSb.append(" " + ApiErrorCode.ACCOUNT_ERROR + " " + msg);
serializedResponse = _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), msg, params, responseType);
if (logger.isTraceEnabled()) {
logger.trace(msg);
}
}
}
// We should not reach here and if we do we throw an exception
throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, serializedResponse);
}
@Nullable
private String getDomainName(StringBuilder auditTrailSb, String[] domainName, String domain) {
if (domainName != null) {
domain = domainName[0];
auditTrailSb.append(" domain=" + domain);
if (domain != null) {
// ensure domain starts with '/' and ends with '/'
if (!domain.endsWith("/")) {
domain += '/';
}
if (!domain.startsWith("/")) {
domain = "/" + domain;
}
}
}
return domain;
}
@Override
public APIAuthenticationType getAPIType() {
return APIAuthenticationType.LOGIN_API;
}
@Override
public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
}
}