Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
public enum ApiErrorCode {

BAD_REQUEST(400),
UNAUTHORIZED(401),
UNAUTHORIZED2FA(511),
METHOD_NOT_ALLOWED(405),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ public ResponseObject loginUser(HttpSession session, String username, String pas
boolean forgotPassword(UserAccount userAccount, Domain domain);

boolean resetPassword(UserAccount userAccount, String token, String password);

boolean isPostRequestsAndTimestampsEnforced();
}
26 changes: 25 additions & 1 deletion server/src/main/java/com/cloud/api/ApiServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
private static final String SANITIZATION_REGEX = "[\n\r]";

private static boolean encodeApiResponse = false;
private boolean isPostRequestsAndTimestampsEnforced = false;

/**
* Non-printable ASCII characters - numbers 0 to 31 and 127 decimal
Expand Down Expand Up @@ -284,6 +285,13 @@
, "Session cookie is marked as secure if this is enabled. Secure cookies only work when HTTPS is used."
, false
, ConfigKey.Scope.Global);
static final ConfigKey<Boolean> EnforcePostRequestsAndTimestamps = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED
, Boolean.class
, "enforce.post.requests.and.timestamps"
, "false"
, "Enable/Disable whether the ApiServer should only accept POST requests for state-changing APIs and requests with timestamps."
, false
, ConfigKey.Scope.Global);
private static final ConfigKey<String> JSONDefaultContentType = new ConfigKey<> (ConfigKey.CATEGORY_ADVANCED
, String.class
, "json.content.type"
Expand Down Expand Up @@ -441,6 +449,7 @@
public boolean start() {
Security.addProvider(new BouncyCastleProvider());
Integer apiPort = IntegrationAPIPort.value(); // api port, null by default
isPostRequestsAndTimestampsEnforced = EnforcePostRequestsAndTimestamps.value();

Check warning on line 452 in server/src/main/java/com/cloud/api/ApiServer.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServer.java#L452

Added line #L452 was not covered by tests
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the configuration 'enforce.post.requests.and.timestamps' might change at runtime, consider updating isPostRequestsAndTimestampsEnforced dynamically rather than only at startup.

Suggested change
isPostRequestsAndTimestampsEnforced = EnforcePostRequestsAndTimestamps.value();
// Removed caching of isPostRequestsAndTimestampsEnforced to ensure dynamic updates.

Copilot uses AI. Check for mistakes.

final Long snapshotLimit = ConcurrentSnapshotsThresholdPerHost.value();
if (snapshotLimit == null || snapshotLimit <= 0) {
Expand Down Expand Up @@ -720,6 +729,11 @@
return response;
}

@Override
public boolean isPostRequestsAndTimestampsEnforced() {
return isPostRequestsAndTimestampsEnforced;
}

Check warning on line 735 in server/src/main/java/com/cloud/api/ApiServer.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServer.java#L733-L735

Added lines #L733 - L735 were not covered by tests

private String getBaseAsyncResponse(final long jobId, final BaseAsyncCmd cmd) {
final AsyncJobResponse response = new AsyncJobResponse();

Expand Down Expand Up @@ -967,7 +981,6 @@

// put the name in a list that we'll sort later
final List<String> parameterNames = new ArrayList<>(requestParameters.keySet());

Collections.sort(parameterNames);

String signatureVersion = null;
Expand Down Expand Up @@ -1019,12 +1032,22 @@
}

final Date now = new Date(System.currentTimeMillis());
final Date thresholdTime = new Date(now.getTime() + 15 * 60 * 1000);

Check warning on line 1035 in server/src/main/java/com/cloud/api/ApiServer.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServer.java#L1035

Added line #L1035 was not covered by tests
if (expiresTS.before(now)) {
signature = signature.replaceAll(SANITIZATION_REGEX, "_");
apiKey = apiKey.replaceAll(SANITIZATION_REGEX, "_");
logger.debug("Request expired -- ignoring ...sig [{}], apiKey [{}].", signature, apiKey);
return false;
} else if (isPostRequestsAndTimestampsEnforced && expiresTS.after(thresholdTime)) {
signature = signature.replaceAll(SANITIZATION_REGEX, "_");
apiKey = apiKey.replaceAll(SANITIZATION_REGEX, "_");
logger.debug(String.format("Expiration parameter is set for too long -- ignoring ...sig [%s], apiKey [%s].", signature, apiKey));
return false;

Check warning on line 1045 in server/src/main/java/com/cloud/api/ApiServer.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServer.java#L1042-L1045

Added lines #L1042 - L1045 were not covered by tests
}
} else if (isPostRequestsAndTimestampsEnforced) {
// Force expiration parameter
logger.debug("Signature Version must be 3, and should be along with the Expires parameter -- ignoring request.");
return false;

Check warning on line 1050 in server/src/main/java/com/cloud/api/ApiServer.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServer.java#L1049-L1050

Added lines #L1049 - L1050 were not covered by tests
}

final TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB);
Expand Down Expand Up @@ -1648,6 +1671,7 @@
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {
EnforcePostRequestsAndTimestamps,
IntegrationAPIPort,
ConcurrentSnapshotsThresholdPerHost,
EncodeApiResponse,
Expand Down
115 changes: 113 additions & 2 deletions server/src/main/java/com/cloud/api/ApiServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.Set;

import javax.inject.Inject;
import javax.servlet.ServletConfig;
Expand All @@ -46,6 +49,7 @@
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.managed.context.ManagedContext;
import org.apache.cloudstack.utils.consoleproxy.ConsoleAccessUtils;
import org.apache.commons.collections.MapUtils;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -78,6 +82,39 @@
private static final Logger ACCESSLOGGER = LogManager.getLogger("apiserver." + ApiServlet.class.getName());
private static final String REPLACEMENT = "_";
private static final String LOGGER_REPLACEMENTS = "[\n\r\t]";
private static final Pattern GET_REQUEST_COMMANDS = Pattern.compile("^(get|list|query|find)(\\w+)+$");
private static final HashSet<String> GET_REQUEST_COMMANDS_LIST = new HashSet<>(Set.of("isaccountallowedtocreateofferingswithtags",
"readyforshutdown", "cloudianisenabled", "quotabalance", "quotasummary", "quotatarifflist", "quotaisenabled", "quotastatement", "verifyoauthcodeandgetuser"));
private static final HashSet<String> POST_REQUESTS_TO_DISABLE_LOGGING = new HashSet<>(Set.of(
"login",
"oauthlogin",
"createaccount",
"createuser",
"updateuser",
"forgotpassword",
"resetpassword",
"importrole",
"updaterolepermission",
"updateprojectrolepermission",
"createstoragepool",
"addhost",
"updatehostpassword",
"addcluster",
"addvmwaredc",
"configureoutofbandmanagement",
"uploadcustomcertificate",
"addciscovnmcresource",
"addnetscalerloadbalancer",
"createtungstenfabricprovider",
"addnsxcontroller",
"configtungstenfabricservice",
"createnetworkacl",
"updatenetworkaclitem",
"quotavalidateactivationrule",
"quotatariffupdate",
"listandswitchsamlaccount",
"uploadresourceicon"
));

@Inject
ApiServerService apiServer;
Expand Down Expand Up @@ -193,11 +230,24 @@

utf8Fixup(req, params);

final Object[] commandObj = params.get(ApiConstants.COMMAND);
final String command = commandObj == null ? null : (String) commandObj[0];

// logging the request start and end in management log for easy debugging
String reqStr = "";
String cleanQueryString = StringUtils.cleanString(req.getQueryString());
if (LOGGER.isDebugEnabled()) {
reqStr = auditTrailSb.toString() + " " + cleanQueryString;
if (req.getMethod().equalsIgnoreCase("POST") && org.apache.commons.lang3.StringUtils.isNotBlank(command)) {
if (!POST_REQUESTS_TO_DISABLE_LOGGING.contains(command.toLowerCase()) && !reqParams.containsKey(ApiConstants.USER_DATA)) {
String cleanParamsString = getCleanParamsString(reqParams);

Check warning on line 243 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L243

Added line #L243 was not covered by tests
if (org.apache.commons.lang3.StringUtils.isNotBlank(cleanParamsString)) {
reqStr += "\n" + cleanParamsString;

Check warning on line 245 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L245

Added line #L245 was not covered by tests
}
} else {
reqStr += " " + command;

Check warning on line 248 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L247-L248

Added lines #L247 - L248 were not covered by tests
}
}
LOGGER.debug("===START=== " + reqStr);
}

Expand All @@ -213,8 +263,6 @@
responseType = (String)responseTypeParam[0];
}

final Object[] commandObj = params.get(ApiConstants.COMMAND);
final String command = commandObj == null ? null : (String) commandObj[0];
final Object[] userObj = params.get(ApiConstants.USERNAME);
String username = userObj == null ? null : (String)userObj[0];
if (LOGGER.isTraceEnabled()) {
Expand Down Expand Up @@ -317,6 +365,19 @@
}
}

if (apiServer.isPostRequestsAndTimestampsEnforced() && !isStateChangingCommandUsingPOST(command, req.getMethod(), params)) {
String errorText = String.format("State changing command %s needs to be sent using POST request", command);

Check warning on line 369 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L369

Added line #L369 was not covered by tests
if (command.equalsIgnoreCase("updateConfiguration") && params.containsKey("name")) {
errorText = String.format("Changes for configuration %s needs to be sent using POST request", params.get("name")[0]);

Check warning on line 371 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L371

Added line #L371 was not covered by tests
}
auditTrailSb.append(" " + HttpServletResponse.SC_BAD_REQUEST + " " + errorText);
final String serializedResponse =
apiServer.getSerializedApiError(new ServerApiException(ApiErrorCode.BAD_REQUEST, errorText), params,

Check warning on line 375 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L373-L375

Added lines #L373 - L375 were not covered by tests
responseType);
HttpUtils.writeHttpResponse(resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType, ApiServer.JSONcontentType.value());
return;

Check warning on line 378 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L377-L378

Added lines #L377 - L378 were not covered by tests
}

Long userId = null;
if (!isNew) {
userId = (Long)session.getAttribute("userid");
Expand Down Expand Up @@ -407,6 +468,15 @@
return verify2FA;
}

private boolean isStateChangingCommandUsingPOST(String command, String method, Map<String, Object[]> params) {

Check warning on line 471 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L471

Added line #L471 was not covered by tests
if (command == null || (!GET_REQUEST_COMMANDS.matcher(command.toLowerCase()).matches() && !GET_REQUEST_COMMANDS_LIST.contains(command.toLowerCase())
&& !command.equalsIgnoreCase("updateConfiguration") && !method.equals("POST"))) {
return false;

Check warning on line 474 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L474

Added line #L474 was not covered by tests
}
return !command.equalsIgnoreCase("updateConfiguration") || method.equals("POST") || (params.containsKey("name")
&& params.get("name")[0].toString().equalsIgnoreCase(ApiServer.EnforcePostRequestsAndTimestamps.key()));
Comment on lines +472 to +477
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider refactoring the conditional logic in isStateChangingCommandUsingPOST for better readability, and add inline comments to explain the complex conditions.

Suggested change
if (command == null || (!GET_REQUEST_COMMANDS.matcher(command.toLowerCase()).matches() && !GET_REQUEST_COMMANDS_LIST.contains(command.toLowerCase())
&& !command.equalsIgnoreCase("updateConfiguration") && !method.equals("POST"))) {
return false;
}
return !command.equalsIgnoreCase("updateConfiguration") || method.equals("POST") || (params.containsKey("name")
&& params.get("name")[0].toString().equalsIgnoreCase(ApiServer.EnforcePostRequestsAndTimestamps.key()));
// Check if the command is null
if (command == null) {
return false;
}
// Convert the command to lowercase for case-insensitive comparison
String commandLowerCase = command.toLowerCase();
// Condition 1: Check if the command matches GET_REQUEST_COMMANDS or is in GET_REQUEST_COMMANDS_LIST
boolean isGetRequestCommand = GET_REQUEST_COMMANDS.matcher(commandLowerCase).matches() || GET_REQUEST_COMMANDS_LIST.contains(commandLowerCase);
// Condition 2: Check if the command is "updateConfiguration" and the method is not "POST"
boolean isUpdateConfigWithNonPost = command.equalsIgnoreCase("updateConfiguration") && !method.equals("POST");
// If neither condition 1 nor condition 2 is satisfied, return false
if (!isGetRequestCommand && !isUpdateConfigWithNonPost) {
return false;
}
// Condition 3: Check if the command is "updateConfiguration" and the method is "POST"
boolean isUpdateConfigWithPost = command.equalsIgnoreCase("updateConfiguration") && method.equals("POST");
// Condition 4: Check if the "name" parameter exists and matches the required key
boolean isEnforcePostRequest = params.containsKey("name") &&
params.get("name")[0].toString().equalsIgnoreCase(ApiServer.EnforcePostRequestsAndTimestamps.key());
// Return true if either condition 3 or condition 4 is satisfied
return isUpdateConfigWithPost || isEnforcePostRequest;

Copilot uses AI. Check for mistakes.
}

Check warning on line 478 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L478

Added line #L478 was not covered by tests

protected boolean skip2FAcheckForAPIs(String command) {
boolean skip2FAcheck = false;

Expand Down Expand Up @@ -644,4 +714,45 @@
}
return null;
}

private String getCleanParamsString(Map<String, String[]> reqParams) {

Check warning on line 718 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L718

Added line #L718 was not covered by tests
if (MapUtils.isEmpty(reqParams)) {
return "";

Check warning on line 720 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L720

Added line #L720 was not covered by tests
}

StringBuilder cleanParamsString = new StringBuilder();

Check warning on line 723 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L723

Added line #L723 was not covered by tests
for (Map.Entry<String, String[]> reqParam : reqParams.entrySet()) {
if (org.apache.commons.lang3.StringUtils.isBlank(reqParam.getKey())) {
continue;

Check warning on line 726 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L726

Added line #L726 was not covered by tests
}

cleanParamsString.append(reqParam.getKey());
cleanParamsString.append("=");

Check warning on line 730 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L729-L730

Added lines #L729 - L730 were not covered by tests

if (reqParam.getKey().toLowerCase().contains("password")
|| reqParam.getKey().toLowerCase().contains("privatekey")
|| reqParam.getKey().toLowerCase().contains("accesskey")
|| reqParam.getKey().toLowerCase().contains("secretkey")) {
cleanParamsString.append("\n");
continue;

Check warning on line 737 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L736-L737

Added lines #L736 - L737 were not covered by tests
}

if (reqParam.getValue() == null || reqParam.getValue().length == 0) {
cleanParamsString.append("\n");
continue;

Check warning on line 742 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L741-L742

Added lines #L741 - L742 were not covered by tests
}

for (String param : reqParam.getValue()) {
if (org.apache.commons.lang3.StringUtils.isBlank(param)) {
continue;

Check warning on line 747 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L747

Added line #L747 was not covered by tests
}
String cleanParamString = StringUtils.cleanString(param.trim());
cleanParamsString.append(cleanParamString);
cleanParamsString.append(" ");

Check warning on line 751 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L749-L751

Added lines #L749 - L751 were not covered by tests
}
cleanParamsString.append("\n");
}

Check warning on line 754 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L753-L754

Added lines #L753 - L754 were not covered by tests

return cleanParamsString.toString();
}

Check warning on line 757 in server/src/main/java/com/cloud/api/ApiServlet.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/api/ApiServlet.java#L756-L757

Added lines #L756 - L757 were not covered by tests
}
44 changes: 29 additions & 15 deletions ui/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,10 @@ import {
ACCESS_TOKEN
} from '@/store/mutation-types'

export function api (command, args = {}, method = 'GET', data = {}) {
Copy link
Contributor

@shwstppr shwstppr Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sureshanaparti wouldn't it be easier to refactor this method itself to enforce use of POST/GET based on configuration/API type? We won't have to modify each caller in that case. Current change will cause merge conflicts for almost all UI related active PRs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will check it @shwstppr

Copy link
Contributor Author

@sureshanaparti sureshanaparti Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shwstppr As discussed offline, the api method is refactored to smaller, separate functions to handle GET & POST calls. These new calls also indicate the underlying method (GET / POST) used for the API call. Also, it's better to have POST method calls for all non-listing, state changing APIs from UI, so keeping these changes as it is. Any merge conflicts / issues, we(or the authors)'ll resolve them.

let params = {}
export function getAPI (command, args = {}) {
args.command = command
args.response = 'json'

if (data) {
params = new URLSearchParams()
Object.entries(data).forEach(([key, value]) => {
params.append(key, value)
})
}

const sessionkey = vueProps.$localStorage.get(ACCESS_TOKEN) || Cookies.get('sessionkey')
if (sessionkey) {
args.sessionkey = sessionkey
Expand All @@ -45,8 +37,30 @@ export function api (command, args = {}, method = 'GET', data = {}) {
...args
},
url: '/',
method,
data: params || {}
method: 'GET'
})
}

export function postAPI (command, data = {}) {
const params = new URLSearchParams()
params.append('command', command)
params.append('response', 'json')
if (data) {
Object.entries(data).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
params.append(key, value)
}
})
}

const sessionkey = vueProps.$localStorage.get(ACCESS_TOKEN) || Cookies.get('sessionkey')
if (sessionkey) {
params.append('sessionkey', sessionkey)
}
return axios({
url: '/',
method: 'POST',
data: params
})
}

Expand All @@ -56,7 +70,7 @@ export function login (arg) {
}

// Logout before login is called to purge any duplicate sessionkey cookies
api('logout')
postAPI('logout')

const params = new URLSearchParams()
params.append('command', 'login')
Expand All @@ -66,7 +80,7 @@ export function login (arg) {
params.append('response', 'json')
return axios({
url: '/',
method: 'post',
method: 'POST',
data: params,
headers: {
'content-type': 'application/x-www-form-urlencoded'
Expand All @@ -77,7 +91,7 @@ export function login (arg) {
export function logout () {
message.destroy()
notification.destroy()
return api('logout')
return postAPI('logout')
}

export function oauthlogin (arg) {
Expand All @@ -86,7 +100,7 @@ export function oauthlogin (arg) {
}

// Logout before login is called to purge any duplicate sessionkey cookies
api('logout')
postAPI('logout')

const params = new URLSearchParams()
params.append('command', 'oauthlogin')
Expand Down
6 changes: 3 additions & 3 deletions ui/src/components/header/SamlDomainSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

<script>
import store from '@/store'
import { api } from '@/api'
import { postAPI } from '@/api'
import _ from 'lodash'

export default {
Expand All @@ -73,7 +73,7 @@ export default {
const samlAccounts = []
const getNextPage = () => {
this.loading = true
api('listAndSwitchSamlAccount', { details: 'min', page: page, pageSize: 500 }).then(json => {
postAPI('listAndSwitchSamlAccount', { details: 'min', page: page, pageSize: 500 }).then(json => {
if (json && json.listandswitchsamlaccountresponse && json.listandswitchsamlaccountresponse.samluseraccount) {
samlAccounts.push(...json.listandswitchsamlaccountresponse.samluseraccount)
}
Expand Down Expand Up @@ -102,7 +102,7 @@ export default {
},
changeAccount (index) {
const account = this.samlAccounts[index]
api('listAndSwitchSamlAccount', {}, 'POST', {
postAPI('listAndSwitchSamlAccount', {
userid: account.userId,
domainid: account.domainId
}).then(response => {
Expand Down
Loading
Loading