blob: d8612a692f8c781fd52db35fcf1fa7f4ab8263ac [file] [log] [blame]
// 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 org.apache.cloudstack.acl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.exception.UnavailableCommandException;
import org.apache.cloudstack.api.APICommand;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.User;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.component.PluggableService;
import com.google.common.base.Strings;
public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements APIAclChecker {
@Inject
private AccountService accountService;
@Inject
private RoleService roleService;
private List<PluggableService> services;
private Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<>();
protected DynamicRoleBasedAPIAccessChecker() {
super();
for (RoleType roleType : RoleType.values()) {
annotationRoleBasedApisMap.put(roleType, new HashSet<String>());
}
}
private void denyApiAccess(final String commandName) throws PermissionDeniedException {
throw new PermissionDeniedException("The API " + commandName + " is blacklisted for the account's role.");
}
public boolean isDisabled() {
return !roleService.isEnabled();
}
@Override
public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
if (isDisabled()) {
return true;
}
Account account = accountService.getAccount(user.getAccountId());
if (account == null) {
throw new PermissionDeniedException("The account id=" + user.getAccountId() + "for user id=" + user.getId() + "is null");
}
final Role accountRole = roleService.findRole(account.getRoleId());
if (accountRole == null || accountRole.getId() < 1L) {
denyApiAccess(commandName);
}
// Allow all APIs for root admins
if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) {
return true;
}
// Check against current list of permissions
for (final RolePermission permission : roleService.findAllPermissionsBy(accountRole.getId())) {
if (permission.getRule().matches(commandName)) {
if (RolePermission.Permission.ALLOW.equals(permission.getPermission())) {
return true;
} else {
denyApiAccess(commandName);
}
}
}
// Check annotations
if (annotationRoleBasedApisMap.get(accountRole.getRoleType()) != null
&& annotationRoleBasedApisMap.get(accountRole.getRoleType()).contains(commandName)) {
return true;
}
// Default deny all
throw new UnavailableCommandException("The API " + commandName + " does not exist or is not available for this account.");
}
public void addApiToRoleBasedAnnotationsMap(final RoleType roleType, final String commandName) {
if (roleType == null || Strings.isNullOrEmpty(commandName)) {
return;
}
final Set<String> commands = annotationRoleBasedApisMap.get(roleType);
if (commands != null && !commands.contains(commandName)) {
commands.add(commandName);
}
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
super.configure(name, params);
return true;
}
@Override
public boolean start() {
for (PluggableService service : services) {
for (Class<?> clz : service.getCommands()) {
APICommand command = clz.getAnnotation(APICommand.class);
for (RoleType role : command.authorized()) {
addApiToRoleBasedAnnotationsMap(role, command.name());
}
}
}
return super.start();
}
public List<PluggableService> getServices() {
return services;
}
@Inject
public void setServices(List<PluggableService> services) {
this.services = services;
}
}