| /* |
| * 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" ); |
| } |
| |
| } |
| |
| } |