Skip to content

RepositoryRestMvcConfiguration can no longer be subclassed #2118

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

Closed
srudin opened this issue Feb 23, 2022 · 2 comments
Closed

RepositoryRestMvcConfiguration can no longer be subclassed #2118

srudin opened this issue Feb 23, 2022 · 2 comments
Assignees
Labels
status: waiting-for-feedback We need additional information before we can continue

Comments

@srudin
Copy link

srudin commented Feb 23, 2022

We have the exact same problem as described in #1981.

Our reason is that we override repositoryInvokerFactory to return a custom SecurityRepositoryInvokerFactory which returns repositories with included permission checks.

The solution in the issue mentioned above was to get away from overriding RepositoryRestMvcConfiguration by making use of other mechanism to achieve the same goal. Could anyone please tell me what would be the suggested solution here?

Not sure it is necessary but for completness here's our implementation:

@Component
@Configuration
public class RepositoryRestMvcConfig extends RepositoryRestMvcConfiguration {

    @Autowired
    ConversionService defaultConversionService;

    public RepositoryRestMvcConfig(ApplicationContext context,
                                   ObjectFactory<ConversionService> conversionService,
                                   ObjectProvider<LinkRelationProvider> relProvider,
                                   ObjectProvider<CurieProvider> curieProvider,
                                   ObjectProvider<HalConfiguration> halConfiguration,
                                   ObjectProvider<ObjectMapper> objectMapper,
                                   ObjectProvider<RepresentationModelProcessorInvoker> invoker,
                                   ObjectProvider<MessageResolver> resolver,
                                   ObjectProvider<GeoModule> geoModule,
                                   ObjectProvider<PathPatternParser> parser) {
        super(context, conversionService, relProvider, curieProvider, halConfiguration,
            objectMapper, invoker, resolver, geoModule, parser);
    }

    @Bean
    @Qualifier
    @Override
    public RepositoryInvokerFactory repositoryInvokerFactory() {
        return new SecurityRepositoryInvokerFactory(
            new UnwrappingRepositoryInvokerFactory(
                new DefaultRepositoryInvokerFactory(repositories(), defaultConversionService),
                getEntityLookups()));
    }

    private static class SecurityRepositoryInvokerFactory implements RepositoryInvokerFactory {
        private RepositoryInvokerFactory delegate;

        public SecurityRepositoryInvokerFactory(RepositoryInvokerFactory delegate) {
            this.delegate = delegate;
        }

        @Override
        public RepositoryInvoker getInvokerFor(Class<?> domainType) {
            return new SecurityRepositoryInvoker(delegate.getInvokerFor(domainType));
        }
    }

    private static class SecurityRepositoryInvoker extends DelegatingRepositoryInvoker {
        private final EntityAccessAuthorizer entityAccessAuthorizer;

        public SecurityRepositoryInvoker(RepositoryInvoker delegate) {
            super(delegate);
            entityAccessAuthorizer = new EntityAccessAuthorizer();
        }

        @Override
        public <T> T invokeSave(T object) {
            return delegate.invokeSave(entityAccessAuthorizer.verifyAuthorization(object));
        }

        @Override
        public <T> Optional<T> invokeFindById(Object id) {
            return entityAccessAuthorizer.verifyAuthorization(delegate.invokeFindById(id));
        }

        @Override
        public Iterable<Object> invokeFindAll(Pageable pageable) {
            return entityAccessAuthorizer.verifyAuthorization(delegate.invokeFindAll(pageable));
        }

        @Override
        public Iterable<Object> invokeFindAll(Sort sort) {
            return entityAccessAuthorizer.verifyAuthorization(delegate.invokeFindAll(sort));
        }

        @Override
        public void invokeDeleteById(Object id) {
            entityAccessAuthorizer.verifyAuthorization(invokeFindById(id));
            delegate.invokeDeleteById(id);
        }
    }

    private static class DelegatingRepositoryInvoker implements RepositoryInvoker {
        protected final RepositoryInvoker delegate;

        public DelegatingRepositoryInvoker(RepositoryInvoker delegate) {
            this.delegate = delegate;
        }

        @Override
        public <T> T invokeSave(T object) {
            return delegate.invokeSave(object);
        }

        @Override
        public <T> Optional<T> invokeFindById(Object id) {
            return delegate.invokeFindById(id);
        }

        @Override
        public Iterable<Object> invokeFindAll(Pageable pageable) {
            return delegate.invokeFindAll(pageable);
        }

        @Override
        public Iterable<Object> invokeFindAll(Sort sort) {
            return delegate.invokeFindAll(sort);
        }

        @Override
        public void invokeDeleteById(Object id) {
            delegate.invokeDeleteById(id);
        }

        @Override
        public Optional<Object> invokeQueryMethod(Method method, MultiValueMap<String, ?> parameters, Pageable pageable, Sort sort) {
            return delegate.invokeQueryMethod(method, parameters, pageable, sort);
        }

        @Override
        public boolean hasSaveMethod() {
            return delegate.hasSaveMethod();
        }

        @Override
        public boolean hasDeleteMethod() {
            return delegate.hasDeleteMethod();
        }

        @Override
        public boolean hasFindOneMethod() {
            return delegate.hasFindOneMethod();
        }

        @Override
        public boolean hasFindAllMethod() {
            return delegate.hasFindAllMethod();
        }
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 23, 2022
@odrotbohm
Copy link
Member

Would you mind elaborating on what exactly you're trying to achieve? Spring Data REST already supports SpringSecurity expressions on the repository interfaces themselves. Other than that, there's nothing to add on top of what has been generally discussed in #1981. RRMC has been deprecated for extensions for a long while now. If we can describe a use case for extending one of the internal components, we can think about exposing customization hooks for those.

@odrotbohm odrotbohm added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 2, 2022
@odrotbohm odrotbohm self-assigned this Mar 2, 2022
@srudin
Copy link
Author

srudin commented Mar 8, 2022

I have inherited this code and I guess neither the original author nor me knew about SpringSecurity expressions. Thanks for pointing me in this direction - I will try and in case our requirements exceed the possibilities of those expressions I will come back to you.

@srudin srudin closed this as completed Mar 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-feedback We need additional information before we can continue
Projects
None yet
Development

No branches or pull requests

3 participants