1

I inherited an app that controls access and editing of a small database. It uses Spring Security for auth. It uses a local table for users and roles.

The app provides an interface to create new users, if you have role ROLE_ADMIN. Inside the method for creating the user is a block of code like this:

    Authentication authentication =SecurityContextHolder.getContext().getAuthentication();
    accountService.createUser(getUserDetails(newUser,password,authentication),role,fullname, displayName);

I have to create new instances of this app with slightly configuration, and an empty database (except for static data). That means the user table is empty. I can only create new users if I can log in (the story about how they got the app running in the first place is confusing).

So, I defined a new bean with a @Component annotation and a @PostConstruct method which does this:

@PostConstruct
public void init() {
    List<UserInfo>  currentUserList = userAccountService.getCurrentUserList();
    if (currentUserList.isEmpty()) {
        Authentication  authentication  = SecurityContextHolder.getContext().getAuthentication();
        User    user    = new User("admin", "admin", true, true, true, true, authentication.getAuthorities());
        userAccountService.createUser(user, EstimationConstants.ROLE_ADMIN, "admin", "admin");
    }
}

When this runs, it gets a NPE because "authentication" is null. I imagine that's because it's not running as a logged-in user in the context.

How can I get this working?

David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • 4
    Instead of calling `authentication.getAuthorities()` can you not set some _default authorities_ you'd like to have? – Morfic Feb 28 '18 at 20:01
  • I managed to figure this out: https://stackoverflow.com/questions/4824395/spring-security-forward-directive-cant-forward-to-login-form/7972971#7972971 . I'll fill in the solution details later. – David M. Karr Feb 28 '18 at 21:04
  • @Morfic I only noticed this comment now. I don't really understand the choices or the effective difference. – David M. Karr Mar 21 '18 at 16:19
  • Since this is a _"one time `userless` execution"_, remove the part related to the authentication data, and just go `new User("admin", "admin", true, true, true, true, Arrays.asList(ADMIN, DEV_OPS, etc))`. Makes it pretty straight forward and easy to understand the intention – Morfic Mar 21 '18 at 16:33

1 Answers1

0

The key code to get a valid security context was the following:

        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority(EstimationConstants.ROLE_ADMIN));
        Authentication  authentication  = new UsernamePasswordAuthenticationToken(userName, password, authorities);
        SecurityContextHolder.getContext().setAuthentication(authentication);
David M. Karr
  • 14,317
  • 20
  • 94
  • 199
  • Out of curiosity, why don't you put the authorities directly in your `@Post` method instead of relying on `SecurityContextHolder.getContext()`? As it is now it's somewhat "_hidden_", difficult for someone new to immediately understand where the authorities are provisioned. – Morfic Mar 20 '18 at 15:25
  • Sorry, I don't understand what you're saying. – David M. Karr Mar 21 '18 at 16:20
  • The code you posted as the answer, where does it belong to, and how does it relate to the `@PostConstruct` method from the question? – Morfic Mar 21 '18 at 16:28
  • It goes in the same method, effectively replacing the line that creates the "authentication" object. – David M. Karr Mar 21 '18 at 16:52