Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limited customization with AuthorizationManager #13060

Open
YaniM opened this issue Apr 17, 2023 · 2 comments
Open

Limited customization with AuthorizationManager #13060

YaniM opened this issue Apr 17, 2023 · 2 comments
Labels
in: core An issue in spring-security-core

Comments

@YaniM
Copy link

YaniM commented Apr 17, 2023

Hello, we are migrating to Spring Boot 3 and trying to replace deprecated GlobalMethodSecurityConfiguration, but it doesn't seems to have a clear, straightforward way of doing that. Could you suggest how we should proceed?

Currently we have this (Spring Boot 2.x)

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    ApplicationContext applicationContext;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        (1) var expressionHandler = new CustomMethodSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }

    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
       (2) return new CustomPermissionMetadataSource();
    }
}

(1):

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    private AuthenticationTrustResolver trustResolver =
            new AuthenticationTrustResolverImpl();

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
            Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root =
                new CustomMethodSecurityExpressionRoot(authentication);
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(this.trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        root.setThis(invocation.getThis());
        return root;
    }
}

(2):

public class CustomPermissionMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {

    @Override
    protected Collection findAttributes(Method method, Class<?> targetClass) {
        if (!targetClass.getPackage().getName().startsWith("some.package")) {
            return null;
        }
        List attributes = new ArrayList<>();

        // if the class is annotated as @Controller or @RestController deny access to all methods
        if (AnnotationUtils.findAnnotation(targetClass, Controller.class) != null
                || AnnotationUtils.findAnnotation(targetClass, RestController.class) != null) {
            attributes.add(DENY_ALL_ATTRIBUTE);
        }

        // unless there is a class level annotation
        var classLevelAnnotationPresent =
                AnnotationUtils.findAnnotation(targetClass, PreAuthorize.class) != null ||
                AnnotationUtils.findAnnotation(targetClass, PostAuthorize.class) != null ||
                AnnotationUtils.findAnnotation(targetClass, NoAuthorization.class) != null;

        if (classLevelAnnotationPresent) {
            return null;
        }

        // or method level annotation
        Annotation[] annotations = AnnotationUtils.getAnnotations(method);
        if (annotations != null) {
            for (Annotation a : annotations) {
                if (a instanceof PreAuthorize || a instanceof PostAuthorize || a instanceof NoAuthorization) {
                    return null;
                }
            }
        }
        return attributes;
    }
}

Migrated: (Spring Boot 3.x)

@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public Advisor customAuthorize() {
        var pattern = new JdkRegexpMethodPointcut();
        pattern.setPattern("my.package.controller");
        var rule = new CustomAuthorizationManager();
        var interceptor = new AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
        interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder() - 1);
        return interceptor;
    }
}
public class CustomAuthorizationManager implements AuthorizationManager<MethodInvocation> {
 @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
      (2) // contains logic defined in CustomPermissionMetadataSource and based on that returns new AuthorizationDecision(true/false)

    }
}
@Component
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    @Override
    public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
        var context = (StandardEvaluationContext) super.createEvaluationContext(authentication, mi);
        var root = new CustomMethodSecurityExpressionRoot(authentication.get());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(new AuthenticationTrustResolverImpl());
        root.setRoleHierarchy(getRoleHierarchy());
        root.setThis(mi.getThis());
        context.setRootObject(root);
        return context;
    }

}

What is the goal:
(2)
To deny access to classes annotated as @ Controller or @ RestController unless there is PreAuthorize, PostAuthorize or NoAuthorization annotation on class/method level. If PreAuthorize or PostAuthorize annotation is present, it is managed by our CustomMethodSecurityExpressionRoot unless it is NoAuthorization (our custom annotation, defining methods as 'authorization not needed') which grants access without further evaluation.

The part where I'm stuck:
Which is the correct way to migrate customMethodSecurityMetadataSource - they way I have mentioned above with CustomAuthorizationManager or something else?

@YaniM YaniM added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Apr 17, 2023
@jzheaux
Copy link
Contributor

jzheaux commented Apr 17, 2023

@YaniM thanks for the report. Sorry, it's quite challenging to go through a post with a lot of unformatted, pasted code, and I'm not clear yet on what you are trying to do and where you are getting stuck.

Can you please put together a minimal GitHub sample that shows where you are stuck?

Also, please consult the migration guide and post here where the guide is unclear.

@jzheaux jzheaux added in: core An issue in spring-security-core and removed status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Apr 17, 2023
@YaniM
Copy link
Author

YaniM commented Apr 18, 2023

@YaniM thanks for the report. Sorry, it's quite challenging to go through a post with a lot of unformatted, pasted code, and I'm not clear yet on what you are trying to do and where you are getting stuck.

Can you please put together a minimal GitHub sample that shows where you are stuck?

Also, please consult the migration guide and post here where the guide is unclear.

Hi, @jzheaux
Yes, sorry for the mess. Actually, I had missed something in the migration guide you have posted... Anyway I updated the ticket and pointed out the part where I'm not sure if that's the correct way. Thank you for your time!

@YaniM YaniM closed this as completed Apr 18, 2023
@YaniM YaniM reopened this Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core An issue in spring-security-core
Projects
None yet
Development

No branches or pull requests

2 participants