com.hortonworks.streamline.streams.security.impl.DefaultStreamlineAuthorizer.java Source code

Java tutorial

Introduction

Here is the source code for com.hortonworks.streamline.streams.security.impl.DefaultStreamlineAuthorizer.java

Source

/**
 * Copyright 2017 Hortonworks.
 *
 * 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 com.hortonworks.streamline.streams.security.impl;

import com.hortonworks.streamline.common.exception.DuplicateEntityException;
import com.hortonworks.streamline.streams.security.AuthenticationContext;
import com.hortonworks.streamline.streams.security.AuthorizationException;
import com.hortonworks.streamline.streams.security.Permission;
import com.hortonworks.streamline.streams.security.Roles;
import com.hortonworks.streamline.streams.security.SecurityUtil;
import com.hortonworks.streamline.streams.security.StreamlineAuthorizer;
import com.hortonworks.streamline.streams.security.catalog.AclEntry;
import com.hortonworks.streamline.streams.security.catalog.Role;
import com.hortonworks.streamline.streams.security.catalog.User;
import com.hortonworks.streamline.streams.security.service.SecurityCatalogService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class DefaultStreamlineAuthorizer implements StreamlineAuthorizer {
    private static final Logger LOG = LoggerFactory.getLogger(DefaultStreamlineAuthorizer.class);

    public static final String CONF_CATALOG_SERVICE = "catalogService";
    public static final String CONF_ADMIN_PRINCIPALS = "adminPrincipals";

    private SecurityCatalogService catalogService;
    private Set<String> adminUsers;

    @SuppressWarnings("unchecked")
    @Override
    public void init(Map<String, Object> config) {
        LOG.info("Initializing DefaultStreamlineAuthorizer with config {}", config);
        catalogService = (SecurityCatalogService) config.get(CONF_CATALOG_SERVICE);
        adminUsers = ((Set<String>) config.get(CONF_ADMIN_PRINCIPALS)).stream().map(SecurityUtil::getUserName)
                .collect(Collectors.toSet());
        LOG.info("Admin users: {}", adminUsers);
        mayBeAddAdminUsers();
        mayBeAssignAdminRole();
    }

    private void mayBeAddAdminUsers() {
        LOG.info("Checking user entries for admin users");
        adminUsers.stream().filter(name -> {
            User user = catalogService.getUser(name);
            if (user != null) {
                LOG.info("Entry for user '{}' already exists", name);
                return false;
            } else {
                return true;
            }
        }).forEach(name -> {
            User user = new User();
            user.setName(name);
            user.setEmail(name + "@auto-generated.com");
            user.setMetadata("{\"colorCode\":\"#8261be\",\"colorLabel\":\"purple\",\"icon\":\"gears\"}");
            try {
                User addedUser = catalogService.addUser(user);
                LOG.info("Added admin user entry: {}", addedUser);
            } catch (DuplicateEntityException exception) {
                // In HA setup the other server may have already added the user.
                LOG.info("Caught exception: " + ExceptionUtils.getStackTrace(exception));
                LOG.info("Admin user entry: {} already exists.", user);
            }
        });
    }

    private void mayBeAssignAdminRole() {
        LOG.info("Checking if admin users have admin role");
        Role adminRole = catalogService.getRole(Roles.ROLE_ADMIN).orElseGet(() -> {
            Role admin = new Role();
            admin.setName("ROLE_ADMIN");
            admin.setDisplayName("Admin");
            admin.setDescription("Super user role that has all the system roles and privileges");
            admin.setMetadata(
                    "{\"colorCode\":\"#8261be\",\"colorLabel\":\"purple\",\"icon\":\"gears\", \"menu\": [\"schemaRegistry\", \"modelRegistry\", \"udf\", \"dashboard\", \"topology\", \"authorizer\", \"notifier\", \"customprocessor\", \"servicepool\", \"environments\"], \"capabilities\": [{\"Applications\": \"Edit\"}, {\"Service Pool\": \"Edit\"}, {\"Environments\": \"Edit\"}, {\"Users\": \"Edit\"}, {\"Dashboard\": \"Edit\"}]}");
            admin.setSystem(false);
            return catalogService.addRole(admin);
        });
        adminUsers.stream().map(userName -> catalogService.getUser(userName)).filter(user -> {
            if (userHasRole(user, Roles.ROLE_ADMIN)) {
                LOG.info("user '{}' already has '{}'", user, Roles.ROLE_ADMIN);
                return false;
            } else {
                return true;
            }
        }).forEach(user -> catalogService.addUserRole(user.getId(), adminRole.getId()));
    }

    @Override
    public boolean hasPermissions(AuthenticationContext ctx, String targetEntityNamespace, Long targetEntityId,
            EnumSet<Permission> permissions) {
        boolean result = checkPermissions(ctx, targetEntityNamespace, targetEntityId, permissions);
        LOG.debug(
                "DefaultStreamlineAuthorizer, AuthenticationContext: {}, targetEntityNamespace: {}, targetEntityId: {}, "
                        + "permissions: {}, result: {}",
                ctx, targetEntityNamespace, targetEntityId, permissions, result);
        return result;
    }

    @Override
    public boolean hasRole(AuthenticationContext ctx, String role) {
        boolean result = checkRole(ctx, role);
        LOG.debug("DefaultStreamlineAuthorizer, AuthenticationContext: {}, Role: {}, Result: {}", ctx, role,
                result);
        return result;
    }

    @Override
    public void addAcl(AuthenticationContext ctx, String targetEntityNamespace, Long targetEntityId, boolean owner,
            boolean grant, EnumSet<Permission> permissions) {
        validateAuthenticationContext(ctx);
        String userName = SecurityUtil.getUserName(ctx);
        User user = catalogService.getUser(userName);
        if (user == null || user.getId() == null) {
            String msg = String.format("No such user '%s'", userName);
            LOG.warn(msg);
            throw new AuthorizationException(msg);
        }
        AclEntry aclEntry = new AclEntry();
        aclEntry.setObjectId(targetEntityId);
        aclEntry.setObjectNamespace(targetEntityNamespace);
        aclEntry.setSidId(user.getId());
        aclEntry.setSidType(AclEntry.SidType.USER);
        aclEntry.setOwner(owner);
        aclEntry.setGrant(grant);
        aclEntry.setPermissions(permissions);
        catalogService.addAcl(aclEntry);
    }

    @Override
    public void removeAcl(AuthenticationContext ctx, String targetEntityNamespace, Long targetEntityId) {
        validateAuthenticationContext(ctx);
        String userName = SecurityUtil.getUserName(ctx);
        User user = catalogService.getUser(userName);
        if (user == null || user.getId() == null) {
            String msg = String.format("No such user '%s'", userName);
            LOG.warn(msg);
            throw new AuthorizationException(msg);
        }
        catalogService.listUserAcls(user.getId(), targetEntityNamespace, targetEntityId).forEach(acl -> {
            LOG.debug("Removing Acl {}", acl);
            catalogService.removeAcl(acl.getId());
        });
    }

    private boolean checkPermissions(AuthenticationContext ctx, String targetEntityNamespace, Long targetEntityId,
            EnumSet<Permission> permissions) {
        validateAuthenticationContext(ctx);
        String userName = SecurityUtil.getUserName(ctx);
        User user = catalogService.getUser(userName);
        if (user == null || user.getId() == null) {
            String msg = String.format("No such user '%s'", userName);
            LOG.warn(msg);
            throw new AuthorizationException(msg);
        }
        return userHasRole(user, Roles.ROLE_ADMIN) || catalogService.checkUserPermissions(targetEntityNamespace,
                targetEntityId, user.getId(), permissions);
    }

    private void validateAuthenticationContext(AuthenticationContext ctx) {
        if (ctx == null || ctx.getPrincipal() == null) {
            throw new AuthorizationException("No principal in AuthenticationContext");
        }
    }

    private boolean checkRole(AuthenticationContext ctx, String role) {
        validateAuthenticationContext(ctx);
        String userName = SecurityUtil.getUserName(ctx);
        User user = catalogService.getUser(userName);
        if (user == null) {
            String msg = String.format("No such user '%s'", userName);
            LOG.warn(msg);
            throw new AuthorizationException(msg);
        }
        return userHasRole(user, Roles.ROLE_ADMIN) || userHasRole(user, role);
    }

    private boolean userHasRole(User user, String roleName) {
        Set<String> userRoles = user.getRoles();
        boolean res = false;
        // top level roles
        if (userRoles.contains(roleName)) {
            res = true;
        } else {
            Role roleToCheck = new Role();
            roleToCheck.setName(roleName);
            // child roles
            for (String userRole : userRoles) {
                Optional<Role> role = catalogService.getRole(userRole);
                if (role.isPresent()) {
                    if (catalogService.getChildRoles(role.get().getId()).contains(roleToCheck)) {
                        res = true;
                        break;
                    }
                }
            }
        }
        LOG.debug("User: {}, Role: {}, Result: {}", user.getName(), roleName, res);
        return res;
    }
}