Spring boot (Webflux): как обеспечить просмотр полей ресурсов на основе ролей для json и ограничить поля редактирования владельцами ресурсов в Spring boot 2.2.x? - PullRequest
0 голосов
/ 07 мая 2020

TL; DR : Как обеспечить представление поля ресурса на основе ролей (json do c) и ограничить редактирование определенных полей только владельцем ресурса?

ДЕТАЛИ

Скажем, у меня есть документ из хранилища документов (couchbase) для различных «пользователей» как

@Document
@Data
public class UserProfile{
    @Id
    private String id;// this is same the ID of the user in Identity provider (keycloak). also = 
    //Principle.getName
    private String name;
    private String phone;
    private String email;
    private String somePersonalId;
    private String myDescription;

}

и некоторые роли authz «ROLE_ADMIN», «ROLE_VERIFIED_VISITOR», «ROLE_SIGNED_UP_USER» где

ROLE_ADMIN может просматривать все поля любого «UserProfile»,

ROLE_VERIFIED_VISITOR может просматривать имя, телефон, адрес электронной почты и myDescription

ROLE_SIGNED_UP_USER может просматривать имя и myDescription

«Пользователь», владеющий данным «UserProfile», может просматривать все свои поля, кроме «id». Но ему разрешено редактировать только поля телефона и myDescription.

Обратите внимание, что в этом Json Do c может быть много таких полей и вложенных документов, и следование подходу «DTO» может быть слишком многословным. Для примера я привел простой документ.

Q1. Как обеспечить доступ к полям на основе ролей для различных ролей?

Я пробовал определять JsonViews, как показано ниже, и следуя этому подходу.

public class ProfileViews {
    public static class SignedUpView{}
    public static class VerifiedView extends SignedUpView{}
    public static class SelfView extends VerifiedView{}
    public static class AdminView extends SelfView{}
}

но что лучше всего делать "реактивным" (webflux) способом? [если вообще имеет смысл сделать его реактивным]

Q2. Как мне запретить пользователю редактировать определенные поля (адрес электронной почты, имя, somePersonalId в данном случае), но разрешить ему их просматривать?

У меня есть API исправлений для редактирования полей, как указано в коде ниже. Этот api принимает часть объекта UserProfile в качестве входных данных json и объединяет поля, переданные во входных данных json, с существующим UserProfile, имеющим «id» = {id}.

@PatchMapping(value="/profiles/{id}", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Mono<ResponseEntity<Profile>> patch(@PathVariable("id") String id, 
    @RequestBody UserProfile patchRequest,Principal principal){
    repo
                .findById(id)
                .filter(profile -> !Objects.isNull(profile))
                .filter(profile -> profile.getId().equals(principal.getName()))
                .filter(profile->id.equals(principal.getName()))
                .flatMap(profile->{
                    profile=APIUtils.mergePatch(profile,patchRequest,UserProfile.class);
                    return repo.save(profile);
                })
                .doOnSuccess(profile->{
                    log.info("Profile is patched for profile with id {}", id);
                    publisher.publishEvent(new ProfileUpdatedEvent(this,profile));// IGNORE THIS.
                })
                .flatMap(profileMono ->Mono.just( ResponseEntity.status(HttpStatus.OK).location(URI.create("/profiles/"+id)).body(profileMono)))
                .defaultIfEmpty(ResponseEntity.status(HttpStatus.NOT_FOUND).body(new Profile()));
    }
// APIUtils.mergePatch Method. "JsonMergePatch" package : //com.github.fge.jsonpatch.mergepatch.JsonMergePatch;

    @SneakyThrows // from lombok
    public static <T> T mergePatch(T source, T patch, Class<T> clazz){
        JsonNode node = objectMapper.convertValue(source, JsonNode.class);
        JsonNode patchNode = objectMapper.readTree(pojoToString(patch));
        JsonMergePatch mergePatch = JsonMergePatch.fromJson(patchNode);
        node = mergePatch.apply(node);
        return objectMapper.treeToValue(node, clazz);
    }

    @SneakyThrows
    public   <T> String pojoToString(T t){
        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(t);
    }

Пожалуйста, дайте мне знать, если потребуется дополнительная информация / правки. Заранее спасибо!

...