/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jbpm.casemgmt.impl;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Stream;
import org.jbpm.casemgmt.api.auth.AuthorizationManager;
import org.jbpm.shared.services.impl.TransactionalCommandService;
import org.jbpm.shared.services.impl.commands.QueryNameCommand;
import org.kie.internal.identity.IdentityProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AuthorizationManagerImpl implements AuthorizationManager {
private static final Logger logger = LoggerFactory.getLogger(AuthorizationManagerImpl.class);
private static final String NO_ACCESS_MSG = "User {0} is not authorized to access case {1}";
private static final String NO_AUTH_OPER_MSG = "User {0} is not authorized to {1} on case {2}";
private IdentityProvider identityProvider;
private TransactionalCommandService commandService;
private boolean enabled = Boolean.parseBoolean(System.getProperty("org.jbpm.cases.auth.enabled", "true"));
private Map<ProtectedOperation, List<String>> operationAuthorization = new HashMap<>();
public AuthorizationManagerImpl(IdentityProvider identityProvider, TransactionalCommandService commandService) {
this.identityProvider = identityProvider;
this.commandService = commandService;
buildAuthorizationConfig();
}
@Override
public void checkAuthorization(String caseId) throws SecurityException {
if (!isEnabled()) {
return;
}
logger.debug("Checking authorization to case {} for user {}", caseId, identityProvider.getName());
Map<String, Object> params = new HashMap<String, Object>();
params.put("caseId", caseId);
List<String> authorizedEntities = commandService.execute(new QueryNameCommand<List<String>>("getAuthorizationToCaseInstance", params));
verifyAuthorization(caseId, authorizedEntities, MessageFormat.format(NO_ACCESS_MSG, identityProvider.getName(), caseId));
}
@Override
public void checkOperationAuthorization(String caseId, ProtectedOperation operation) throws SecurityException {
List<String> rolesForOperation = operationAuthorization.get(operation);
if (rolesForOperation == null || rolesForOperation.isEmpty()) {
logger.debug("No restrictions defined for operation {}", operation);
checkAuthorization(caseId);
return;
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("caseId", caseId);
params.put("roles", rolesForOperation);
List<String> authorizedEntities = commandService.execute(new QueryNameCommand<List<String>>("getAuthorizationToCaseInstanceByRole", params));
verifyAuthorization(caseId, authorizedEntities, MessageFormat.format(NO_AUTH_OPER_MSG, identityProvider.getName(), operation, caseId));
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/*
* Helper methods
*/
protected void buildAuthorizationConfig() {
Properties loaded = new Properties();
InputStream configuration = this.getClass().getResourceAsStream("/case-authorization.properties");
if (configuration != null) {
try {
loaded.load(configuration);
} catch (IOException e) {
logger.error("Error loading case autorization config from file due to {}", e.getMessage(), e);
}
}
Stream.of(ProtectedOperation.values()).forEach(operation -> {
List<String> roles = new ArrayList<>();
String grantedRoles = loaded.getProperty(operation.toString());
if (grantedRoles != null) {
roles.addAll(Arrays.asList(grantedRoles.split(",")));
}
operationAuthorization.put(operation, roles);
});
}
protected void verifyAuthorization(String caseId, List<String> authorizedEntities, String errorMessage) {
logger.debug("Case {} authorization set is {}", caseId, authorizedEntities);
if (authorizedEntities.isEmpty()) {
logger.debug("Not access restrictions defined for case {}", caseId);
// no authorization configured - roles assignment are not defined
return;
}
List<String> callerAuthorization = collectUserAuthInfo();
logger.debug("Caller authorization set is {}", callerAuthorization);
boolean isAuthorized = callerAuthorization.stream().anyMatch(entity -> authorizedEntities.contains(entity));
if (!isAuthorized) {
logger.debug("User {} not authorized to access case {}", identityProvider.getName(), caseId);
throw new SecurityException(errorMessage);
}
logger.debug("User {} authorized to access case {}", identityProvider.getName(), caseId);
}
protected List<String> collectUserAuthInfo() {
List<String> entities = new ArrayList<>();
entities.add(identityProvider.getName());
entities.addAll(identityProvider.getRoles());
// add special public role to allow to find cases that do not use case roles
entities.add(PUBLIC_GROUP);
return entities;
}
}