SecurityContextUserServiceImpl.java
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openwms.core.uaa.impl;
import org.ameba.annotation.Measured;
import org.ameba.annotation.TxService;
import org.ehcache.core.Ehcache;
import org.openwms.core.uaa.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import static java.time.ZonedDateTime.now;
import static java.util.Arrays.asList;
/**
* A SecurityContextUserServiceImpl extends Spring {@link UserDetailsService} to
* read {@code User}s and {@code Role}s from the persistent storage and wraps them into security objects.
*
* @author <a href="mailto:russelltina@users.sourceforge.net">Tina Russell</a>
*/
@TxService
class SecurityContextUserServiceImpl implements UserDetailsService, ApplicationListener<UserEvent> {
private final String systemUsername;
private final UserService userService;
private final UserCache userCache;
private final Ehcache cache;
private final PasswordEncoder enc;
public SecurityContextUserServiceImpl(
@Value("${owms.security.system.username:}") String systemUsername,
UserService userService,
@Autowired(required = false) UserCache userCache,
@Autowired(required = false) Ehcache cache,
PasswordEncoder enc
) {
this.systemUsername = systemUsername == null ? SystemUser.SYSTEM_USERNAME : systemUsername;
this.userService = userService;
this.userCache = userCache;
this.cache = cache;
this.enc = enc;
}
/**
* {@inheritDoc}
*/
@Override
public void onApplicationEvent(UserEvent event) {
if (cache != null) {
cache.clear();
}
}
/**
* {@inheritDoc}
*
* Implemented as read-only transactional
**/
@Transactional(readOnly = true)
@Override
@Measured
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
var ud = userCache == null ? null : userCache.getUserFromCache(username);
if (null == ud) {
if (systemUsername.equals(username)) {
var user = userService.createSystemUser();
ud = new SecureUser(
systemUsername,
enc.encode(user.getPassword()),
true,
true,
true,
true,
asList(new SecurityObjectAuthority(SystemUser.SYSTEM_ROLE_NAME))
);
} else {
var user = userService
.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User with username [%s] not found", username)));
ud = new SecureUser(
username,
user.getPassword(),
user.getExpirationDate() == null || user.getExpirationDate().isAfter(now()),
!user.isLocked(),
true,
user.isEnabled(),
user.getGrants().stream().map(SecurityObjectAuthority::new).toList()
);
}
if (userCache != null) {
userCache.putUserInCache(ud);
}
}
return ud;
}
}