/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.services.security.userrole.jdbc; import org.pentaho.platform.api.engine.IUserRoleListService; import org.pentaho.platform.api.engine.security.IAuthenticationRoleMapper; import org.pentaho.platform.api.mt.ITenant; import org.pentaho.platform.repository2.unified.jcr.JcrTenantUtils; import org.springframework.context.ApplicationContextException; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.jdbc.object.MappingSqlQuery; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; public class JdbcUserRoleListService extends JdbcDaoSupport implements IUserRoleListService { // ~ Static fields/initializers // ============================================= public static final String DEF_ALL_AUTHORITIES_QUERY = "SELECT distinct(authority) as authority FROM authorities ORDER BY authority"; //$NON-NLS-1$ public static final String DEF_ALL_USERNAMES_QUERY = "SELECT distinct(username) as username FROM users ORDER BY username"; //$NON-NLS-1$ public static final String DEF_ALL_USERNAMES_IN_ROLE_QUERY = "SELECT distinct(username) as username FROM authorities WHERE authority = ?"; //$NON-NLS-1$ // ~ Instance fields // ======================================================== protected MappingSqlQuery allAuthoritiesMapping; protected MappingSqlQuery allUsernamesMapping; protected MappingSqlQuery allUsernamesInRoleMapping; private String allAuthoritiesQuery; private String allUsernamesQuery; private String allUsernamesInRoleQuery; private UserDetailsService userDetailsService; private String rolePrefix; private List<String> systemRoles; private List<String> extraRoles; private IAuthenticationRoleMapper roleMapper; // ~ Constructors // =========================================================== public JdbcUserRoleListService( final UserDetailsService userDetailsService, final List<String> systemRoles ) { allAuthoritiesQuery = JdbcUserRoleListService.DEF_ALL_AUTHORITIES_QUERY; allUsernamesQuery = JdbcUserRoleListService.DEF_ALL_USERNAMES_QUERY; allUsernamesInRoleQuery = JdbcUserRoleListService.DEF_ALL_USERNAMES_IN_ROLE_QUERY; this.userDetailsService = userDetailsService; this.systemRoles = systemRoles; this.extraRoles = PentahoSystem.get( ArrayList.class, "extraSystemAuthorities", PentahoSessionHolder.getSession() ); } // ~ Methods // ================================================================ /** * Allows the default query string used to retrieve all authorities to be overriden, if default table or column names * need to be changed. The default query is {@link #DEF_ALL_AUTHORITIES_QUERY}; when modifying this query, ensure that * all returned columns are mapped back to the same column names as in the default query. * * @param queryString * The query string to set */ public void setAllAuthoritiesQuery( final String queryString ) { allAuthoritiesQuery = queryString; } public String getAllAuthoritiesQuery() { return allAuthoritiesQuery; } /** * Allows the default query string used to retrieve all user names in a role to be overriden, if default table or * column names need to be changed. The default query is {@link #DEF_ALL_USERNAMES_QUERY}; when modifying this query, * ensure that all returned columns are mapped back to the same column names as in the default query. * * @param queryString * The query string to set */ public void setAllUsernamesInRoleQuery( final String queryString ) { allUsernamesInRoleQuery = queryString; } public String getAllUsernamesInRoleQuery() { return allUsernamesInRoleQuery; } /** * Allows the default query string used to retrieve all user names to be overriden, if default table or column names * need to be changed. The default query is {@link #DEF_ALL_USERNAMES_IN_ROLE_QUERY}; when modifying this query, * ensure that all returned columns are mapped back to the same column names as in the default query. * * @param queryString * The query string to set */ public void setAllUsernamesQuery( final String queryString ) { allUsernamesQuery = queryString; } public String getAllUsernamesQuery() { return allUsernamesQuery; } public List<String> getAllRoles() throws DataAccessException { List<GrantedAuthority> allAuths = allAuthoritiesMapping.execute(); LinkedHashSet<String> roles = new LinkedHashSet<String>( allAuths.size() ); for ( GrantedAuthority role : allAuths ) { if ( roleMapper != null ) { roles.add( roleMapper.toPentahoRole( role.getAuthority() ) ); } else { roles.add( role.getAuthority() ); } } return new ArrayList<>( addExtraRoles( roles ) ); } private LinkedHashSet<String> addExtraRoles( LinkedHashSet<String> roles ) { if ( extraRoles == null ) { return roles; } // Now add extra role if it does not exist in the list for ( String extraRole : extraRoles ) { roles.add( extraRole ); } return roles; } public List<String> getAllUsers() throws DataAccessException { List<String> allUserNames = allUsernamesMapping.execute(); return allUserNames; } public List<String> getUsersInRole( final String role ) { String roleToTest = role; if ( roleMapper != null ) { roleToTest = roleMapper.fromPentahoRole( role ); } List<String> allUserNamesInRole = allUsernamesInRoleMapping.execute( roleToTest ); return allUserNamesInRole; } @Override protected void initDao() throws ApplicationContextException { initMappingSqlQueries(); } /** * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass */ protected void initMappingSqlQueries() { this.allAuthoritiesMapping = new AllAuthoritiesMapping( getDataSource() ); this.allUsernamesInRoleMapping = new AllUserNamesInRoleMapping( getDataSource() ); this.allUsernamesMapping = new AllUserNamesMapping( getDataSource() ); } // ~ Inner Classes // ========================================================== /** * Query object to look up all users. */ protected class AllUserNamesMapping extends MappingSqlQuery { protected AllUserNamesMapping( final DataSource ds ) { super( ds, allUsernamesQuery ); compile(); } @Override protected Object mapRow( final ResultSet rs, final int rownum ) throws SQLException { return rs.getString( 1 ); } } /** * Query object to look up users in a role. */ protected class AllUserNamesInRoleMapping extends MappingSqlQuery { protected AllUserNamesInRoleMapping( final DataSource ds ) { super( ds, allUsernamesInRoleQuery ); declareParameter( new SqlParameter( Types.VARCHAR ) ); compile(); } @Override protected Object mapRow( final ResultSet rs, final int rownum ) throws SQLException { return rs.getString( 1 ); } } /** * Query object to look up all authorities. */ protected class AllAuthoritiesMapping extends MappingSqlQuery { protected AllAuthoritiesMapping( final DataSource ds ) { super( ds, allAuthoritiesQuery ); compile(); } @Override protected Object mapRow( final ResultSet rs, final int rownum ) throws SQLException { return new SimpleGrantedAuthority( ( ( null != rolePrefix ) ? rolePrefix : "" ) + rs.getString( 1 ) ); //$NON-NLS-1$ } } public List<String> getRolesForUser( final String username ) throws UsernameNotFoundException, DataAccessException { UserDetails user = userDetailsService.loadUserByUsername( username ); LinkedHashSet<String> roles = new LinkedHashSet<String>( user.getAuthorities().size() ); for ( GrantedAuthority role : user.getAuthorities() ) { if ( roleMapper != null ) { roles.add( roleMapper.toPentahoRole( role.getAuthority() ) ); } else { roles.add( role.getAuthority() ); } } return new ArrayList<>( addExtraRoles( roles ) ); } public void setRolePrefix( final String rolePrefix ) { this.rolePrefix = rolePrefix; } public void setUserDetailsService( final UserDetailsService userDetailsService ) { this.userDetailsService = userDetailsService; } @Override public List<String> getAllRoles( ITenant tenant ) { if ( tenant != null && !tenant.equals( JcrTenantUtils.getDefaultTenant() ) ) { throw new UnsupportedOperationException( "only allowed to access to default tenant" ); } return getAllRoles(); } @Override public List<String> getAllUsers( ITenant tenant ) { if ( tenant != null && !tenant.equals( JcrTenantUtils.getDefaultTenant() ) ) { throw new UnsupportedOperationException( "only allowed to access to default tenant" ); } return getAllUsers(); } @Override public List<String> getUsersInRole( ITenant tenant, String role ) { if ( tenant != null && !tenant.equals( JcrTenantUtils.getDefaultTenant() ) ) { throw new UnsupportedOperationException( "only allowed to access to default tenant" ); } return getUsersInRole( role ); } @Override public List<String> getRolesForUser( ITenant tenant, String username ) { if ( tenant != null && !tenant.equals( JcrTenantUtils.getDefaultTenant() ) ) { throw new UnsupportedOperationException( "only allowed to access to default tenant" ); } return getRolesForUser( username ); } @Override public List<String> getSystemRoles() { return systemRoles; } public void setRoleMapper( IAuthenticationRoleMapper roleMapper ) { this.roleMapper = roleMapper; } }