Skip to content

Commit

Permalink
Merge pull request #72 from georchestra/refactoring_accounts_creation…
Browse files Browse the repository at this point in the history
…_and_rabbitmq

Refactor LDAP account creation functionality for better separation of concerns
  • Loading branch information
groldan authored Nov 4, 2023
2 parents 26d6f80 + b674b26 commit 53622b9
Show file tree
Hide file tree
Showing 68 changed files with 1,414 additions and 519 deletions.
11 changes: 10 additions & 1 deletion datadir/gateway/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
georchestra:
gateway:
security:
createNonExistingUsersInLDAP: false
events:
rabbitmq:
# Note usually enableRabbitmqEvents, rabbitmqHost, etc. come from georchestra's default.properties
enabled: ${enableRabbitmqEvents:false}
host: ${rabbitmqHost}
port: ${rabbitmqPort}
user: ${rabbitmqUser}
password: ${rabbitmqPassword}
oauth2:
# if enabled, make sure to have at least one OAuth2 client
# set up at spring.security.oauth2.client below
Expand All @@ -19,7 +28,7 @@ georchestra:
# Multiple LDAP data sources are supported. The first key defines a simple
# name for them. The `default` one here, disabled by default, is pre-configured
# to use Georchestra's default OpenLDAP database.
# You should usually just enable it in the georchestra dataidr's gateway.yml
# You should usually just enable it in the georchestra datadir's gateway.yml
# with georchestra.gateway.security.ldap.default.enabled: true
default:
enabled: true
Expand Down
9 changes: 0 additions & 9 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ volumes:
o: bind
device: $PWD/datadir

secrets:
slapd_password:
file: ./datadir/secrets/slapd_password.txt
geoserver_privileged_user_passwd:
file: ./datadir/secrets/geoserver_privileged_user_passwd.txt

services:
database:
image: georchestra/database:latest
Expand All @@ -28,9 +22,6 @@ services:

ldap:
image: georchestra/ldap:latest
secrets:
- slapd_password
- geoserver_privileged_user_passwd
environment:
- SLAPD_ORGANISATION=georchestra
- SLAPD_DOMAIN=georchestra.org
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 by the geOrchestra PSC
*
* This file is part of geOrchestra.
*
* geOrchestra is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* geOrchestra is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* geOrchestra. If not, see <http://www.gnu.org/licenses/>.
*/
package org.georchestra.gateway.accounts.admin;

import java.util.Optional;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;

import org.georchestra.security.model.GeorchestraUser;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public abstract class AbstractAccountsManager implements AccountManager {

private final @NonNull Consumer<AccountCreated> eventPublisher;

protected final ReadWriteLock lock = new ReentrantReadWriteLock();

@Override
public GeorchestraUser getOrCreate(@NonNull GeorchestraUser mappedUser) {
return find(mappedUser).orElseGet(() -> createIfMissing(mappedUser));
}

protected Optional<GeorchestraUser> find(GeorchestraUser mappedUser) {
lock.readLock().lock();
try {
return findInternal(mappedUser);
} finally {
lock.readLock().unlock();
}
}

protected Optional<GeorchestraUser> findInternal(GeorchestraUser mappedUser) {
if (null != mappedUser.getOAuth2ProviderId()) {
return findByOAuth2ProviderId(mappedUser.getOAuth2ProviderId());
}
return findByUsername(mappedUser.getUsername());
}

GeorchestraUser createIfMissing(GeorchestraUser mapped) {
lock.writeLock().lock();
try {
GeorchestraUser existing = findInternal(mapped).orElse(null);
if (null == existing) {
createInternal(mapped);
existing = findInternal(mapped).orElseThrow(() -> new IllegalStateException(
"User " + mapped.getUsername() + " not found right after creation"));
eventPublisher.accept(new AccountCreated(existing));
}
return existing;

} finally {
lock.writeLock().unlock();
}
}

protected abstract Optional<GeorchestraUser> findByOAuth2ProviderId(String oauth2ProviderId);

protected abstract Optional<GeorchestraUser> findByUsername(String username);

protected abstract void createInternal(GeorchestraUser mapped);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 by the geOrchestra PSC
*
* This file is part of geOrchestra.
*
* geOrchestra is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* geOrchestra is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* geOrchestra. If not, see <http://www.gnu.org/licenses/>.
*/
package org.georchestra.gateway.accounts.admin;

import org.georchestra.security.model.GeorchestraUser;

import lombok.NonNull;
import lombok.Value;

/**
* Application event published when a new account was created
*/
@Value
public class AccountCreated {

private @NonNull GeorchestraUser user;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 by the geOrchestra PSC
*
* This file is part of geOrchestra.
*
* geOrchestra is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* geOrchestra is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* geOrchestra. If not, see <http://www.gnu.org/licenses/>.
*/
package org.georchestra.gateway.accounts.admin;

import org.georchestra.gateway.security.GeorchestraUserMapper;
import org.georchestra.gateway.security.ResolveGeorchestraUserGlobalFilter;
import org.georchestra.security.model.GeorchestraUser;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;

/**
* @see CreateAccountUserCustomizer
* @see ResolveGeorchestraUserGlobalFilter
*/
public interface AccountManager {

/**
* Finds the stored user that belongs to the {@code mappedUser} or creates it if
* it doesn't exist in the users repository.
* <p>
* When a user is created, an {@link AccountCreated} event must be published to
* the {@link ApplicationEventPublisher}.
*
* @param mappedUser the user {@link ResolveGeorchestraUserGlobalFilter}
* resolved by calling
* {@link GeorchestraUserMapper#resolve(Authentication)}
* @return the stored version of the user, whether it existed or was created as
* the result of calling this method.
*/
GeorchestraUser getOrCreate(GeorchestraUser mappedUser);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 by the geOrchestra PSC
*
* This file is part of geOrchestra.
*
* geOrchestra is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* geOrchestra is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* geOrchestra. If not, see <http://www.gnu.org/licenses/>.
*/
package org.georchestra.gateway.accounts.admin;

import java.util.Objects;

import org.georchestra.gateway.security.GeorchestraUserCustomizerExtension;
import org.georchestra.security.model.GeorchestraUser;
import org.springframework.core.Ordered;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;

/**
* {@link GeorchestraUserCustomizerExtension} that
* {@link AccountManager#getOrCreate creates an account} when authenticated
* through request headers (trusted proxy feature) or through OAuth2.
*/
@RequiredArgsConstructor
public class CreateAccountUserCustomizer implements GeorchestraUserCustomizerExtension, Ordered {

private final @NonNull AccountManager accounts;

/**
* @return {@link Ordered#LOWEST_PRECEDENCE} so it runs after all other
* authentication customizations have been performed, such as setting
* additional roles from externalized configuration, etc.
*/
public @Override int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}

/**
* @return the stored version (either existing or created as result of calling
* this method) of the user account, if the {@code Authentication}
* object is either an {@link OAuth2AuthenticationToken}
*/
@Override
public @NonNull GeorchestraUser apply(@NonNull Authentication auth, @NonNull GeorchestraUser mappedUser) {
final boolean isOauth2 = auth instanceof OAuth2AuthenticationToken;
if (isOauth2) {
Objects.requireNonNull(mappedUser.getOAuth2ProviderId(), "GeorchestraUser.oAuth2ProviderId is null");
}
if (isOauth2) {
return accounts.getOrCreate(mappedUser);
}
return mappedUser;
}

}
Loading

0 comments on commit 53622b9

Please sign in to comment.