package com.thinkbiganalytics.metadata.upgrade;
/*-
* #%L
* kylo-operational-metadata-upgrade-service
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* 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.
* #L%
*/
import com.thinkbiganalytics.KyloVersion;
import com.thinkbiganalytics.KyloVersionUtil;
import com.thinkbiganalytics.feedmgr.security.FeedServicesAccessControl;
import com.thinkbiganalytics.jobrepo.security.OperationsAccessControl;
import com.thinkbiganalytics.metadata.api.MetadataAccess;
import com.thinkbiganalytics.metadata.api.PostMetadataConfigAction;
import com.thinkbiganalytics.metadata.api.app.KyloVersionProvider;
import com.thinkbiganalytics.metadata.api.category.Category;
import com.thinkbiganalytics.metadata.api.category.CategoryProvider;
import com.thinkbiganalytics.metadata.api.category.security.CategoryAccessControl;
import com.thinkbiganalytics.metadata.api.datasource.security.DatasourceAccessControl;
import com.thinkbiganalytics.metadata.api.feed.Feed;
import com.thinkbiganalytics.metadata.api.feed.FeedProvider;
import com.thinkbiganalytics.metadata.api.feed.OpsManagerFeed;
import com.thinkbiganalytics.metadata.api.feed.OpsManagerFeedProvider;
import com.thinkbiganalytics.metadata.api.feed.security.FeedAccessControl;
import com.thinkbiganalytics.metadata.api.security.RoleNotFoundException;
import com.thinkbiganalytics.metadata.api.template.FeedManagerTemplate;
import com.thinkbiganalytics.metadata.api.template.FeedManagerTemplateProvider;
import com.thinkbiganalytics.metadata.api.template.security.TemplateAccessControl;
import com.thinkbiganalytics.metadata.api.user.User;
import com.thinkbiganalytics.metadata.api.user.UserGroup;
import com.thinkbiganalytics.metadata.api.user.UserProvider;
import com.thinkbiganalytics.metadata.jpa.feed.JpaOpsManagerFeed;
import com.thinkbiganalytics.metadata.modeshape.JcrMetadataAccess;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.category.JcrCategory;
import com.thinkbiganalytics.metadata.modeshape.feed.JcrFeed;
import com.thinkbiganalytics.metadata.modeshape.security.action.JcrAllowedActions;
import com.thinkbiganalytics.metadata.modeshape.support.JcrPropertyUtil;
import com.thinkbiganalytics.metadata.modeshape.template.JcrFeedTemplate;
import com.thinkbiganalytics.metadata.upgrade.version_0_7_1.UpgradeAction;
import com.thinkbiganalytics.security.AccessController;
import com.thinkbiganalytics.security.action.Action;
import com.thinkbiganalytics.security.action.AllowableAction;
import com.thinkbiganalytics.security.action.AllowedActions;
import com.thinkbiganalytics.security.action.AllowedEntityActionsProvider;
import com.thinkbiganalytics.security.role.SecurityRole;
import com.thinkbiganalytics.security.role.SecurityRoleProvider;
import com.thinkbiganalytics.support.FeedNameUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
@Order(PostMetadataConfigAction.LATE_ORDER + 100)
public class UpgradeKyloService implements PostMetadataConfigAction {
//public class UpgradeKyloService {
// public static final KyloVersion[] UPGRADE_VERSIONS = new KyloVersion[]
// {
// KyloVersionUtil.version("0.7.")
// };
private static final Logger log = LoggerFactory.getLogger(UpgradeKyloService.class);
@Inject
OpsManagerFeedProvider opsManagerFeedProvider;
@Inject
MetadataAccess metadataAccess;
@Inject
private KyloVersionProvider kyloVersionProvider;
@Inject
private FeedProvider feedProvider;
@Inject
private FeedManagerTemplateProvider feedManagerTemplateProvider;
@Inject
private CategoryProvider categoryProvider;
@Inject
private UserProvider userProvider;
@Inject
private SecurityRoleProvider roleProvider;
@Inject
private PasswordEncoder passwordEncoder;
@Inject
private AllowedEntityActionsProvider actionsProvider;
@Inject
private AccessController accessController;
public void run() {
onMetadataStart();
}
public KyloVersion getCurrentVersion() {
KyloVersion version = kyloVersionProvider.getCurrentVersion();
if (version != null) {
return new KyloVersionUtil.Version(version.getMajorVersion(), version.getMinorVersion());
} else {
return null;
}
}
public boolean upgradeFrom(KyloVersion startingVersion) {
if (startingVersion == null) {
setupFreshInstall();
} else {
//uncomment after we build the correct upgrade strategy. For now call the 0.7.1 upgrade explicitly
UpgradeAction upgradeAction = new UpgradeAction();
upgradeAction.upgradeFrom(startingVersion);
// getUpgradeState(startingVersion).ifPresent(upgrade -> upgrade.upgradeFrom(startingVersion));
}
// TODO: This current implementation assumes all upgrading occurs from the single state found above.
// This should be changed to supporting a repeated upgrade path from starting ver->next ver->...->latest vers.
kyloVersionProvider.updateToLatestVersion();
return kyloVersionProvider.isUpToDate();
}
public Optional<UpgradeState> getUpgradeState(KyloVersion version) {
try {
String className = getPackageName(version) + ".UpgradeAction";
@SuppressWarnings("unchecked")
Class<UpgradeState> upgradeClass = (Class<UpgradeState>) Class.forName(className);
return Optional.of(ConstructorUtils.invokeConstructor(upgradeClass));
} catch (ClassNotFoundException e) {
return Optional.empty();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
throw new IllegalStateException("Unable to load upgrade state for version: " + version, e);
}
}
protected String getPackageName(KyloVersion version) {
return getClass().getPackage().getName() + ".version_" + createVersionTag(version);
}
protected String createVersionTag(KyloVersion version) {
return version.getVersion().replaceAll("[.-]", "_");
}
/**
* This code will run when the latest version is up and running
*/
private void onMetadataStart() {
if (kyloVersionProvider.isUpToDate()) {
ensureFeedTemplateFeedRelationships();
fixDefaultGroupNames();
if (accessController.isEntityAccessControlled()) {
ensureDefaultEntityRoles();
}
}
}
private void fixDefaultGroupNames() {
metadataAccess.commit(() -> {
this.userProvider.findGroupByName("designer")
.ifPresent(oldGrp -> {
UserGroup designersGroup = createDefaultGroup("designers", "Designers");
oldGrp.getUsers().forEach(user -> designersGroup.addUser(user));
actionsProvider.getAllowedActions(AllowedActions.SERVICES)
.ifPresent((allowed) -> {
allowed.enable(designersGroup.getRootPrincial(),
OperationsAccessControl.ACCESS_OPS,
FeedServicesAccessControl.EDIT_FEEDS,
FeedServicesAccessControl.ACCESS_TABLES,
FeedServicesAccessControl.IMPORT_FEEDS,
FeedServicesAccessControl.EXPORT_FEEDS,
FeedServicesAccessControl.EDIT_CATEGORIES,
FeedServicesAccessControl.EDIT_DATASOURCES,
FeedServicesAccessControl.EDIT_TEMPLATES,
FeedServicesAccessControl.IMPORT_TEMPLATES,
FeedServicesAccessControl.EXPORT_TEMPLATES,
FeedServicesAccessControl.ADMIN_TEMPLATES,
FeedServicesAccessControl.ACCESS_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.EDIT_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.ACCESS_GLOBAL_SEARCH);
});
this.userProvider.deleteGroup(oldGrp);
});
this.userProvider.findGroupByName("analyst")
.ifPresent(oldGrp -> {
UserGroup analystsGroup = createDefaultGroup("analysts", "Analysts");
oldGrp.getUsers().forEach(user -> analystsGroup.addUser(user));
actionsProvider.getAllowedActions(AllowedActions.SERVICES)
.ifPresent((allowed) -> {
allowed.enable(analystsGroup.getRootPrincial(),
OperationsAccessControl.ACCESS_OPS,
FeedServicesAccessControl.EDIT_FEEDS,
FeedServicesAccessControl.ACCESS_TABLES,
FeedServicesAccessControl.IMPORT_FEEDS,
FeedServicesAccessControl.EXPORT_FEEDS,
FeedServicesAccessControl.EDIT_CATEGORIES,
FeedServicesAccessControl.ACCESS_TEMPLATES,
FeedServicesAccessControl.ACCESS_DATASOURCES,
FeedServicesAccessControl.ACCESS_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.EDIT_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.ACCESS_GLOBAL_SEARCH);
});
this.userProvider.deleteGroup(oldGrp);
});
}, MetadataAccess.SERVICE);
}
private void setupFreshInstall() {
metadataAccess.commit(() -> {
User dladmin = createDefaultUser("dladmin", "Data Lake Administrator", "thinkbig");
User analyst = createDefaultUser("analyst", "Analyst", "analyst");
User designer = createDefaultUser("designer", "Designer", "designer");
User operator = createDefaultUser("operator", "Operator", "operator");
// Create default groups if they don't exist.
UserGroup adminsGroup = createDefaultGroup("admin", "Administrators");
UserGroup opsGroup = createDefaultGroup("operations", "Operations");
UserGroup designersGroup = createDefaultGroup("designers", "Designers");
UserGroup analystsGroup = createDefaultGroup("analysts", "Analysts");
UserGroup usersGroup = createDefaultGroup("user", "Users");
// Add default users to their respective groups
adminsGroup.addUser(dladmin);
designersGroup.addUser(designer);
analystsGroup.addUser(analyst);
opsGroup.addUser(operator);
usersGroup.addUser(dladmin);
usersGroup.addUser(analyst);
usersGroup.addUser(designer);
usersGroup.addUser(operator);
// Setup initial group access control. Administrators group already has all rights.
actionsProvider.getAllowedActions(AllowedActions.SERVICES)
.ifPresent((allowed) -> {
allowed.enable(opsGroup.getRootPrincial(),
OperationsAccessControl.ADMIN_OPS,
FeedServicesAccessControl.ACCESS_CATEGORIES,
FeedServicesAccessControl.ACCESS_FEEDS,
FeedServicesAccessControl.ACCESS_TEMPLATES,
FeedServicesAccessControl.ACCESS_TABLES,
FeedServicesAccessControl.ACCESS_SERVICE_LEVEL_AGREEMENTS);
allowed.enable(designersGroup.getRootPrincial(),
OperationsAccessControl.ACCESS_OPS,
FeedServicesAccessControl.EDIT_FEEDS,
FeedServicesAccessControl.ACCESS_TABLES,
FeedServicesAccessControl.IMPORT_FEEDS,
FeedServicesAccessControl.EXPORT_FEEDS,
FeedServicesAccessControl.EDIT_CATEGORIES,
FeedServicesAccessControl.EDIT_DATASOURCES,
FeedServicesAccessControl.EDIT_TEMPLATES,
FeedServicesAccessControl.IMPORT_TEMPLATES,
FeedServicesAccessControl.EXPORT_TEMPLATES,
FeedServicesAccessControl.ADMIN_TEMPLATES,
FeedServicesAccessControl.ACCESS_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.EDIT_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.ACCESS_GLOBAL_SEARCH);
allowed.enable(analystsGroup.getRootPrincial(),
OperationsAccessControl.ACCESS_OPS,
FeedServicesAccessControl.EDIT_FEEDS,
FeedServicesAccessControl.ACCESS_TABLES,
FeedServicesAccessControl.IMPORT_FEEDS,
FeedServicesAccessControl.EXPORT_FEEDS,
FeedServicesAccessControl.EDIT_CATEGORIES,
FeedServicesAccessControl.ACCESS_TEMPLATES,
FeedServicesAccessControl.ACCESS_DATASOURCES,
FeedServicesAccessControl.ACCESS_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.EDIT_SERVICE_LEVEL_AGREEMENTS,
FeedServicesAccessControl.ACCESS_GLOBAL_SEARCH);
});
}, MetadataAccess.SERVICE);
}
private void createDefaultRoles() {
metadataAccess.commit(() -> {
// Create default roles
SecurityRole feedEditor = createDefaultRole(SecurityRole.FEED, "editor", "Editor", "Allows a user to edit, enable/disable, delete, export, and access job operations.",
FeedAccessControl.EDIT_DETAILS,
FeedAccessControl.DELETE,
FeedAccessControl.ACCESS_OPS,
FeedAccessControl.ENABLE_DISABLE,
FeedAccessControl.EXPORT);
//admin can do everything the editor does + change perms
createDefaultRole(SecurityRole.FEED, "admin", "Admin", "All capabilities defined in the 'Editor' role along with the ability to change the permissions", feedEditor,
FeedAccessControl.CHANGE_PERMS);
createDefaultRole(SecurityRole.FEED, "readOnly", "Read-Only", "Allows a user to view the feed and access job operations",
FeedAccessControl.ACCESS_DETAILS,
FeedAccessControl.ACCESS_OPS);
SecurityRole templateEditor = createDefaultRole(SecurityRole.TEMPLATE, "editor", "Editor", "Allows a user to edit,export a template",
TemplateAccessControl.ACCESS_TEMPLATE,
TemplateAccessControl.EDIT_TEMPLATE,
TemplateAccessControl.CREATE_FEED,
TemplateAccessControl.EXPORT);
createDefaultRole(SecurityRole.TEMPLATE, "admin", "Admin", "All capabilities defined in the 'Editor' role along with the ability to change the permissions", templateEditor,
TemplateAccessControl.CHANGE_PERMS);
createDefaultRole(SecurityRole.TEMPLATE, "readOnly", "Read-Only", "Allows a user to view the template", TemplateAccessControl.ACCESS_TEMPLATE);
SecurityRole categoryEditor = createDefaultRole(SecurityRole.CATEGORY, "editor", "Editor", "Allows a user to edit, export, delete, and create feeds using this category",
CategoryAccessControl.ACCESS_CATEGORY,
CategoryAccessControl.EDIT_DETAILS,
CategoryAccessControl.EDIT_SUMMARY,
CategoryAccessControl.EXPORT,
CategoryAccessControl.CREATE_FEED,
CategoryAccessControl.DELETE);
createDefaultRole(SecurityRole.CATEGORY, "admin", "Admin", "All capabilities defined in the 'Editor' role along with the ability to change the permissions", categoryEditor,
CategoryAccessControl.CHANGE_PERMS);
createDefaultRole(SecurityRole.CATEGORY, "readOnly", "Read-Only", "Allows a user to view the category", CategoryAccessControl.ACCESS_CATEGORY);
createDefaultRole(SecurityRole.CATEGORY, "feedCreator", "Feed Creator", "Allows a user to create a new feed using this category", CategoryAccessControl.ACCESS_DETAILS,
CategoryAccessControl.CREATE_FEED);
final SecurityRole datasourceEditor = createDefaultRole(SecurityRole.DATASOURCE, "editor", "Editor", "Allows a user to edit,delete datasources",
DatasourceAccessControl.ACCESS_DATASOURCE,
DatasourceAccessControl.EDIT_DETAILS,
DatasourceAccessControl.EDIT_SUMMARY,
DatasourceAccessControl.DELETE);
createDefaultRole(SecurityRole.DATASOURCE, "admin", "Admin", "All capabilities defined in the 'Editor' role along with the ability to change the permissions", datasourceEditor,
DatasourceAccessControl.CHANGE_PERMS);
createDefaultRole(SecurityRole.DATASOURCE, "readOnly", "Read-Only", "Allows a user to view the datasource", DatasourceAccessControl.ACCESS_DATASOURCE);
}, MetadataAccess.SERVICE);
}
private void ensureDefaultEntityRoles() {
createDefaultRoles();
metadataAccess.commit(() -> {
ensureTemplateRoles();
ensureCategoryRoles();
ensureFeedRoles();
}, MetadataAccess.SERVICE);
}
private void ensureFeedRoles() {
List<Feed> feeds = feedProvider.findAll();
if (feeds != null) {
List<SecurityRole> roles = this.roleProvider.getEntityRoles(SecurityRole.FEED);
Optional<AllowedActions> allowedActions = this.actionsProvider.getAvailableActions(AllowedActions.FEED);
feeds.stream().forEach(feed -> {
Principal owner = feed.getOwner() != null ? feed.getOwner() : JcrMetadataAccess.getActiveUser();
allowedActions.ifPresent(actions -> ((JcrFeed) feed).enableAccessControl((JcrAllowedActions) actions, owner, roles));
});
}
}
private void ensureCategoryRoles() {
List<Category> categories = categoryProvider.findAll();
if (categories != null) {
List<SecurityRole> roles = this.roleProvider.getEntityRoles(SecurityRole.CATEGORY);
Optional<AllowedActions> allowedActions = this.actionsProvider.getAvailableActions(AllowedActions.CATEGORY);
categories.stream().forEach(category -> {
Principal owner = category.getOwner() != null ? category.getOwner() : JcrMetadataAccess.getActiveUser();
allowedActions.ifPresent(actions -> ((JcrCategory) category).enableAccessControl((JcrAllowedActions) actions, owner, roles));
});
}
}
private void ensureTemplateRoles() {
List<FeedManagerTemplate> templates = feedManagerTemplateProvider.findAll();
if (templates != null) {
List<SecurityRole> roles = this.roleProvider.getEntityRoles(SecurityRole.TEMPLATE);
Optional<AllowedActions> allowedActions = this.actionsProvider.getAvailableActions(AllowedActions.TEMPLATE);
templates.stream().forEach(template -> {
Principal owner = template.getOwner() != null ? template.getOwner() : JcrMetadataAccess.getActiveUser();
allowedActions.ifPresent(actions -> ((JcrFeedTemplate) template).enableAccessControl((JcrAllowedActions) actions, owner, roles));
});
}
}
/*
* Ensure the Feed Template has the relationships setup to its related feeds
*/
private void ensureFeedTemplateFeedRelationships() {
metadataAccess.commit(() -> {
//ensure the templates have the feed relationships
List<Feed> feeds = feedProvider.findAll();
if (feeds != null) {
feeds.stream().forEach(feed -> {
FeedManagerTemplate template = feed.getTemplate();
if (template != null) {
//ensure the template has feeds.
List<Feed> templateFeeds = null;
try {
templateFeeds = template.getFeeds();
} catch (MetadataRepositoryException e) {
//templateFeeds are weak references.
//if the template feeds return itemNotExists we need to reset it
Throwable rootCause = ExceptionUtils.getRootCause(e);
if (rootCause != null && rootCause instanceof ItemNotFoundException) {
//reset the reference collection. It will be rebuilt in the subsequent call
JcrPropertyUtil.removeAllFromSetProperty(((JcrFeedTemplate) template).getNode(), JcrFeedTemplate.FEEDS);
}
}
if (templateFeeds == null || !templateFeeds.contains(feed)) {
template.addFeed(feed);
feedManagerTemplateProvider.update(template);
}
}
});
}
feedProvider.populateInverseFeedDependencies();
return null;
}, MetadataAccess.SERVICE);
}
/*
* Migrate and remove or move any properties defined
*/
private void migrateUnusedFeedProperties() {
Set<String> propertiesToRemove = new HashSet<>();
//propertiesToRemove.add('nametoremove');
if (!propertiesToRemove.isEmpty()) {
metadataAccess.commit(() -> {
List<Feed> domainFeeds = feedProvider.findAll();
for (Feed feedManagerFeed : domainFeeds) {
final PropertyIterator iterator;
try {
iterator = ((JcrFeed) feedManagerFeed).getNode().getProperties();
} catch (RepositoryException e) {
throw new MetadataRepositoryException("Failed to get properties for node: " + feedManagerFeed, e);
}
while (iterator.hasNext()) {
final Property property = iterator.nextProperty();
try {
if (propertiesToRemove.contains(property.getName())) {
property.remove();
}
} catch (Exception e) {
}
}
}
return null;
}, MetadataAccess.SERVICE);
}
}
/**
* Performs the necessary actions to ensure that Kylo versions < 0.4.0 are upgraded
*
* @return the Kylo version after upgrade attempt
*/
public void upgradeTo0_4_0() {
metadataAccess.commit(() -> metadataAccess.commit(() -> {
for (Category category : categoryProvider.findAll()) {
// Ensure each category has an allowedActions (gets create if not present.)
category.getAllowedActions();
}
// get all feeds defined in feed manager
List<Feed> domainFeeds = feedProvider.findAll();
Map<String, Feed> feedManagerFeedMap = new HashMap<>();
if (domainFeeds != null && !domainFeeds.isEmpty()) {
List<OpsManagerFeed.ID> opsManagerFeedIds = new ArrayList<OpsManagerFeed.ID>();
for (Feed feedManagerFeed : domainFeeds) {
opsManagerFeedIds.add(opsManagerFeedProvider.resolveId(feedManagerFeed.getId().toString()));
feedManagerFeedMap.put(feedManagerFeed.getId().toString(), feedManagerFeed);
// Ensure each feed has an allowedActions (gets create if not present.)
feedManagerFeed.getAllowedActions();
}
//find those that match
List<? extends OpsManagerFeed> opsManagerFeeds = opsManagerFeedProvider.findByFeedIds(opsManagerFeedIds);
if (opsManagerFeeds != null) {
for (OpsManagerFeed opsManagerFeed : opsManagerFeeds) {
feedManagerFeedMap.remove(opsManagerFeed.getId().toString());
}
}
List<OpsManagerFeed> feedsToAdd = new ArrayList<>();
for (Feed feed : feedManagerFeedMap.values()) {
String fullName = FeedNameUtil.fullName(feed.getCategory().getName(), feed.getName());
OpsManagerFeed.ID opsManagerFeedId = opsManagerFeedProvider.resolveId(feed.getId().toString());
OpsManagerFeed opsManagerFeed = new JpaOpsManagerFeed(opsManagerFeedId, fullName);
feedsToAdd.add(opsManagerFeed);
}
log.info("Synchronizing Feeds from Feed Manager. About to insert {} feed ids/names into Operations Manager", feedsToAdd.size());
opsManagerFeedProvider.save(feedsToAdd);
}
return null;
//update the version
// return kyloVersionProvider.updateToLatestVersion();
}), MetadataAccess.SERVICE);
}
protected User createDefaultUser(String username, String displayName, String password) {
Optional<User> userOption = userProvider.findUserBySystemName(username);
User user = null;
// Create the user if it doesn't exists.
if (userOption.isPresent()) {
user = userOption.get();
} else {
user = userProvider.ensureUser(username);
user.setPassword(passwordEncoder.encode(password));
user.setDisplayName(displayName);
}
return user;
}
protected UserGroup createDefaultGroup(String groupName, String title) {
UserGroup newGroup = userProvider.ensureGroup(groupName);
newGroup.setTitle(title);
return newGroup;
}
protected SecurityRole createDefaultRole(String entityName, String roleName, String title, String desc, Action... actions) {
Supplier<SecurityRole> createIfNotFound = () -> {
SecurityRole role = roleProvider.createRole(entityName, roleName, title, desc);
role.setPermissions(actions);
return role;
};
Function<SecurityRole, SecurityRole> ensureActions = (role) -> {
role.setDescription(desc);
if (actions != null) {
List<Action> actionsList = Arrays.asList(actions);
boolean needsUpdate = actionsList.stream().anyMatch(action -> !role.getAllowedActions().hasPermission(action));
if (needsUpdate) {
role.setPermissions(actions);
}
}
return role;
};
try {
return roleProvider.getRole(entityName, roleName).map(ensureActions).orElseGet(createIfNotFound);
} catch (RoleNotFoundException e) {
return createIfNotFound.get();
}
}
/**
* Constructs a new role using the specified base role and actions.
*
* @param entityName the folder name
* @param roleName the system name for the role
* @param title the human-readable name for the role
* @param baseRole the role with the default actions for this role
* @param actions additional actions for this role
* @return the security role
*/
protected SecurityRole createDefaultRole(@Nonnull final String entityName, @Nonnull final String roleName, @Nonnull final String title, final String desc, @Nonnull final SecurityRole baseRole,
final Action... actions) {
final Stream<Action> baseActions = baseRole.getAllowedActions().getAvailableActions().stream().flatMap(AllowableAction::stream);
final Action[] allowedActions = Stream.concat(baseActions, Stream.of(actions)).toArray(Action[]::new);
return createDefaultRole(entityName, roleName, title, desc, allowedActions);
}
}