io.gravitee.am.identityprovider.jdbc.authentication.JdbcAuthenticationProvider Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* 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 io.gravitee.am.identityprovider.jdbc.authentication;
import io.gravitee.am.common.exception.authentication.BadCredentialsException;
import io.gravitee.am.common.exception.authentication.InternalAuthenticationServiceException;
import io.gravitee.am.common.exception.authentication.UsernameNotFoundException;
import io.gravitee.am.common.oidc.StandardClaims;
import io.gravitee.am.identityprovider.api.Authentication;
import io.gravitee.am.identityprovider.api.AuthenticationContext;
import io.gravitee.am.identityprovider.api.AuthenticationProvider;
import io.gravitee.am.identityprovider.api.DefaultUser;
import io.gravitee.am.identityprovider.api.IdentityProviderGroupMapper;
import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
import io.gravitee.am.identityprovider.api.SimpleAuthenticationContext;
import io.gravitee.am.identityprovider.api.User;
import io.gravitee.am.identityprovider.api.UserCredentialEvaluation;
import io.gravitee.am.identityprovider.jdbc.JdbcAbstractProvider;
import io.gravitee.am.identityprovider.jdbc.authentication.spring.JdbcAuthenticationProviderConfiguration;
import io.gravitee.am.identityprovider.jdbc.utils.ColumnMapRowMapper;
import io.gravitee.am.identityprovider.jdbc.utils.ParametersUtils;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.Optional.ofNullable;
/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
@Import(JdbcAuthenticationProviderConfiguration.class)
public class JdbcAuthenticationProvider extends JdbcAbstractProvider implements AuthenticationProvider {
@Autowired
private IdentityProviderMapper mapper;
@Autowired
private IdentityProviderRoleMapper roleMapper;
@Autowired
private IdentityProviderGroupMapper groupMapper;
@Override
public Maybe loadUserByUsername(Authentication authentication) {
final String username = authentication.getPrincipal().toString();
final String presentedPassword = authentication.getCredentials().toString();
return selectUserByMultipleField(username)
.toList()
.flatMapPublisher(users -> {
if (users.isEmpty()) {
return Flowable.error(new UsernameNotFoundException(username));
}
return Flowable.fromIterable(users);
})
.map(result -> {
// check password
String password = String.valueOf(result.get(configuration.getPasswordAttribute()));
if (password == null) {
LOGGER.debug("Authentication failed: password is null");
return new UserCredentialEvaluation<>(false, result);
}
if (configuration.isUseDedicatedSalt()) {
String hash = String.valueOf(result.get(configuration.getPasswordSaltAttribute()));
if (!passwordEncoder.matches(presentedPassword, password, hash)) {
LOGGER.debug("Authentication failed: password does not match stored value");
return new UserCredentialEvaluation<>(false, result);
}
} else {
if (!passwordEncoder.matches(presentedPassword, password)) {
LOGGER.debug("Authentication failed: password does not match stored value");
return new UserCredentialEvaluation<>(false, result);
}
}
return new UserCredentialEvaluation<>(true, result);
})
.toList()
.flatMapMaybe(userEvaluations -> {
final var validUsers = userEvaluations.stream().filter(UserCredentialEvaluation::isPasswordValid).collect(Collectors.toList());
if (validUsers.size() > 1) {
LOGGER.debug("Authentication failed: multiple accounts with same credentials");
return Maybe.error(new BadCredentialsException("Bad credentials"));
}
var userEvaluation = !validUsers.isEmpty() ? validUsers.get(0) : userEvaluations.get(0);
var user = this.createUser(authentication.getContext(), userEvaluation.getUser());
ofNullable(authentication.getContext()).ifPresent(auth -> auth.set(ACTUAL_USERNAME, user.getUsername()));
return userEvaluation.isPasswordValid() ?
Maybe.just(user) :
Maybe.error(new BadCredentialsException("Bad credentials"));
});
}
private Flowable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy