spring-graphql icon indicating copy to clipboard operation
spring-graphql copied to clipboard

@QueryMapping or @PreAuthorize don't work when used on an @GraphQlRepository

Open tkaefer opened this issue 3 years ago • 3 comments

Setup

Given

  • a Spring-Boot artifact (Spring-Boot version 2.7.4)
  • with JPA based DBMS (PostgreSQL) Repo
  • using lombok
  • a working Spring Security OAuth2.0+JWT setup (with a working role mapper)

Entity like:

// package + imports ...

@Entity
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class Foo {
    @Id
    @GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
    @GeneratedValue(generator = "UUIDGenerator")
    @Column(name = "id", updatable = false, nullable = false)
    public UUID id;

    private String name;
}

GraphQlRepository like:

// package + imports ...

@GraphQlRepository
public interface FooGraphQlRepository extends JpaRepository<Foo, UUID>, QuerydslPredicateExecutor<Foo> {

    @PreAuthorize("hasRole('ROLE_read')")
    @QueryMapping(name = "findFooByName")
    List<Foo> findFooByName(@Argument("name") String name);

    @PreAuthorize("hasRole('ROLE_read')")
    @QueryMapping
    List<Foo> findAll();

    @PreAuthorize("hasRole('ROLE_read) and hasRole('ROLE_write')")
    @MutationMapping(name = "createFoo")
    Foo save(@Argument("input") FooInput fooInput);

    // I did use also the Foo Entity but it did not change anything.
    @ProjectedPayload
    public interface FooInput {
        String getName();
    }
}

schema.graphqls like:


type Query {
    foo: [Foo]
    findFooByName(name: String): [Foo]
}

type Mutation {
    createFoo(input: FooInput): Foo
}

input FooInput {
    name: String!
}

type Foo {
    id: ID!
    name: String!
}

Issue

In the end it never mattered what I used in the @QueryMapping, @MutationMappin or even @PreAuthorize config. The requests have always been forwarded to a responding method in the repository. Neither using an non-existant role (like "ROLE_doesnotexists") or changing the name in either the mutation or query mapper changed anything. Removing the @GraphQlRepository made the repo unreachable (just to make sure, there wasn't any other bean responding).

Workaround

Using a dedicated @Controller with a separate CrudRepository

CrudRepository like:

// package + imports ...

public interface Foo extends CrudRepository<Foo, UUID> {
  List<Foo> findFooByName(@Param("name") String name);
}

Controller like:

// package + imports ...

@Controller
@SchemaMapping(typeName = "Foo")
public class FooGraphQlController {

    @Autowired
    private FooRepository fooRepository;

    @PreAuthorize("hasRole('ROLE_read')")
    @QueryMapping("findFooByName")
    public List<Foo> findFooByName(@Argument("name") String name) {
        return fooRestRepository.findFooByName(name);
    }

    @PreAuthorize("hasRole('ROLE_read')")
    @QueryMapping("foo")
    Iterable<Foo> findAll() {
        return fooRestRepository.findAll();
    }

    @PreAuthorize("hasRole('ROLE_read') and hasRole('ROLE_write')")
    @MutationMapping(name = "createFoo")
    Foo createFoo(@Argument("input") FooInput fooInput) {
        return fooRestRepository.save(fooInput.builder()
                .name(Foo.getName())
                .build());
        }

    @ProjectedPayload
    public interface FooInput {
        String getName();
    }

}

The Controller approach works but adds quite some boilerplate code.

Did I miss something?

tkaefer avatar Oct 06 '22 14:10 tkaefer

I would expect this to work. Maybe the repository is not decorated with a Spring Security AOP proxy? You can check by putting a breakpoint in QuerydslDataFetcher#autoRegistrationConfigurer and inspecting the repositories passed in. Otherwise, if you could please extract a small sample, and we'll have a closer look and debug it.

rstoyanchev avatar Oct 10 '22 08:10 rstoyanchev

I just noticed this. I was only referring to @PreAuthorize in my previous comment, but I see you also have @QueryMapping and @MutationMapping on the repository, which is not expected to work. Those are only for use in controllers.

Are you aware that @GraphQlRepository makes the repository eligible for auto-registration for queries that match the return type, so it shouldn't be necessary to put @QueryMapping. We don't, however, support auto-registration for mutations currently, but it's something we could explore. It's analogous to queries, but matching by the input rather than the return type.

rstoyanchev avatar Oct 10 '22 08:10 rstoyanchev

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Oct 17 '22 08:10 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Oct 24 '22 08:10 spring-projects-issues