/* * Copyright (c) 2012, Paul Merlin. All Rights Reserved. * * 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.qi4j.library.shiro.domain.passwords; import java.util.Set; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.DefaultPasswordService; import org.apache.shiro.authc.credential.PasswordMatcher; import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.Authorizer; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.DefaultHashService; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.Realm; import org.apache.shiro.subject.PrincipalCollection; import org.qi4j.api.configuration.Configuration; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.injection.scope.This; import org.qi4j.api.query.QueryBuilder; import org.qi4j.api.service.ServiceActivation; import org.qi4j.api.structure.Module; import org.qi4j.api.unitofwork.UnitOfWork; import org.qi4j.library.shiro.Shiro; import org.qi4j.library.shiro.domain.permissions.RoleAssignee; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.qi4j.api.query.QueryExpressions.eq; import static org.qi4j.api.query.QueryExpressions.templateFor; public class PasswordRealmMixin extends AuthorizingRealm implements Realm, Authorizer, PasswordService, ServiceActivation { private static final Logger LOG = LoggerFactory.getLogger( Shiro.LOGGER_NAME ); @Structure private Module module; @This private Configuration<PasswordRealmConfiguration> configuration; private final DefaultPasswordService passwordService; public PasswordRealmMixin() { super(); passwordService = new DefaultPasswordService(); PasswordMatcher matcher = new PasswordMatcher(); matcher.setPasswordService( passwordService ); setCredentialsMatcher( matcher ); } @Override public void activateService() throws Exception { configuration.refresh(); PasswordRealmConfiguration config = configuration.get(); String algorithm = config.hashAlgorithmName().get(); Integer iterations = config.hashIterationsCount().get(); if ( algorithm != null || iterations != null ) { DefaultHashService hashService = ( DefaultHashService ) passwordService.getHashService(); if ( algorithm != null ) { hashService.setHashAlgorithmName( algorithm ); } if ( iterations != null ) { hashService.setHashIterations( iterations ); } } } @Override public void passivateService() throws Exception { } @Override public String encryptPassword( Object plaintextPassword ) throws IllegalArgumentException { return passwordService.encryptPassword( plaintextPassword ); } @Override public boolean passwordsMatch( Object submittedPlaintext, String encrypted ) { return passwordService.passwordsMatch( submittedPlaintext, encrypted ); } @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) throws AuthenticationException { UnitOfWork uow = module.newUnitOfWork(); try { String username = ( ( UsernamePasswordToken ) token ).getUsername(); PasswordSecurable account = findPasswordSecurable( uow, username ); if ( account == null ) { LOG.debug( "Unknown subject identifier: {}" + username ); return null; } LOG.debug( "Found account for {}: {}", username, account ); return new SimpleAuthenticationInfo( account.subjectIdentifier().get(), account.password().get(), getName() ); } finally { uow.discard(); } } @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals ) { UnitOfWork uow = module.newUnitOfWork(); try { String username = getAvailablePrincipal( principals ).toString(); RoleAssignee roleAssignee = findRoleAssignee( uow, username ); if ( roleAssignee == null ) { LOG.debug( "No authorization info for {}", username ); return null; } LOG.debug( "Found role assignee for {}: {}", username, roleAssignee ); Set<String> roleNames = roleAssignee.roleNames(); Set<String> permissionStrings = roleAssignee.permissionStrings(); LOG.debug( "Found role assignee has the following roles: {}", roleNames ); LOG.debug( "Found role assignee has the following permissions: {}", permissionStrings ); SimpleAuthorizationInfo atzInfo = new SimpleAuthorizationInfo( roleNames ); atzInfo.setStringPermissions( permissionStrings ); return atzInfo; } finally { uow.discard(); } } private PasswordSecurable findPasswordSecurable( UnitOfWork uow, String username ) { QueryBuilder<PasswordSecurable> builder = module.newQueryBuilder( PasswordSecurable.class ); builder = builder.where( eq( templateFor( PasswordSecurable.class ).subjectIdentifier(), username ) ); return uow.newQuery( builder ).find(); } private RoleAssignee findRoleAssignee( UnitOfWork uow, String username ) { QueryBuilder<RoleAssignee> builder = module.newQueryBuilder( RoleAssignee.class ); builder = builder.where( eq( templateFor( RoleAssignee.class ).subjectIdentifier(), username ) ); return uow.newQuery( builder ).find(); } }