Create a scheduled task to automatically deactivate accounts
This job will perform scheduled sweeps of accounts registered in the Gerrit to deactivate those which are reported as inactive by the auth backend. This job is by default disabled. Currently, only the LDAP implementation is covered. Change-Id: I4ecdc08df477960f0e884d7e31e174ad6d47e6e0
This commit is contained in:
parent
c24f7246dd
commit
e2877bb28d
@ -638,6 +638,11 @@ updated to be inactive and the login attempt will be blocked. Users enabling thi
|
|||||||
should ensure that their authentication back-end is supported. Currently, only
|
should ensure that their authentication back-end is supported. Currently, only
|
||||||
strict 'LDAP' authentication is supported.
|
strict 'LDAP' authentication is supported.
|
||||||
+
|
+
|
||||||
|
In addition, if this parameter is not set, or false, the corresponding scheduled
|
||||||
|
task to deactivate inactive Gerrit accounts will also be disabled. If this
|
||||||
|
parameter is set to true, users should also consider configuring the
|
||||||
|
link:#accountDeactivation[accountDeactivation] section appropriately.
|
||||||
|
+
|
||||||
By default, false.
|
By default, false.
|
||||||
|
|
||||||
[[cache]]
|
[[cache]]
|
||||||
@ -4550,6 +4555,44 @@ on the server. One or more groups can be set.
|
|||||||
If no groups are added, any user will be allowed to execute
|
If no groups are added, any user will be allowed to execute
|
||||||
'upload-pack' on the server.
|
'upload-pack' on the server.
|
||||||
|
|
||||||
|
[[accountDeactivation]]
|
||||||
|
=== Section accountDeactivation
|
||||||
|
|
||||||
|
Configures the parameters for the scheduled task to sweep and deactivate Gerrit
|
||||||
|
accounts according to their status reported by the auth backend. Currently only
|
||||||
|
supported for LDAP backends.
|
||||||
|
|
||||||
|
[[accountDeactivation.startTime]]accountDeactivation.startTime::
|
||||||
|
+
|
||||||
|
Start time to define the first execution of account deactivations.
|
||||||
|
If the configured `'accountDeactivation.interval'` is shorter than `'accountDeactivation.startTime - now'`
|
||||||
|
the start time will be preponed by the maximum integral multiple of
|
||||||
|
`'accountDeactivation.interval'` so that the start time is still in the future.
|
||||||
|
+
|
||||||
|
----
|
||||||
|
<day of week> <hours>:<minutes>
|
||||||
|
or
|
||||||
|
<hours>:<minutes>
|
||||||
|
|
||||||
|
<day of week> : Mon, Tue, Wed, Thu, Fri, Sat, Sun
|
||||||
|
<hours> : 00-23
|
||||||
|
<minutes> : 0-59
|
||||||
|
----
|
||||||
|
|
||||||
|
[[accountDeactivation.interval]]accountDeactivation.interval::
|
||||||
|
+
|
||||||
|
Interval for periodic repetition of triggering account deactivation sweeps.
|
||||||
|
The interval must be larger than zero. The following suffixes are supported
|
||||||
|
to define the time unit for the interval:
|
||||||
|
+
|
||||||
|
* `s, sec, second, seconds`
|
||||||
|
* `m, min, minute, minutes`
|
||||||
|
* `h, hr, hour, hours`
|
||||||
|
* `d, day, days`
|
||||||
|
* `w, week, weeks` (`1 week` is treated as `7 days`)
|
||||||
|
* `mon, month, months` (`1 month` is treated as `30 days`)
|
||||||
|
* `y, year, years` (`1 year` is treated as `365 days`)
|
||||||
|
|
||||||
[[urlAlias]]
|
[[urlAlias]]
|
||||||
=== Section urlAlias
|
=== Section urlAlias
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ import com.google.gerrit.pgm.util.RuntimeShutdown;
|
|||||||
import com.google.gerrit.pgm.util.SiteProgram;
|
import com.google.gerrit.pgm.util.SiteProgram;
|
||||||
import com.google.gerrit.server.LibModuleLoader;
|
import com.google.gerrit.server.LibModuleLoader;
|
||||||
import com.google.gerrit.server.StartupChecks;
|
import com.google.gerrit.server.StartupChecks;
|
||||||
|
import com.google.gerrit.server.account.AccountDeactivator;
|
||||||
import com.google.gerrit.server.account.InternalAccountDirectory;
|
import com.google.gerrit.server.account.InternalAccountDirectory;
|
||||||
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
|
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
|
||||||
import com.google.gerrit.server.change.ChangeCleanupRunner;
|
import com.google.gerrit.server.change.ChangeCleanupRunner;
|
||||||
@ -461,6 +462,7 @@ public class Daemon extends SiteProgram {
|
|||||||
});
|
});
|
||||||
modules.add(new GarbageCollectionModule());
|
modules.add(new GarbageCollectionModule());
|
||||||
if (!slave) {
|
if (!slave) {
|
||||||
|
modules.add(new AccountDeactivator.Module());
|
||||||
modules.add(new ChangeCleanupRunner.Module());
|
modules.add(new ChangeCleanupRunner.Module());
|
||||||
}
|
}
|
||||||
modules.addAll(LibModuleLoader.loadModules(cfgInjector));
|
modules.addAll(LibModuleLoader.loadModules(cfgInjector));
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright (C) 2017 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// 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 com.google.gerrit.server.account;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.config.ScheduleConfig.MISSING_CONFIG;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.events.LifecycleListener;
|
||||||
|
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.config.ScheduleConfig;
|
||||||
|
import com.google.gerrit.server.git.WorkQueue;
|
||||||
|
import com.google.gerrit.server.query.account.AccountPredicates;
|
||||||
|
import com.google.gerrit.server.query.account.InternalAccountQuery;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/** Runnable to enable scheduling account deactivations to run periodically */
|
||||||
|
public class AccountDeactivator implements Runnable {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AccountDeactivator.class);
|
||||||
|
|
||||||
|
public static class Module extends LifecycleModule {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
listener().to(Lifecycle.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Lifecycle implements LifecycleListener {
|
||||||
|
private final WorkQueue queue;
|
||||||
|
private final AccountDeactivator deactivator;
|
||||||
|
private final boolean supportAutomaticAccountActivityUpdate;
|
||||||
|
private final ScheduleConfig scheduleConfig;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Lifecycle(WorkQueue queue, AccountDeactivator deactivator, @GerritServerConfig Config cfg) {
|
||||||
|
this.queue = queue;
|
||||||
|
this.deactivator = deactivator;
|
||||||
|
scheduleConfig = new ScheduleConfig(cfg, "accountDeactivation");
|
||||||
|
supportAutomaticAccountActivityUpdate =
|
||||||
|
cfg.getBoolean("auth", "autoUpdateAccountActiveStatus", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
if (!supportAutomaticAccountActivityUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long interval = scheduleConfig.getInterval();
|
||||||
|
long delay = scheduleConfig.getInitialDelay();
|
||||||
|
if (delay == MISSING_CONFIG && interval == MISSING_CONFIG) {
|
||||||
|
log.info("Ignoring missing accountDeactivator schedule configuration");
|
||||||
|
} else if (delay < 0 || interval <= 0) {
|
||||||
|
log.warn(
|
||||||
|
String.format(
|
||||||
|
"Ignoring invalid accountDeactivator schedule configuration: %s", scheduleConfig));
|
||||||
|
} else {
|
||||||
|
queue
|
||||||
|
.getDefaultQueue()
|
||||||
|
.scheduleAtFixedRate(deactivator, delay, interval, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
// handled by WorkQueue.stop() already
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Provider<InternalAccountQuery> accountQueryProvider;
|
||||||
|
private final Realm realm;
|
||||||
|
private final SetInactiveFlag sif;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountDeactivator(
|
||||||
|
Provider<InternalAccountQuery> accountQueryProvider, SetInactiveFlag sif, Realm realm) {
|
||||||
|
this.accountQueryProvider = accountQueryProvider;
|
||||||
|
this.sif = sif;
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log.debug("Running account deactivations");
|
||||||
|
try {
|
||||||
|
int numberOfAccountsDeactivated = 0;
|
||||||
|
for (AccountState acc : accountQueryProvider.get().query(AccountPredicates.isActive())) {
|
||||||
|
log.debug("processing account " + acc.getUserName());
|
||||||
|
if (acc.getUserName() != null && !realm.isActive(acc.getUserName())) {
|
||||||
|
sif.deactivate(acc.getAccount().getId());
|
||||||
|
log.debug("deactivated accout " + acc.getUserName());
|
||||||
|
numberOfAccountsDeactivated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info(
|
||||||
|
"Deactivations complete, {} account(s) were deactivated", numberOfAccountsDeactivated);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to deactivate inactive accounts " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "account deactivator";
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,8 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.security.auth.login.LoginException;
|
||||||
|
|
||||||
public interface Realm {
|
public interface Realm {
|
||||||
/** Can the end-user modify this field of their own account? */
|
/** Can the end-user modify this field of their own account? */
|
||||||
@ -45,4 +47,15 @@ public interface Realm {
|
|||||||
* into an email address, and then locate the user by that email address.
|
* into an email address, and then locate the user by that email address.
|
||||||
*/
|
*/
|
||||||
Account.Id lookup(String accountName) throws IOException;
|
Account.Id lookup(String accountName) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the account is active.
|
||||||
|
* @throws NamingException
|
||||||
|
* @throws LoginException
|
||||||
|
* @throws AccountException
|
||||||
|
*/
|
||||||
|
default boolean isActive(@SuppressWarnings("unused") String username)
|
||||||
|
throws LoginException, NamingException, AccountException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +323,19 @@ class LdapRealm extends AbstractRealm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive(String username)
|
||||||
|
throws LoginException, NamingException, AccountException {
|
||||||
|
try {
|
||||||
|
DirContext ctx = helper.open();
|
||||||
|
Helper.LdapSchema schema = helper.getSchema(ctx);
|
||||||
|
helper.findAccount(schema, ctx, username, false);
|
||||||
|
} catch (NoSuchUserException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
|
static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
|
||||||
private final ExternalIds externalIds;
|
private final ExternalIds externalIds;
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ import com.google.gerrit.server.PluginUser;
|
|||||||
import com.google.gerrit.server.Sequences;
|
import com.google.gerrit.server.Sequences;
|
||||||
import com.google.gerrit.server.account.AccountCacheImpl;
|
import com.google.gerrit.server.account.AccountCacheImpl;
|
||||||
import com.google.gerrit.server.account.AccountControl;
|
import com.google.gerrit.server.account.AccountControl;
|
||||||
|
import com.google.gerrit.server.account.AccountDeactivator;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AccountResolver;
|
import com.google.gerrit.server.account.AccountResolver;
|
||||||
import com.google.gerrit.server.account.AccountVisibilityProvider;
|
import com.google.gerrit.server.account.AccountVisibilityProvider;
|
||||||
@ -279,6 +280,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
|
|
||||||
bind(GcConfig.class);
|
bind(GcConfig.class);
|
||||||
bind(ChangeCleanupConfig.class);
|
bind(ChangeCleanupConfig.class);
|
||||||
|
bind(AccountDeactivator.class);
|
||||||
|
|
||||||
bind(ApprovalsUtil.class);
|
bind(ApprovalsUtil.class);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
|
|||||||
import com.google.gerrit.pgm.util.LogFileCompressor;
|
import com.google.gerrit.pgm.util.LogFileCompressor;
|
||||||
import com.google.gerrit.server.LibModuleLoader;
|
import com.google.gerrit.server.LibModuleLoader;
|
||||||
import com.google.gerrit.server.StartupChecks;
|
import com.google.gerrit.server.StartupChecks;
|
||||||
|
import com.google.gerrit.server.account.AccountDeactivator;
|
||||||
import com.google.gerrit.server.account.InternalAccountDirectory;
|
import com.google.gerrit.server.account.InternalAccountDirectory;
|
||||||
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
|
import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
|
||||||
import com.google.gerrit.server.change.ChangeCleanupRunner;
|
import com.google.gerrit.server.change.ChangeCleanupRunner;
|
||||||
@ -365,6 +366,7 @@ public class WebAppInitializer extends GuiceServletContextListener implements Fi
|
|||||||
});
|
});
|
||||||
modules.add(new GarbageCollectionModule());
|
modules.add(new GarbageCollectionModule());
|
||||||
modules.add(new ChangeCleanupRunner.Module());
|
modules.add(new ChangeCleanupRunner.Module());
|
||||||
|
modules.add(new AccountDeactivator.Module());
|
||||||
modules.addAll(LibModuleLoader.loadModules(cfgInjector));
|
modules.addAll(LibModuleLoader.loadModules(cfgInjector));
|
||||||
return cfgInjector.createChildInjector(modules);
|
return cfgInjector.createChildInjector(modules);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user