0

Is it possible to secure update of certain entity properties using spring security.? for example if I have a user entity , I want ROLE_USER to be able to modify/update all the properties of user except active column which would be updatable by ROLE_ADMIN.

ArslanAnjum
  • 1,674
  • 2
  • 17
  • 31
  • Yes it is possible, take a look at how using `@PreAuthorize` and `@PostAuthorize`. – akuma8 Aug 14 '17 at 08:59
  • can you provide an example please – ArslanAnjum Aug 14 '17 at 19:18
  • Here : https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#method-security-expressions – akuma8 Aug 14 '17 at 21:15
  • didnt help. I am using spring data rest. I annotated the save method with @PreAuthorize("@screenSecurityService.canUpdate(#screen)") but how do i get which properties are being updated? – ArslanAnjum Aug 15 '17 at 06:02

1 Answers1

0

I haven't found any solution strictly provided by Spring Security Yet. However I have achieved what I wanted as follows by using custom annotation @SecureUpdate() on entity variables:-

Following is my paging and sorting repository:-

@Transactional("jpaTXManager")
public interface ScreenRepo extends PagingAndSortingRepository<Screen, Integer>{

    @Override
    @PreAuthorize("@patchSecurityService.canUpdate(#screen)")
    Screen save(@Param("screen")Screen screen);
}

PatchSecurityService.java

@Service("patchSecurityService")
public class PatchSecurityService {

    public boolean canUpdate(Object obj){

        List<GrantedAuthority> authorities = 
                (List<GrantedAuthority>) SecurityContextHolder
        .getContext()
        .getAuthentication()
        .getAuthorities();

        if (obj instanceof OEntity){
            OEntity oEntity = (OEntity) obj;
            return oEntity.canUpdate(authorities);
        }else{
            return true;
        }
    }
}

OEntity.java

@MappedSuperclass
public class OEntity<T> {

    @Transient
    T originalObj;

    @Transient
    public T getOriginalObj(){
        return this.originalObj;
    }

    @PostLoad
    public void onLoad(){
        ObjectMapper mapper = new ObjectMapper();
        try {
            String serialized = mapper.writeValueAsString(this);
            this.originalObj = (T) mapper.readValue(serialized, this.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean canUpdate(List<GrantedAuthority> authorities){
        for (Field field : this.getClass().getDeclaredFields()){
            SecureUpdate secureUpdate = field.getAnnotation(SecureUpdate.class);
            if (secureUpdate != null){
                try{
                    field.setAccessible(true);

                    Object persistedField = field.get(this);
                    Object originalField = field.get(originalObj);

                    String[] allowedRoles = secureUpdate.value();
                    if (!persistedField.equals(originalField)){
                        boolean canUpdate = false;
                        for (String role : allowedRoles){
                            for (GrantedAuthority authority : authorities){
                                if (authority.getAuthority().equalsIgnoreCase(role)){
                                    return true;
                                }
                            }
                        }
                        return false;
                    }
                }catch(Exception e){
                    System.out.println(e.getMessage());
                }
            }
        }

        return true;
    }
}

@SecureUpdate

@Documented
@Target(FIELD)
@Retention(RUNTIME)
public @interface SecureUpdate {
    String[] value();
}

and finally entity class (Screen.class)

@Entity
@Table(name="screen",schema="public")
@JsonIgnoreProperties(ignoreUnknown=true)
public class Screen extends OEntity<Screen>{

    Integer screenId;
    String screenName;

    @SecureUpdate({"ROLE_CLIENT"})
    String address;

    ScreenType screenType;

    @SecureUpdate({"ROLE_ADMIN"})
    ScreenSize screenSize;

    BigDecimal latitude;
    BigDecimal longitude;
    Boolean active;
    AppUser appUser;

    .......Constructor, Getters and Setters...
}
ArslanAnjum
  • 1,674
  • 2
  • 17
  • 31