blob: fa0625c74ff57d27e48a481b97c69059aba412b0 [file] [log] [blame]
/*
* Copyright (c) 2010, 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.apache.zest.library.shiro.concerns;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.PermissionUtils;
import org.apache.zest.api.common.AppliesTo;
import org.apache.zest.api.common.Optional;
import org.apache.zest.api.concern.ConcernOf;
import org.apache.zest.api.injection.scope.Invocation;
import org.apache.zest.library.shiro.Shiro;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@AppliesTo( { RequiresAuthentication.class,
RequiresGuest.class,
RequiresPermissions.class,
RequiresRoles.class,
RequiresUser.class } )
public class SecurityConcern
extends ConcernOf<InvocationHandler>
implements InvocationHandler
{
private static final Logger LOGGER = LoggerFactory.getLogger( Shiro.LOGGER_NAME );
@Optional
@Invocation
private RequiresAuthentication requiresAuthentication;
@Optional
@Invocation
private RequiresGuest requiresGuest;
@Optional
@Invocation
private RequiresPermissions requiresPermissions;
@Optional
@Invocation
private RequiresRoles requiresRoles;
@Optional
@Invocation
private RequiresUser requiresUser;
@Override
public Object invoke( Object proxy, Method method, Object[] args )
throws Throwable
{
Subject subject = SecurityUtils.getSubject();
handleRequiresGuest( subject );
handleRequiresUser( subject );
handleRequiresAuthentication( subject );
handleRequiresRoles( subject );
handleRequiresPermissions( subject );
return next.invoke( proxy, method, args );
}
private void handleRequiresGuest( Subject subject )
{
if ( requiresGuest != null ) {
LOGGER.debug( "SecurityConcern::RequiresGuest" );
if ( subject.getPrincipal() != null ) {
throw new UnauthenticatedException(
"Attempting to perform a guest-only operation. The current Subject is "
+ "not a guest (they have been authenticated or remembered from a previous login). Access "
+ "denied." );
}
} else {
LOGGER.debug( "SecurityConcern::RequiresGuest: not concerned" );
}
}
private void handleRequiresUser( Subject subject )
{
if ( requiresUser != null ) {
LOGGER.debug( "SecurityConcern::RequiresUser" );
if ( subject.getPrincipal() == null ) {
throw new UnauthenticatedException(
"Attempting to perform a user-only operation. The current Subject is "
+ "not a user (they haven't been authenticated or remembered from a previous login). "
+ "Access denied." );
}
} else {
LOGGER.debug( "SecurityConcern::RequiresUser: not concerned" );
}
}
private void handleRequiresAuthentication( Subject subject )
{
if ( requiresAuthentication != null ) {
LOGGER.debug( "SecurityConcern::RequiresAuthentication" );
if ( !subject.isAuthenticated() ) {
throw new UnauthenticatedException( "The current Subject is not authenticated. Access denied." );
}
} else {
LOGGER.debug( "SecurityConcern::RequiresAuthentication: not concerned" );
}
}
private void handleRequiresRoles( Subject subject )
{
if ( requiresRoles != null ) {
LOGGER.debug( "SecurityConcern::RequiresRoles" );
String roleId = requiresRoles.value();
String[] roles = roleId.split( "," );
if ( roles.length == 1 ) {
if ( !subject.hasRole( roles[ 0] ) ) {
String msg = "Calling Subject does not have required role [" + roleId + "]. "
+ "MethodInvocation denied.";
throw new UnauthorizedException( msg );
}
} else {
Set<String> rolesSet = new LinkedHashSet<String>( Arrays.asList( roles ) );
if ( !subject.hasAllRoles( rolesSet ) ) {
String msg = "Calling Subject does not have required roles [" + roleId + "]. "
+ "MethodInvocation denied.";
throw new UnauthorizedException( msg );
}
}
} else {
LOGGER.debug( "SecurityConcern::RequiresRoles: not concerned" );
}
}
private void handleRequiresPermissions( Subject subject )
{
if ( requiresPermissions != null ) {
LOGGER.debug( "SecurityConcern::RequiresPermissions" );
String permsString = requiresPermissions.value();
Set<String> permissions = PermissionUtils.toPermissionStrings( permsString );
if ( permissions.size() == 1 ) {
if ( !subject.isPermitted( permissions.iterator().next() ) ) {
String msg = "Calling Subject does not have required permission [" + permsString + "]. "
+ "Method invocation denied.";
throw new UnauthorizedException( msg );
}
} else {
String[] permStrings = new String[ permissions.size() ];
permStrings = permissions.toArray( permStrings );
if ( !subject.isPermittedAll( permStrings ) ) {
String msg = "Calling Subject does not have required permissions [" + permsString + "]. "
+ "Method invocation denied.";
throw new UnauthorizedException( msg );
}
}
} else {
LOGGER.debug( "SecurityConcern::RequiresPermissions: not concerned" );
}
}
}