/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved.
* <p>
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
* <p>
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.authentication.manager;
import org.cloudfoundry.identity.uaa.audit.AuditEvent;
import org.cloudfoundry.identity.uaa.audit.AuditEventType;
import org.cloudfoundry.identity.uaa.audit.UaaAuditService;
import org.cloudfoundry.identity.uaa.provider.LockoutPolicy;
import org.cloudfoundry.identity.uaa.util.TimeService;
import java.util.List;
/**
* Common login policy for both user login and client credential authentication, specifically for
* lockouts.
*/
public class CommonLoginPolicy implements LoginPolicy {
private final UaaAuditService auditService;
private final LockoutPolicyRetriever lockoutPolicyRetriever;
private final AuditEventType successEventType;
private final AuditEventType failureEventType;
private final TimeService timeService;
private final boolean enabled;
public CommonLoginPolicy(UaaAuditService auditService,
LockoutPolicyRetriever lockoutPolicyRetriever,
AuditEventType successEventType,
AuditEventType failureEventType,
TimeService timeService,
boolean enabled) {
this.auditService = auditService;
this.lockoutPolicyRetriever = lockoutPolicyRetriever;
this.successEventType = successEventType;
this.failureEventType = failureEventType;
this.timeService = timeService;
this.enabled = enabled;
}
@Override
public Result isAllowed(String principalId) {
int failureCount = 0;
if (enabled) {
LockoutPolicy lockoutPolicy = lockoutPolicyRetriever.getLockoutPolicy();
long eventsAfter = timeService.getCurrentTimeMillis() - lockoutPolicy.getCountFailuresWithin() * 1000;
List<AuditEvent> events = auditService.find(principalId, eventsAfter);
failureCount = sequentialFailureCount(events);
if (failureCount >= lockoutPolicy.getLockoutAfterFailures()) {
// Check whether time of most recent failure is within the lockout period
AuditEvent lastFailure = mostRecentFailure(events);
if (lastFailure != null && lastFailure.getTime() > timeService.getCurrentTimeMillis() - lockoutPolicy.getLockoutPeriodSeconds() * 1000) {
return new Result(false, failureCount);
}
}
}
return new Result(true, failureCount);
}
/**
* Counts the number of failures that occurred without an intervening
* successful login.
*/
private int sequentialFailureCount(List<AuditEvent> events) {
int failureCount = 0;
for (AuditEvent event : events) {
if (event.getType() == failureEventType) {
failureCount++;
} else if (event.getType() == successEventType) {
// Successful authentication occurred within last allowable
// failures, so ignore
break;
}
}
return failureCount;
}
private AuditEvent mostRecentFailure(List<AuditEvent> events) {
for (AuditEvent event : events) {
if (event.getType() == failureEventType) {
return event;
}
}
return null;
}
public LockoutPolicyRetriever getLockoutPolicyRetriever() {
return lockoutPolicyRetriever;
}
}