/** * Only allow the request through if successfully authenticated or if authentication is not * required. * * @throws java.lang.Exception * @see io.undertow.server.HttpHandler#handleRequest(io.undertow.server.HttpServerExchange) */ @Override public void handleRequest(HttpServerExchange exchange, RequestContext context) throws Exception { if (exchange.isInIoThread()) { exchange.dispatch(this); return; } SecurityContext rcontext = exchange.getSecurityContext(); if (rcontext.authenticate()) { if (!exchange.isComplete()) { getNext().handleRequest(exchange, context); } } else { exchange.endExchange(); } }
protected void completeAuthentication( HttpServerExchange exchange, SecurityContext securityContext, SkeletonKeyToken token, String surrogate) { final SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate); Set<String> roles = null; if (config.isUseResourceRoleMappings()) { SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName()); if (access != null) roles = access.getRoles(); } else { SkeletonKeyToken.Access access = token.getRealmAccess(); if (access != null) roles = access.getRoles(); } if (roles == null) roles = Collections.emptySet(); final Set<String> accountRoles = roles; Account account = new Account() { @Override public Principal getPrincipal() { return skeletonKeyPrincipal; } @Override public Set<String> getRoles() { return accountRoles; } }; securityContext.authenticationComplete(account, "FORM"); }
@Override public void handleRequest(final HttpServerExchange exchange) throws Exception { final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY); ServletRequest request = servletRequestContext.getServletRequest(); if (request.getDispatcherType() == DispatcherType.REQUEST) { List<SingleConstraintMatch> constraints = servletRequestContext.getRequiredConstrains(); SecurityContext sc = exchange.getSecurityContext(); if (!authorizationManager.canAccessResource( constraints, sc.getAuthenticatedAccount(), servletRequestContext.getCurrentServlet().getManagedServlet().getServletInfo(), servletRequestContext.getOriginalRequest(), servletRequestContext.getDeployment())) { HttpServletResponse response = (HttpServletResponse) servletRequestContext.getServletResponse(); response.sendError(403); return; } } next.handleRequest(exchange); }
@Override protected void completeAuthentication(final SamlSession samlSession) { Account undertowAccount = new Account() { @Override public Principal getPrincipal() { return samlSession.getPrincipal(); } @Override public Set<String> getRoles() { return samlSession.getRoles(); } }; securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false); }
public 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 = new HashSet<>(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; } // TODO - Validate the URI 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(); // We do not mark as a failure on the security context as this is not quite a failure, a // client with a cached nonce // 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 }
@SuppressWarnings("deprecation") private IdentityManager getIdentityManager(SecurityContext securityContext) { return identityManager != null ? identityManager : securityContext.getIdentityManager(); }