io.gravitee.am.identityprovider.jdbc.user.JdbcUserProvider Maven / Gradle / Ivy
/**
* 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.user;
import com.google.common.base.Strings;
import io.gravitee.am.common.oidc.StandardClaims;
import io.gravitee.am.common.utils.RandomString;
import io.gravitee.am.identityprovider.api.DefaultUser;
import io.gravitee.am.identityprovider.api.User;
import io.gravitee.am.identityprovider.api.UserProvider;
import io.gravitee.am.identityprovider.api.encoding.BinaryToTextEncoder;
import io.gravitee.am.identityprovider.jdbc.JdbcAbstractProvider;
import io.gravitee.am.identityprovider.jdbc.user.spring.JdbcUserProviderConfiguration;
import io.gravitee.am.identityprovider.jdbc.utils.ColumnMapRowMapper;
import io.gravitee.am.identityprovider.jdbc.utils.ParametersUtils;
import io.gravitee.am.service.exception.UserAlreadyExistsException;
import io.gravitee.am.service.exception.UserNotFoundException;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.Result;
import io.r2dbc.spi.Statement;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
@Import(JdbcUserProviderConfiguration.class)
public class JdbcUserProvider extends JdbcAbstractProvider implements UserProvider {
private final Pattern pattern = Pattern.compile("idp_users___");
@Autowired
private BinaryToTextEncoder binaryToTextEncoder;
@Override
protected void doStart() throws Exception {
super.doStart();
if (configuration.getAutoProvisioning()) {
LOGGER.debug("Auto provisioning of identity provider table enabled");
// for now simply get the file named .schema, more complex stuffs will be done if schema updates have to be done in the future
final String sqlScript = "database/" + configuration.getProtocol() + ".schema";
try (InputStream input = this.getClass().getClassLoader().getResourceAsStream(sqlScript);
BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
Single.fromPublisher(connectionPool.create())
.flatMapPublisher(connection -> {
final String tableExistsStatement = tableExists(configuration.getProtocol(), configuration.getUsersTable());
return query(connection, tableExistsStatement, new Object[0])
.flatMap(Result::getRowsUpdated)
.first(0)
.flatMapPublisher(total -> {
if (total == 0) {
LOGGER.debug("SQL datatable {} does not exist.", configuration.getUsersTable());
final List sqlStatements = reader.lines()
// remove empty line and comment
.filter(line -> !line.trim().isEmpty() && !line.trim().startsWith("--"))
.map(line -> {
// update table & index names
String finalLine = pattern.matcher(line).replaceAll(configuration.getUsersTable());
LOGGER.debug("Statement to execute: {}", finalLine);
return finalLine;
})
.distinct()
.collect(Collectors.toList());
LOGGER.debug("Found {} statements to execute", sqlStatements.size());
return Flowable.fromIterable(sqlStatements)
.flatMap(statement -> query(connection, statement, new Object[0]))
.flatMap(Result::getRowsUpdated);
} else {
return Flowable.empty();
}
})
.doFinally(() -> Completable.fromPublisher(connection.close()).subscribe());
})
.ignoreElements()
.blockingAwait();
} catch (Exception e) {
LOGGER.error("Unable to initialize the identity provider schema", e);
}
}
}
@Override
public Single asyncStart() {
try {
super.doStart();
} catch (Exception e) {
return Single.error(e);
}
if (configuration.getAutoProvisioning()) {
LOGGER.debug("Auto provisioning of identity provider table enabled");
// for now simply get the file named .schema, more complex stuffs will be done if schema updates have to be done in the future
final String sqlScript = "database/" + configuration.getProtocol() + ".schema";
try (InputStream input = this.getClass().getClassLoader().getResourceAsStream(sqlScript);
BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
final List sqlStatements = reader.lines()
// remove empty line and comment
.filter(line -> !line.trim().isEmpty() && !line.trim().startsWith("--"))
.map(line -> {
// update table & index names
String finalLine = pattern.matcher(line).replaceAll(configuration.getUsersTable());
LOGGER.debug("Statement to execute: {}", finalLine);
return finalLine;
})
.distinct()
.collect(Collectors.toList());
LOGGER.debug("Found {} statements to execute if SQL schema doesn't exist", sqlStatements.size());
return Single.fromPublisher(connectionPool.create())
.flatMapPublisher(connection -> {
final String tableExistsStatement = tableExists(configuration.getProtocol(), configuration.getUsersTable());
return query(connection, tableExistsStatement, new Object[0])
.flatMap(Result::getRowsUpdated)
.first(0)
.flatMapPublisher(total -> {
if (total == 0) {
LOGGER.debug("SQL datatable {} doest not exists, initialize it.", configuration.getUsersTable());
return Flowable.fromIterable(sqlStatements)
.flatMap(statement -> query(connection, statement, new Object[0]))
.flatMap(Result::getRowsUpdated);
} else {
return Flowable.empty();
}
})
.doFinally(() -> Completable.fromPublisher(connection.close()).subscribe());
}).toList()
.map(__ -> this);
} catch (Exception e) {
LOGGER.error("Unable to initialize the identity provider schema", e);
return Single.error(e);
}
}
return Single.just(this);
}
private String tableExists(String protocol, String table) {
if ("sqlserver".equalsIgnoreCase(protocol)) {
return "SELECT 1 FROM sysobjects WHERE name = '" + table + "' AND xtype = 'U'";
} else {
return "SELECT 1 FROM information_schema.tables WHERE table_name = '" + table + "'";
}
}
@Override
public Maybe findByEmail(String email) {
return selectUserByEmail(email)
.map(result -> createUser(result));
}
private Maybe
© 2015 - 2025 Weber Informatics LLC | Privacy Policy