/*
* (C) Copyright 2010-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Gagnavarslan ehf
*/
package org.nuxeo.ecm.ui.web.auth.digest;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.directory.Directory;
import org.nuxeo.ecm.directory.DirectoryException;
import org.nuxeo.ecm.directory.Session;
import org.nuxeo.ecm.directory.api.DirectoryService;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.login.BaseLoginModule;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.api.Framework;
/**
* Nuxeo Login Plugin for HTTP Digest Access Authentication (RFC 2617).
*/
public class DigestLoginPlugin extends BaseLoginModule {
private static final Log log = LogFactory.getLog(DigestLoginPlugin.class);
protected static final String REALM = "realm";
protected static final String HTTP_METHOD = "httpMethod";
protected static final String URI = "uri";
protected static final String QOP = "qop";
protected static final String NONCE = "nonce";
protected static final String NC = "nc";
protected static final String CNONCE = "cnonce";
protected static final String PASSWORD_FIELD = "passwordField";
@Override
public Boolean initLoginModule() {
return Boolean.TRUE;
}
@Override
public String validatedUserIdentity(UserIdentificationInfo userIdent) {
try {
String storedHA1 = getStoredHA1(userIdent.getUserName());
if (StringUtils.isEmpty(storedHA1)) {
log.warn("Digest authentication failed. Stored HA1 is empty");
return null;
}
Map<String, String> loginParameters = userIdent.getLoginParameters();
String generateDigest = generateDigest(storedHA1, loginParameters.get(HTTP_METHOD), //
loginParameters.get(URI), //
loginParameters.get(QOP), // RFC 2617 extension
loginParameters.get(NONCE), //
loginParameters.get(NC), // RFC 2617 extension
loginParameters.get(CNONCE) // RFC 2617 extension
);
if (generateDigest.equals(userIdent.getPassword())) {
return userIdent.getUserName();
} else {
log.warn("Digest authentication failed for user: " + userIdent.getUserName() + " realm: "
+ loginParameters.get(REALM));
return null;
}
} catch (IllegalArgumentException | DirectoryException e) {
log.error("Digest authentication failed", e);
return null;
}
}
public static String generateDigest(String ha1, String httpMethod, String uri, String qop, String nonce, String nc,
String cnonce) throws IllegalArgumentException {
String a2 = httpMethod + ":" + uri;
String ha2 = DigestUtils.md5Hex(a2);
String digest;
if (qop == null) {
digest = ha1 + ":" + nonce + ":" + ha2;
} else if ("auth".equals(qop)) {
digest = ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2;
} else {
throw new IllegalArgumentException("This method does not support a qop: '" + qop + "'");
}
return DigestUtils.md5Hex(digest);
}
public static String encodeDigestAuthPassword(String username, String realm, String password) {
String a1 = username + ":" + realm + ":" + password;
return DigestUtils.md5Hex(a1);
}
protected String getStoredHA1(String username) throws DirectoryException {
UserManager userManager = Framework.getService(UserManager.class);
String dirName = userManager.getDigestAuthDirectory();
DirectoryService directoryService = Framework.getLocalService(DirectoryService.class);
Directory directory = directoryService.getDirectory(dirName);
if (directory == null) {
throw new IllegalArgumentException("Digest Auth directory not found: " + dirName);
}
try (Session dir = directoryService.open(dirName)) {
dir.setReadAllColumns(true); // needed to read digest password
String schema = directoryService.getDirectorySchema(dirName);
DocumentModel entry = dir.getEntry(username, true);
String passwordField = (parameters.containsKey(PASSWORD_FIELD)) ? parameters.get(PASSWORD_FIELD)
: dir.getPasswordField();
return entry == null ? null : (String) entry.getProperty(schema, passwordField);
}
}
}