How to achieve row level authorization in spring-boot?

Viewed 83

Assuming I've the following endpoints in spring boot

GET /todo

DELETE /todo/{id}

How can ensure that only entries for the userid are returned and that the user can only update his own todos? I've a populated Authentication object. Is there any build in way I can use? Or just make sure to always call findXyzByIdAndUserId where userid is always retrieved from the Principal?

I'm a bit worried about the possibility to forget the check and displaying entries from other users.

1 Answers

My approach to this would be a 3 way implementation: (using jpa & hibernate)

  • a user request context
  • a mapped superclass to get your context
  • a statement inspector to inject your userid

For example:

public final class UserRequestContext {
  public static String getUserId() {
    // code to retrieve your userid and throw when there is none!
    if (userId == null) throw new IllegalStateException("userid null");
    return userId;
  }
}
@MappedSuperclass
public class UserResolver {
  public static final String USER_RESOLVER = "USER_RESOLVER";

  @Access(AccessType.PROPERTY)
  public String getUserId() {
    return UserRequestContext.getUserId();
  }

}
@Component
public class UserInspector implements StatementInspector {
  @Override
  public String inspect(String statement) {
    if (statement.contains(UserResolver.USER_RESOLVER)) {
       statement = statement.replace(UserResolver.USER_RESOLVER, "userId = '" + UserRequestContext.getUserId() + "'" );
    }
    return sql;
  }

  @Bean
  public HibernatePropertyCustomizer hibernatePropertyCustomizer() {
    return hibernateProperies -> hibernateProperties.put("hibernate.session_factory.statement_inspector", 
    UserInspector.class.getName());
  }
}

So your Entity looks like this:

@Entity
...
@Where(clause = UserResolver.USER_RESOLVER)
public class Todo extends UserResolver {
   ...
}
Related