summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/HtpasswdUserService.java
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2013-11-24 23:18:50 -0500
committerJames Moger <james.moger@gitblit.com>2013-11-29 11:05:51 -0500
commit04a98505a4ab8f48aee22800fcac193d9367d0ae (patch)
treeeb05bc77eeafda1c5b7af9d7b5b27012065f7a98 /src/main/java/com/gitblit/HtpasswdUserService.java
parentf8f6aa4d07cdfaaf23e24bf9eaf0a5fb9b437dda (diff)
downloadgitblit-04a98505a4ab8f48aee22800fcac193d9367d0ae.tar.gz
gitblit-04a98505a4ab8f48aee22800fcac193d9367d0ae.zip
Refactor user services and separate authentication (issue-281)
Change-Id: I336e005e02623fc5e11a4f8b4408bea5465a43fd
Diffstat (limited to 'src/main/java/com/gitblit/HtpasswdUserService.java')
-rw-r--r--src/main/java/com/gitblit/HtpasswdUserService.java361
1 files changed, 0 insertions, 361 deletions
diff --git a/src/main/java/com/gitblit/HtpasswdUserService.java b/src/main/java/com/gitblit/HtpasswdUserService.java
deleted file mode 100644
index ca5295c9..00000000
--- a/src/main/java/com/gitblit/HtpasswdUserService.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright 2013 Florian Zschocke
- * Copyright 2013 gitblit.com
- *
- * 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.gitblit;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.text.MessageFormat;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.digest.Crypt;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.codec.digest.Md5Crypt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.gitblit.Constants.AccountType;
-import com.gitblit.manager.IRuntimeManager;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.StringUtils;
-
-
-/**
- * Implementation of a user service using an Apache htpasswd file for authentication.
- *
- * This user service implement custom authentication using entries in a file created
- * by the 'htpasswd' program of an Apache web server. All possible output
- * options of the 'htpasswd' program version 2.2 are supported:
- * plain text (only on Windows and Netware),
- * glibc crypt() (not on Windows and NetWare),
- * Apache MD5 (apr1),
- * unsalted SHA-1.
- *
- * Configuration options:
- * realm.htpasswd.backingUserService - Specify the backing user service that is used
- * to keep the user data other than the password.
- * The default is '${baseFolder}/users.conf'.
- * realm.htpasswd.userfile - The text file with the htpasswd entries to be used for
- * authentication.
- * The default is '${baseFolder}/htpasswd'.
- * realm.htpasswd.overrideLocalAuthentication - Specify if local accounts are overwritten
- * when authentication matches for an
- * external account.
- *
- * @author Florian Zschocke
- *
- */
-public class HtpasswdUserService extends GitblitUserService
-{
-
- private static final String KEY_BACKING_US = Keys.realm.htpasswd.backingUserService;
- private static final String DEFAULT_BACKING_US = "${baseFolder}/users.conf";
-
- private static final String KEY_HTPASSWD_FILE = Keys.realm.htpasswd.userfile;
- private static final String DEFAULT_HTPASSWD_FILE = "${baseFolder}/htpasswd";
-
- private static final String KEY_OVERRIDE_LOCALAUTH = Keys.realm.htpasswd.overrideLocalAuthentication;
- private static final boolean DEFAULT_OVERRIDE_LOCALAUTH = true;
-
- private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
-
- private final boolean SUPPORT_PLAINTEXT_PWD;
-
- private IRuntimeManager runtimeManager;
- private IStoredSettings settings;
- private File htpasswdFile;
-
-
- private final Logger logger = LoggerFactory.getLogger(HtpasswdUserService.class);
-
- private final Map<String, String> htUsers = new ConcurrentHashMap<String, String>();
-
- private volatile long lastModified;
-
- private volatile boolean forceReload;
-
-
-
- public HtpasswdUserService()
- {
- super();
-
- String os = System.getProperty("os.name").toLowerCase();
- if (os.startsWith("windows") || os.startsWith("netware")) {
- SUPPORT_PLAINTEXT_PWD = true;
- }
- else {
- SUPPORT_PLAINTEXT_PWD = false;
- }
- }
-
-
-
- /**
- * Setup the user service.
- *
- * The HtpasswdUserService extends the GitblitUserService and is thus
- * backed by the available user services provided by the GitblitUserService.
- * In addition the setup tries to read and parse the htpasswd file to be used
- * for authentication.
- *
- * @param runtimeManager
- * @since 1.4.0
- */
- @Override
- public void setup(IRuntimeManager runtimeManager)
- {
- this.runtimeManager = runtimeManager;
- this.settings = runtimeManager.getSettings();
-
- // This is done in two steps in order to avoid calling GitBlit.getFileOrFolder(String, String) which will segfault for unit tests.
- String file = settings.getString(KEY_BACKING_US, DEFAULT_BACKING_US);
- File realmFile = runtimeManager.getFileOrFolder(file);
- serviceImpl = createUserService(realmFile);
- logger.info("Htpasswd User Service backed by " + serviceImpl.toString());
-
- read();
-
- logger.debug("Read " + htUsers.size() + " users from htpasswd file: " + this.htpasswdFile);
- }
-
-
-
- /**
- * For now, credentials are defined in the htpasswd file and can not be manipulated
- * from Gitblit.
- *
- * @return false
- * @since 1.0.0
- */
- @Override
- public boolean supportsCredentialChanges()
- {
- return false;
- }
-
-
-
- /**
- * Authenticate a user based on a username and password.
- *
- * If the account is determined to be a local account, authentication
- * will be done against the locally stored password.
- * Otherwise, the configured htpasswd file is read. All current output options
- * of htpasswd are supported: clear text, crypt(), Apache MD5 and unsalted SHA-1.
- *
- * @param username
- * @param password
- * @return a user object or null
- */
- @Override
- public UserModel authenticate(String username, char[] password)
- {
- if (isLocalAccount(username)) {
- // local account, bypass htpasswd authentication
- return super.authenticate(username, password);
- }
-
-
- read();
- String storedPwd = htUsers.get(username);
- if (storedPwd != null) {
- boolean authenticated = false;
- final String passwd = new String(password);
-
- // test Apache MD5 variant encrypted password
- if ( storedPwd.startsWith("$apr1$") ) {
- if ( storedPwd.equals(Md5Crypt.apr1Crypt(passwd, storedPwd)) ) {
- logger.debug("Apache MD5 encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- }
- // test unsalted SHA password
- else if ( storedPwd.startsWith("{SHA}") ) {
- String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(passwd));
- if ( storedPwd.substring("{SHA}".length()).equals(passwd64) ) {
- logger.debug("Unsalted SHA-1 encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- }
- // test libc crypt() encoded password
- else if ( supportCryptPwd() && storedPwd.equals(Crypt.crypt(passwd, storedPwd)) ) {
- logger.debug("Libc crypt encoded password matched for user '" + username + "'");
- authenticated = true;
- }
- // test clear text
- else if ( supportPlaintextPwd() && storedPwd.equals(passwd) ){
- logger.debug("Clear text password matched for user '" + username + "'");
- authenticated = true;
- }
-
-
- if (authenticated) {
- logger.debug("Htpasswd authenticated: " + username);
-
- UserModel user = getUserModel(username);
- if (user == null) {
- // create user object for new authenticated user
- user = new UserModel(username);
- }
-
- // create a user cookie
- if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
- user.cookie = StringUtils.getSHA1(user.username + passwd);
- }
-
- // Set user attributes, hide password from backing user service.
- user.password = Constants.EXTERNAL_ACCOUNT;
- user.accountType = getAccountType();
-
- // Push the looked up values to backing file
- super.updateUserModel(user);
-
- return user;
- }
- }
-
- return null;
- }
-
-
-
- /**
- * Determine if the account is to be treated as a local account.
- *
- * This influences authentication. A local account will be authenticated
- * by the backing user service while an external account will be handled
- * by this user service.
- * <br/>
- * The decision also depends on the setting of the key
- * realm.htpasswd.overrideLocalAuthentication.
- * If it is set to true, then passwords will first be checked against the
- * htpasswd store. If an account exists and is marked as local in the backing
- * user service, that setting will be overwritten by the result. This
- * means that an account that looks local to the backing user service will
- * be turned into an external account upon valid login of a user that has
- * an entry in the htpasswd file.
- * If the key is set to false, then it is determined if the account is local
- * according to the logic of the GitblitUserService.
- */
- @Override
- protected boolean isLocalAccount(String username)
- {
- if ( settings.getBoolean(KEY_OVERRIDE_LOCALAUTH, DEFAULT_OVERRIDE_LOCALAUTH) ) {
- read();
- if ( htUsers.containsKey(username) ) return false;
- }
- return super.isLocalAccount(username);
- }
-
-
-
- /**
- * Get the account type used for this user service.
- *
- * @return AccountType.HTPASSWD
- */
- @Override
- public AccountType getAccountType()
- {
- return AccountType.HTPASSWD;
- }
-
-
-
- private String htpasswdFilePath = null;
- /**
- * Reads the realm file and rebuilds the in-memory lookup tables.
- */
- protected synchronized void read()
- {
-
- // This is done in two steps in order to avoid calling GitBlit.getFileOrFolder(String, String) which will segfault for unit tests.
- String file = settings.getString(KEY_HTPASSWD_FILE, DEFAULT_HTPASSWD_FILE);
- if ( !file.equals(htpasswdFilePath) ) {
- // The htpasswd file setting changed. Rediscover the file.
- this.htpasswdFilePath = file;
- this.htpasswdFile = runtimeManager.getFileOrFolder(file);
- this.htUsers.clear();
- this.forceReload = true;
- }
-
- if (htpasswdFile.exists() && (forceReload || (htpasswdFile.lastModified() != lastModified))) {
- forceReload = false;
- lastModified = htpasswdFile.lastModified();
- htUsers.clear();
-
- Pattern entry = Pattern.compile("^([^:]+):(.+)");
-
- Scanner scanner = null;
- try {
- scanner = new Scanner(new FileInputStream(htpasswdFile));
- while( scanner.hasNextLine()) {
- String line = scanner.nextLine().trim();
- if ( !line.isEmpty() && !line.startsWith("#") ) {
- Matcher m = entry.matcher(line);
- if ( m.matches() ) {
- htUsers.put(m.group(1), m.group(2));
- }
- }
- }
- } catch (Exception e) {
- logger.error(MessageFormat.format("Failed to read {0}", htpasswdFile), e);
- }
- finally {
- if (scanner != null) scanner.close();
- }
- }
- }
-
-
-
- private boolean supportPlaintextPwd()
- {
- return this.settings.getBoolean(KEY_SUPPORT_PLAINTEXT_PWD, SUPPORT_PLAINTEXT_PWD);
- }
-
-
- private boolean supportCryptPwd()
- {
- return !supportPlaintextPwd();
- }
-
-
-
- @Override
- public String toString()
- {
- return getClass().getSimpleName() + "(" + ((htpasswdFile != null) ? htpasswdFile.getAbsolutePath() : "null") + ")";
- }
-
-
-
-
- /*
- * Method only used for unit tests. Return number of users read from htpasswd file.
- */
- public int getNumberHtpasswdUsers()
- {
- return this.htUsers.size();
- }
-}