All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.arbeitsagentur.opdt.keycloak.cassandra.connection.DefaultCassandraConnectionProviderFactory Maven / Gradle / Ivy

/*
 * Copyright 2022 IT-Systemhaus der Bundesagentur fuer Arbeit
 *
 * 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 de.arbeitsagentur.opdt.keycloak.cassandra.connection;

import com.datastax.oss.driver.api.core.ConsistencyLevel;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.querybuilder.SchemaBuilder;
import com.datastax.oss.driver.api.querybuilder.schema.CreateKeyspace;
import com.datastax.oss.driver.internal.core.type.codec.extras.enums.EnumNameCodec;
import com.datastax.oss.driver.internal.core.type.codec.extras.json.JsonCodec;
import com.google.auto.service.AutoService;
import de.arbeitsagentur.opdt.keycloak.cassandra.CassandraJsonSerialization;
import de.arbeitsagentur.opdt.keycloak.cassandra.CompositeRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.ManagedCompositeCassandraRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.authSession.persistence.AuthSessionMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.authSession.persistence.AuthSessionMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.authSession.persistence.AuthSessionRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.authSession.persistence.CassandraAuthSessionRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.cache.L1CacheInterceptor;
import de.arbeitsagentur.opdt.keycloak.cassandra.client.persistence.CassandraClientRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.client.persistence.ClientMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.client.persistence.ClientMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.client.persistence.ClientRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.clientScope.persistence.CassandraClientScopeRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.clientScope.persistence.ClientScopeMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.clientScope.persistence.ClientScopeMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.clientScope.persistence.ClientScopeRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.clientScope.persistence.entities.ClientScopeValue;
import de.arbeitsagentur.opdt.keycloak.cassandra.group.persistence.CassandraGroupRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.group.persistence.GroupMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.group.persistence.GroupMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.group.persistence.GroupRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.group.persistence.entities.GroupValue;
import de.arbeitsagentur.opdt.keycloak.cassandra.loginFailure.persistence.CassandraLoginFailureRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.loginFailure.persistence.LoginFailureMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.loginFailure.persistence.LoginFailureMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.loginFailure.persistence.LoginFailureRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.realm.persistence.CassandraRealmRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.realm.persistence.RealmMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.realm.persistence.RealmMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.realm.persistence.RealmRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.CassandraRoleRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.RoleMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.RoleMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.RoleRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.role.persistence.entities.RoleValue;
import de.arbeitsagentur.opdt.keycloak.cassandra.singleUseObject.persistence.CassandraSingleUseObjectRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.singleUseObject.persistence.SingleUseObjectMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.singleUseObject.persistence.SingleUseObjectMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.singleUseObject.persistence.SingleUseObjectRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.user.persistence.CassandraUserRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.user.persistence.UserMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.user.persistence.UserMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.user.persistence.UserRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.user.persistence.entities.CredentialValue;
import de.arbeitsagentur.opdt.keycloak.cassandra.userSession.persistence.CassandraUserSessionRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.userSession.persistence.UserSessionMapper;
import de.arbeitsagentur.opdt.keycloak.cassandra.userSession.persistence.UserSessionMapperBuilder;
import de.arbeitsagentur.opdt.keycloak.cassandra.userSession.persistence.UserSessionRepository;
import de.arbeitsagentur.opdt.keycloak.cassandra.userSession.persistence.entities.AuthenticatedClientSessionValue;
import lombok.extern.jbosslog.JBossLog;
import org.cognitor.cassandra.migration.Database;
import org.cognitor.cassandra.migration.MigrationConfiguration;
import org.cognitor.cassandra.migration.MigrationRepository;
import org.cognitor.cassandra.migration.MigrationTask;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.sessions.CommonClientSessionModel;

import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@JBossLog
@AutoService(CassandraConnectionProviderFactory.class)
public class DefaultCassandraConnectionProviderFactory implements CassandraConnectionProviderFactory, EnvironmentDependentProviderFactory {
    public static final String PROVIDER_ID = "default";
    private CqlSession cqlSession;
    private CompositeRepository repository;

    @Override
    public CassandraConnectionProvider create(KeycloakSession session) {
        return new CassandraConnectionProvider() {
            @Override
            public CqlSession getCqlSession() {
                return cqlSession;
            }

            @Override
            public CompositeRepository getRepository() {
                L1CacheInterceptor intercepted = new L1CacheInterceptor(session, repository);
                return (CompositeRepository) Proxy.newProxyInstance(Thread.currentThread()
                    .getContextClassLoader(), new Class[]{CompositeRepository.class}, intercepted);
            }

            @Override
            public void close() {

            }
        };
    }

    @Override
    public void init(Config.Scope scope) {
        // kc.spi.cassandra-connection.default.contactPoints
        // Env: KC_SPI_CASSANDRA_CONNECTION_DEFAULT_CONTACT_POINTS

        String contactPoints = scope.get("contactPoints");
        log.infov("Init CassandraProviderFactory with contactPoints {0}", contactPoints);

        int port = Integer.parseInt(scope.get("port"));
        String localDatacenter = scope.get("localDatacenter");
        String keyspace = scope.get("keyspace");
        String username = scope.get("username");
        String password = scope.get("password");
        int replicationFactor = Integer.parseInt(scope.get("replicationFactor"));

        List contactPointsList =
            Arrays.stream(contactPoints.split(","))
                .map(cp -> new InetSocketAddress(cp, port))
                .collect(Collectors.toList());

        if (scope.getBoolean("createKeyspace", true)) {
            log.info("Create keyspace (if not exists)...");
            createDbIfNotExists(contactPointsList, username, password, localDatacenter, keyspace, replicationFactor);
        } else {
            log.info("Skipping create keyspace, assuming keyspace and tables already exist...");
        }

        cqlSession = CqlSession.builder()
            .addContactPoints(contactPointsList)
            .withAuthCredentials(username, password)
            .withLocalDatacenter(localDatacenter)
            .withKeyspace(keyspace)
            .addTypeCodecs(new EnumNameCodec<>(UserSessionModel.State.class))
            .addTypeCodecs(new EnumNameCodec<>(UserSessionModel.SessionPersistenceState.class))
            .addTypeCodecs(new EnumNameCodec<>(CommonClientSessionModel.ExecutionStatus.class))
            .addTypeCodecs(new JsonCodec<>(RoleValue.class, CassandraJsonSerialization.getMapper()))
            .addTypeCodecs(new JsonCodec<>(GroupValue.class, CassandraJsonSerialization.getMapper()))
            .addTypeCodecs(new JsonCodec<>(CredentialValue.class, CassandraJsonSerialization.getMapper()))
            .addTypeCodecs(new JsonCodec<>(AuthenticatedClientSessionValue.class, CassandraJsonSerialization.getMapper()))
            .addTypeCodecs(new JsonCodec<>(ClientScopeValue.class, CassandraJsonSerialization.getMapper()))
            .build();

        repository = createRepository(cqlSession);
    }

    private void createDbIfNotExists(List contactPointsList, String username, String password, String localDatacenter, String keyspace, int replicationFactor) {
        try (CqlSession createKeyspaceSession =
                 CqlSession.builder()
                     .addContactPoints(contactPointsList)
                     .withAuthCredentials(username, password)
                     .withLocalDatacenter(localDatacenter)
                     .build()) {
            createKeyspaceIfNotExists(createKeyspaceSession, keyspace, replicationFactor);
        }

        log.info("Create schema...");
        try (CqlSession createKeyspaceSession =
                 CqlSession.builder()
                     .addContactPoints(contactPointsList)
                     .withAuthCredentials(username, password)
                     .withLocalDatacenter(localDatacenter)
                     .withKeyspace(keyspace)
                     .build()) {
            createTables(createKeyspaceSession, keyspace);
        }
    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {

    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }

    @Override
    public boolean isSupported() {
        return true;
    }

    @Override
    public void close() {
        cqlSession.close();
    }

    private void createKeyspaceIfNotExists(CqlSession cqlSession, String keyspaceName, int replicationFactor) {
        CreateKeyspace createKeyspace = SchemaBuilder.createKeyspace(keyspaceName)
            .ifNotExists()
            .withNetworkTopologyStrategy(Map.of("replication_factor", replicationFactor)); // special dc-name to activate autodiscovery

        cqlSession.execute(createKeyspace.build());
        cqlSession.close();
    }

    private void createTables(CqlSession cqlSession, String keyspace) {
        MigrationConfiguration mgConfig = new MigrationConfiguration()
            .withKeyspaceName(keyspace);
        Database database = new Database(cqlSession, mgConfig)
            .setConsistencyLevel(ConsistencyLevel.ALL);
        MigrationTask migration = new MigrationTask(database, new MigrationRepository());
        migration.migrate();
    }

    private CompositeRepository createRepository(CqlSession cqlSession) {
        UserMapper userMapper = new UserMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        UserRepository userRepository = new CassandraUserRepository(userMapper.userDao());

        RoleMapper roleMapper = new RoleMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        RoleRepository roleRepository = new CassandraRoleRepository(roleMapper.roleDao());

        GroupMapper groupMapper = new GroupMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        GroupRepository groupRepository = new CassandraGroupRepository(groupMapper.groupDao());

        RealmMapper realmMapper = new RealmMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        RealmRepository realmRepository = new CassandraRealmRepository(realmMapper.realmDao());

        UserSessionMapper userSessionMapper = new UserSessionMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        UserSessionRepository userSessionRepository = new CassandraUserSessionRepository(userSessionMapper.userSessionDao());

        AuthSessionMapper authSessionMapper = new AuthSessionMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        AuthSessionRepository authSessionRepository = new CassandraAuthSessionRepository(authSessionMapper.authSessionDao());

        LoginFailureMapper loginFailureMapper = new LoginFailureMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        LoginFailureRepository loginFailureRepository = new CassandraLoginFailureRepository(loginFailureMapper.loginFailureDao());

        SingleUseObjectMapper singleUseObjectMapper = new SingleUseObjectMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        SingleUseObjectRepository singleUseObjectRepository = new CassandraSingleUseObjectRepository(singleUseObjectMapper.singleUseObjectDao());

        ClientMapper clientMapper = new ClientMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        ClientRepository clientRepository = new CassandraClientRepository(clientMapper.clientDao());

        ClientScopeMapper clientScopeMapper = new ClientScopeMapperBuilder(cqlSession).withSchemaValidationEnabled(false)
            .build();
        ClientScopeRepository clientScopeRepository = new CassandraClientScopeRepository(clientScopeMapper.clientScopeDao());

        ManagedCompositeCassandraRepository cassandraRepository = new ManagedCompositeCassandraRepository();
        cassandraRepository.setRoleRepository(roleRepository);
        cassandraRepository.setGroupRepository(groupRepository);
        cassandraRepository.setUserRepository(userRepository);
        cassandraRepository.setRealmRepository(realmRepository);
        cassandraRepository.setUserSessionRepository(userSessionRepository);
        cassandraRepository.setAuthSessionRepository(authSessionRepository);
        cassandraRepository.setLoginFailureRepository(loginFailureRepository);
        cassandraRepository.setSingleUseObjectRepository(singleUseObjectRepository);
        cassandraRepository.setClientRepository(clientRepository);
        cassandraRepository.setClientScopeRepository(clientScopeRepository);

        return cassandraRepository;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy