Skip to content

Commit

Permalink
Fix HTTP error codes related to SecQ authn
Browse files Browse the repository at this point in the history
Now midPoint returns 401 consistently when the authentication fails
(see MID-5725).
  • Loading branch information
mederly authored and semancik committed Oct 11, 2019
1 parent 8c3f578 commit cd01b50
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 121 deletions.
Expand Up @@ -73,132 +73,127 @@ public abstract class MidpointRestAuthenticator<T extends AbstractAuthentication
protected abstract AuthenticationEvaluator<T> getAuthenticationEvaluator();
protected abstract T createAuthenticationContext(AuthorizationPolicy policy, ContainerRequestContext requestCtx);

public void handleRequest(AuthorizationPolicy policy, Message m, ContainerRequestContext requestCtx) {
public void handleRequest(AuthorizationPolicy policy, Message m, ContainerRequestContext requestCtx) {

if (policy == null){
RestServiceUtil.createAbortMessage(requestCtx);
return;
}
if (policy == null) {
RestServiceUtil.createAbortMessage(requestCtx);
return;
}

T authenticationContext = createAuthenticationContext(policy, requestCtx);

T authenticationContext = createAuthenticationContext(policy, requestCtx);
if (authenticationContext == null) {
return;
}

if (authenticationContext == null) {
return;
}
String enteredUsername = authenticationContext.getUsername();

String enteredUsername = authenticationContext.getUsername();
if (enteredUsername == null) {
RestServiceUtil.createAbortMessage(requestCtx);
return;
}

if (enteredUsername == null){
RestServiceUtil.createAbortMessage(requestCtx);
return;
}
LOGGER.trace("Authenticating username '{}' to REST service", enteredUsername);

// We need to create task before attempting authentication. Task ID is also a session ID.
Task task = taskManager.createTaskInstance(ModelRestService.OPERATION_REST_SERVICE);
task.setChannel(SchemaConstants.CHANNEL_REST_URI);

ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_REST_URI);
connEnv.setSessionIdOverride(task.getTaskIdentifier());
UsernamePasswordAuthenticationToken token;
try {
token = getAuthenticationEvaluator().authenticate(connEnv, authenticationContext);
} catch (UsernameNotFoundException | BadCredentialsException | DisabledException | LockedException |
CredentialsExpiredException | AccessDeniedException | AuthenticationCredentialsNotFoundException |
AuthenticationServiceException e) {
LOGGER.trace("Exception while authenticating username '{}' to REST service: {}", enteredUsername, e.getMessage(), e);
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).build());
return;
}

LOGGER.trace("Authenticating username '{}' to REST service", enteredUsername);
UserType user = ((MidPointPrincipal) token.getPrincipal()).getUser();
task.setOwner(user.asPrismObject());

// We need to create task before attempting authentication. Task ID is also a session ID.
Task task = taskManager.createTaskInstance(ModelRestService.OPERATION_REST_SERVICE);
task.setChannel(SchemaConstants.CHANNEL_REST_URI);
// m.put(RestServiceUtil.MESSAGE_PROPERTY_TASK_NAME, task);
if (!authorizeUser(user, null, enteredUsername, connEnv, requestCtx)) {
return;
}

ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_REST_URI);
connEnv.setSessionIdOverride(task.getTaskIdentifier());
UsernamePasswordAuthenticationToken token;
try {
token = getAuthenticationEvaluator().authenticate(connEnv, authenticationContext);
} catch (UsernameNotFoundException | BadCredentialsException e) {
LOGGER.trace("Exception while authenticating username '{}' to REST service: {}", enteredUsername, e.getMessage(), e);
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).header("WWW-Authenticate", "Basic authentication failed. Cannot authenticate user.").build());
return;
} catch (DisabledException | LockedException | CredentialsExpiredException | AccessDeniedException
| AuthenticationCredentialsNotFoundException | AuthenticationServiceException e) {
LOGGER.trace("Exception while authenticating username '{}' to REST service: {}", enteredUsername, e.getMessage(), e);
requestCtx.abortWith(Response.status(Status.FORBIDDEN).build());
return;
}

UserType user = ((MidPointPrincipal)token.getPrincipal()).getUser();
task.setOwner(user.asPrismObject());

// m.put(RestServiceUtil.MESSAGE_PROPERTY_TASK_NAME, task);
if (!authorizeUser(user, null, enteredUsername, connEnv, requestCtx)){
return;
}

String oid = requestCtx.getHeaderString("Switch-To-Principal");
OperationResult result = task.getResult();
if (StringUtils.isNotBlank(oid)){
try {
PrismObject<UserType> authorizedUser = model.getObject(UserType.class, oid, null, task, result);
task.setOwner(authorizedUser);
if (!authorizeUser(AuthorizationConstants.AUTZ_REST_PROXY_URL, user, authorizedUser, enteredUsername, connEnv, requestCtx)){
return;
}
authenticateUser(authorizedUser, authorizedUser.getName().getOrig(), connEnv, requestCtx);
// if (!authorizeUser(authorizedUser.asObjectable(), null, authorizedUser.getName().getOrig(), connEnv, requestCtx)){
// return;
// }
} catch (ObjectNotFoundException | SchemaException | SecurityViolationException
| CommunicationException | ConfigurationException | ExpressionEvaluationException e) {
LOGGER.trace("Exception while authenticating user identified with '{}' to REST service: {}", oid, e.getMessage(), e);
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).header("WWW-Authenticate", "Proxy Authentication failed. Cannot authenticate user.").build());
String oid = requestCtx.getHeaderString("Switch-To-Principal");
OperationResult result = task.getResult();
if (StringUtils.isNotBlank(oid)) {
try {
PrismObject<UserType> authorizedUser = model.getObject(UserType.class, oid, null, task, result);
task.setOwner(authorizedUser);
if (!authorizeUser(AuthorizationConstants.AUTZ_REST_PROXY_URL, user, authorizedUser, enteredUsername, connEnv, requestCtx)) {
return;
}


}

m.put(RestServiceUtil.MESSAGE_PROPERTY_TASK_NAME, task);

LOGGER.trace("Authorized to use REST service ({})", user);
authenticateUser(authorizedUser, authorizedUser.getName().getOrig(), connEnv, requestCtx);
// if (!authorizeUser(authorizedUser.asObjectable(), null, authorizedUser.getName().getOrig(), connEnv, requestCtx)){
// return;
// }
} catch (ObjectNotFoundException | SchemaException | SecurityViolationException
| CommunicationException | ConfigurationException | ExpressionEvaluationException e) {
LOGGER.trace("Exception while authenticating user identified with '{}' to REST service: {}", oid, e.getMessage(), e);
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).build());
return;
}

}

private boolean authorizeUser(UserType user, PrismObject<UserType> proxyUser, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
authenticateUser(user.asPrismObject(), enteredUsername, connEnv, requestCtx);
return authorizeUser(AuthorizationConstants.AUTZ_REST_ALL_URL, user, null, enteredUsername, connEnv, requestCtx);
}
}

private void authenticateUser(PrismObject<UserType> user, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
try {
securityContextManager.setupPreAuthenticatedSecurityContext(user);
} catch (SchemaException e) {
securityHelper.auditLoginFailure(enteredUsername, user.asObjectable(), connEnv, "Schema error: "+e.getMessage());
requestCtx.abortWith(Response.status(Status.BAD_REQUEST).build());
// return false;
}
m.put(RestServiceUtil.MESSAGE_PROPERTY_TASK_NAME, task);

LOGGER.trace("Authenticated to REST service as {}", user);
}

private boolean authorizeUser(String authorization, UserType user, PrismObject<UserType> proxyUser, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
Task task = taskManager.createTaskInstance(MidpointRestAuthenticator.class.getName() + ".authorizeUser");
try {
// authorize for proxy
securityEnforcer.authorize(authorization, null, AuthorizationParameters.Builder.buildObject(proxyUser), null, task, task.getResult());
} catch (SecurityViolationException e){
securityHelper.auditLoginFailure(enteredUsername, user, connEnv, "Not authorized");
requestCtx.abortWith(Response.status(Status.FORBIDDEN).build());
return false;
} catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException e) {
securityHelper.auditLoginFailure(enteredUsername, user, connEnv, "Internal error: "+e.getMessage());
requestCtx.abortWith(Response.status(Status.BAD_REQUEST).build());
return false;
}
return true;
}
LOGGER.trace("Authorized to use REST service ({})", user);
}

public SecurityContextManager getSecurityContextManager() {
return securityContextManager;
}
private boolean authorizeUser(UserType user, PrismObject<UserType> proxyUser, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
authenticateUser(user.asPrismObject(), enteredUsername, connEnv, requestCtx);
return authorizeUser(AuthorizationConstants.AUTZ_REST_ALL_URL, user, null, enteredUsername, connEnv, requestCtx);
}

public SecurityEnforcer getSecurityEnforcer() {
return securityEnforcer;
}
public ModelService getModel() {
return model;
private void authenticateUser(PrismObject<UserType> user, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
try {
securityContextManager.setupPreAuthenticatedSecurityContext(user);
} catch (SchemaException e) {
securityHelper.auditLoginFailure(enteredUsername, user.asObjectable(), connEnv, "Schema error: "+e.getMessage());
requestCtx.abortWith(Response.status(Status.BAD_REQUEST).build());
// return false;
}

public TaskManager getTaskManager() {
return taskManager;
LOGGER.trace("Authenticated to REST service as {}", user);
}

private boolean authorizeUser(String authorization, UserType user, PrismObject<UserType> proxyUser, String enteredUsername, ConnectionEnvironment connEnv, ContainerRequestContext requestCtx) {
Task task = taskManager.createTaskInstance(MidpointRestAuthenticator.class.getName() + ".authorizeUser");
try {
// authorize for proxy
securityEnforcer.authorize(authorization, null, AuthorizationParameters.Builder.buildObject(proxyUser), null, task, task.getResult());
} catch (SecurityViolationException e){
securityHelper.auditLoginFailure(enteredUsername, user, connEnv, "Not authorized");
requestCtx.abortWith(Response.status(Status.FORBIDDEN).build());
return false;
} catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException e) {
securityHelper.auditLoginFailure(enteredUsername, user, connEnv, "Internal error: "+e.getMessage());
requestCtx.abortWith(Response.status(Status.BAD_REQUEST).build());
return false;
}
return true;
}

public SecurityContextManager getSecurityContextManager() {
return securityContextManager;
}

public SecurityEnforcer getSecurityEnforcer() {
return securityEnforcer;
}
public ModelService getModel() {
return model;
}

public TaskManager getTaskManager() {
return taskManager;
}
}
Expand Up @@ -109,27 +109,27 @@ protected SecurityQuestionsAuthenticationContext createAuthenticationContext(Aut

if (answerNode instanceof MissingNode) {
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("restapi", "REST", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
SearchResultList<PrismObject<UserType>> users = null;
SearchResultList<PrismObject<UserType>> users;
try {
users = searchUser(userName);
} finally {
SecurityContextHolder.getContext().setAuthentication(null);
}

if (users.size() != 1) {
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).header("WWW-Authenticate", "Security question authentication failed. Incorrect username and/or password").build());
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).build());
return null;
}

PrismObject<UserType> user = users.get(0);
PrismContainer<SecurityQuestionAnswerType> questionAnswerContainer = user.findContainer(SchemaConstants.PATH_SECURITY_QUESTIONS_QUESTION_ANSWER);
if (questionAnswerContainer == null || questionAnswerContainer.isEmpty()){
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).header("WWW-Authenticate", "Security question authentication failed. Incorrect username and/or password").build());
if (questionAnswerContainer == null || questionAnswerContainer.isEmpty()) {
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).build());
return null;
}

String questionChallenge = "";
List<SecurityQuestionDefinitionType> questions = null;
List<SecurityQuestionDefinitionType> questions;
try {
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("restapi", "REST", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
questions = getQuestions(user);
Expand Down
Expand Up @@ -177,8 +177,7 @@ public static Response.ResponseBuilder createResultHeaders(Response.ResponseBuil
}

public static void createAbortMessage(ContainerRequestContext requestCtx){
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED)
.header("WWW-Authenticate", RestAuthenticationMethod.BASIC.getMethod() + " realm=\"midpoint\", " + RestAuthenticationMethod.SECURITY_QUESTIONS.getMethod()).build());
requestCtx.abortWith(Response.status(Status.UNAUTHORIZED).build());
}

public static void createSecurityQuestionAbortMessage(ContainerRequestContext requestCtx, String secQChallenge){
Expand Down
Expand Up @@ -332,8 +332,8 @@ public void test017GetUnauthorizedUser() {
TestUtil.displayWhen(TEST_NAME);
Response response = client.get();

TestUtil.displayThen(TEST_NAME);
assertStatus(response, 403);
displayThen(TEST_NAME);
assertStatus(response, 401);

display("Audit", getDummyAuditService());
getDummyAuditService().assertRecords(1);
Expand Down Expand Up @@ -642,8 +642,8 @@ public void test131GetUserAdministratorByDarthAdder() {
TestUtil.displayWhen(TEST_NAME);
Response response = client.get();

TestUtil.displayThen(TEST_NAME);
assertStatus(response, 403);
displayThen(TEST_NAME);
assertStatus(response, 401);
assertNoEmptyResponse(response);

display("Audit", getDummyAuditService());
Expand Down Expand Up @@ -760,8 +760,8 @@ public void test141GetUserAdministratorByNopasswordBadPassword() {
TestUtil.displayWhen(TEST_NAME);
Response response = client.get();

TestUtil.displayThen(TEST_NAME);
assertStatus(response, 403);
displayThen(TEST_NAME);
assertStatus(response, 401);
assertNoEmptyResponse(response);

display("Audit", getDummyAuditService());
Expand Down

0 comments on commit cd01b50

Please sign in to comment.