use of io.undertow.security.idm.Account in project undertow by undertow-io.
the class AsyncWebSocketHttpServerExchange method getUserPrincipal.
@Override
public Principal getUserPrincipal() {
SecurityContext sc = exchange.getSecurityContext();
if (sc == null) {
return null;
}
Account authenticatedAccount = sc.getAuthenticatedAccount();
if (authenticatedAccount == null) {
return null;
}
return authenticatedAccount.getPrincipal();
}
use of io.undertow.security.idm.Account in project undertow by undertow-io.
the class DigestAuthenticationMechanism method handleDigestHeader.
private AuthenticationMechanismOutcome handleDigestHeader(HttpServerExchange exchange, final SecurityContext securityContext) {
DigestContext context = exchange.getAttachment(DigestContext.ATTACHMENT_KEY);
Map<DigestAuthorizationToken, String> parsedHeader = context.getParsedHeader();
// Step 1 - Verify the set of tokens received to ensure valid values.
Set<DigestAuthorizationToken> mandatoryTokens = EnumSet.copyOf(MANDATORY_REQUEST_TOKENS);
if (!supportedAlgorithms.contains(DigestAlgorithm.MD5)) {
// If we don't support MD5 then the client must choose an algorithm as we can not fall back to MD5.
mandatoryTokens.add(DigestAuthorizationToken.ALGORITHM);
}
if (!supportedQops.isEmpty() && !supportedQops.contains(DigestQop.AUTH)) {
// If we do not support auth then we are mandating auth-int so force the client to send a QOP
mandatoryTokens.add(DigestAuthorizationToken.MESSAGE_QOP);
}
DigestQop qop = null;
// This check is early as is increases the list of mandatory tokens.
if (parsedHeader.containsKey(DigestAuthorizationToken.MESSAGE_QOP)) {
qop = DigestQop.forName(parsedHeader.get(DigestAuthorizationToken.MESSAGE_QOP));
if (qop == null || !supportedQops.contains(qop)) {
// We are also ensuring the client is not trying to force a qop that has been disabled.
REQUEST_LOGGER.invalidTokenReceived(DigestAuthorizationToken.MESSAGE_QOP.getName(), parsedHeader.get(DigestAuthorizationToken.MESSAGE_QOP));
// TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new challenge.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
context.setQop(qop);
mandatoryTokens.add(DigestAuthorizationToken.CNONCE);
mandatoryTokens.add(DigestAuthorizationToken.NONCE_COUNT);
}
// Check all mandatory tokens are present.
mandatoryTokens.removeAll(parsedHeader.keySet());
if (mandatoryTokens.size() > 0) {
for (DigestAuthorizationToken currentToken : mandatoryTokens) {
// TODO - Need a better check and possible concatenate the list of tokens - however
// even having one missing token is not something we should routinely expect.
REQUEST_LOGGER.missingAuthorizationToken(currentToken.getName());
}
// TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new challenge.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
// Perform some validation of the remaining tokens.
if (!realmName.equals(parsedHeader.get(DigestAuthorizationToken.REALM))) {
REQUEST_LOGGER.invalidTokenReceived(DigestAuthorizationToken.REALM.getName(), parsedHeader.get(DigestAuthorizationToken.REALM));
// TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new challenge.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
if (parsedHeader.containsKey(DigestAuthorizationToken.DIGEST_URI)) {
String uri = parsedHeader.get(DigestAuthorizationToken.DIGEST_URI);
String requestURI = exchange.getRequestURI();
if (!exchange.getQueryString().isEmpty()) {
requestURI = requestURI + "?" + exchange.getQueryString();
}
if (!uri.equals(requestURI)) {
// it is possible we were given an absolute URI
// we reconstruct the URI from the host header to make sure they match up
// I am not sure if this is overly strict, however I think it is better
// to be safe than sorry
requestURI = exchange.getRequestURL();
if (!exchange.getQueryString().isEmpty()) {
requestURI = requestURI + "?" + exchange.getQueryString();
}
if (!uri.equals(requestURI)) {
// just end the auth process
exchange.setStatusCode(StatusCodes.BAD_REQUEST);
exchange.endExchange();
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
}
} else {
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
if (parsedHeader.containsKey(DigestAuthorizationToken.OPAQUE)) {
if (!OPAQUE_VALUE.equals(parsedHeader.get(DigestAuthorizationToken.OPAQUE))) {
REQUEST_LOGGER.invalidTokenReceived(DigestAuthorizationToken.OPAQUE.getName(), parsedHeader.get(DigestAuthorizationToken.OPAQUE));
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
}
DigestAlgorithm algorithm;
if (parsedHeader.containsKey(DigestAuthorizationToken.ALGORITHM)) {
algorithm = DigestAlgorithm.forName(parsedHeader.get(DigestAuthorizationToken.ALGORITHM));
if (algorithm == null || !supportedAlgorithms.contains(algorithm)) {
// We are also ensuring the client is not trying to force an algorithm that has been disabled.
REQUEST_LOGGER.invalidTokenReceived(DigestAuthorizationToken.ALGORITHM.getName(), parsedHeader.get(DigestAuthorizationToken.ALGORITHM));
// TODO - This actually needs to result in a HTTP 400 Bad Request response and not a new challenge.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
} else {
// We know this is safe as the algorithm token was made mandatory
// if MD5 is not supported.
algorithm = DigestAlgorithm.MD5;
}
try {
context.setAlgorithm(algorithm);
} catch (NoSuchAlgorithmException e) {
/*
* This should not be possible in a properly configured installation.
*/
REQUEST_LOGGER.exceptionProcessingRequest(e);
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
final String userName = parsedHeader.get(DigestAuthorizationToken.USERNAME);
final IdentityManager identityManager = getIdentityManager(securityContext);
final Account account;
if (algorithm.isSession()) {
/* This can follow one of the following: -
* 1 - New session so use DigestCredentialImpl with the IdentityManager to
* create a new session key.
* 2 - Obtain the existing session key from the session store and validate it, just use
* IdentityManager to validate account is still active and the current role assignment.
*/
throw new IllegalStateException("Not yet implemented.");
} else {
final DigestCredential credential = new DigestCredentialImpl(context);
account = identityManager.verify(userName, credential);
}
if (account == null) {
// Authentication has failed, this could either be caused by the user not-existing or it
// could be caused due to an invalid hash.
securityContext.authenticationFailed(MESSAGES.authenticationFailed(userName), mechanismName);
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
// Step 3 - Verify that the nonce was eligible to be used.
if (!validateNonceUse(context, parsedHeader, exchange)) {
// TODO - This is the right place to make use of the decision but the check needs to be much much sooner
// otherwise a failure server
// side could leave a packet that could be 're-played' after the failed auth.
// The username and password verification passed but for some reason we do not like the nonce.
context.markStale();
// can easily hit this point.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
// We have authenticated the remote user.
sendAuthenticationInfoHeader(exchange);
securityContext.authenticationComplete(account, mechanismName, false);
return AuthenticationMechanismOutcome.AUTHENTICATED;
// Step 4 - Set up any QOP related requirements.
// TODO - Do QOP
}
use of io.undertow.security.idm.Account in project undertow by undertow-io.
the class GSSAPIAuthenticationMechanism method authenticate.
@Override
public AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange, final SecurityContext securityContext) {
ServerConnection connection = exchange.getConnection();
NegotiationContext negContext = connection.getAttachment(NegotiationContext.ATTACHMENT_KEY);
if (negContext != null) {
UndertowLogger.SECURITY_LOGGER.debugf("Existing negotiation context found for %s", exchange);
exchange.putAttachment(NegotiationContext.ATTACHMENT_KEY, negContext);
if (negContext.isEstablished()) {
IdentityManager identityManager = getIdentityManager(securityContext);
final Account account = identityManager.verify(new GSSContextCredential(negContext.getGssContext()));
if (account != null) {
securityContext.authenticationComplete(account, name, false);
UndertowLogger.SECURITY_LOGGER.debugf("Authenticated as user %s with existing GSSAPI negotiation context for %s", account.getPrincipal().getName(), exchange);
return AuthenticationMechanismOutcome.AUTHENTICATED;
} else {
UndertowLogger.SECURITY_LOGGER.debugf("Failed to authenticate with existing GSSAPI negotiation context for %s", exchange);
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
}
}
List<String> authHeaders = exchange.getRequestHeaders().get(AUTHORIZATION);
if (authHeaders != null) {
for (String current : authHeaders) {
if (current.startsWith(NEGOTIATE_PREFIX)) {
String base64Challenge = current.substring(NEGOTIATE_PREFIX.length());
try {
ByteBuffer challenge = FlexBase64.decode(base64Challenge);
return runGSSAPI(exchange, challenge, securityContext);
} catch (IOException e) {
}
// it was not correctly structured.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
}
}
// No suitable header was found so authentication was not even attempted.
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
}
use of io.undertow.security.idm.Account in project undertow by undertow-io.
the class FormAuthenticationMechanism method runFormAuth.
public AuthenticationMechanismOutcome runFormAuth(final HttpServerExchange exchange, final SecurityContext securityContext) {
final FormDataParser parser = formParserFactory.createParser(exchange);
if (parser == null) {
UndertowLogger.SECURITY_LOGGER.debug("Could not authenticate as no form parser is present");
// TODO - May need a better error signaling mechanism here to prevent repeated attempts.
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
Throwable original = null;
AuthenticationMechanismOutcome retValue = null;
try {
final FormData data = parser.parseBlocking();
final FormData.FormValue jUsername = data.getFirst("j_username");
final FormData.FormValue jPassword = data.getFirst("j_password");
if (jUsername == null || jPassword == null) {
UndertowLogger.SECURITY_LOGGER.debugf("Could not authenticate as username or password was not present in the posted result for %s", exchange);
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
}
final String userName = jUsername.getValue();
final String password = jPassword.getValue();
AuthenticationMechanismOutcome outcome = null;
PasswordCredential credential = new PasswordCredential(password.toCharArray());
try {
IdentityManager identityManager = getIdentityManager(securityContext);
Account account = identityManager.verify(userName, credential);
if (account != null) {
securityContext.authenticationComplete(account, name, true);
UndertowLogger.SECURITY_LOGGER.debugf("Authenticated user %s using for auth for %s", account.getPrincipal().getName(), exchange);
outcome = AuthenticationMechanismOutcome.AUTHENTICATED;
} else {
securityContext.authenticationFailed(MESSAGES.authenticationFailed(userName), name);
}
} catch (Throwable t) {
original = t;
} finally {
try {
if (outcome == AuthenticationMechanismOutcome.AUTHENTICATED) {
handleRedirectBack(exchange);
exchange.endExchange();
}
retValue = outcome != null ? outcome : AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
} catch (Throwable t) {
if (original != null) {
original.addSuppressed(t);
} else {
original = t;
}
}
}
} catch (IOException e) {
original = new UncheckedIOException(e);
}
if (original != null) {
if (original instanceof RuntimeException) {
throw (RuntimeException) original;
}
if (original instanceof Error) {
throw (Error) original;
}
}
return retValue;
}
use of io.undertow.security.idm.Account in project undertow by undertow-io.
the class GenericHeaderAuthenticationMechanism method authenticate.
@Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
String principal = getPrincipal(exchange);
if (principal == null) {
return NOT_ATTEMPTED;
}
String session = getSession(exchange);
if (session == null) {
return NOT_ATTEMPTED;
}
Account account = identityManager.verify(principal, new PasswordCredential(session.toCharArray()));
if (account == null) {
securityContext.authenticationFailed(UndertowMessages.MESSAGES.authenticationFailed(principal), mechanismName);
return NOT_AUTHENTICATED;
}
securityContext.authenticationComplete(account, mechanismName, false);
return AUTHENTICATED;
}
Aggregations