use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.
the class UserInfoReader method isRequiresOtpConfig.
@Override
public boolean isRequiresOtpConfig() throws PwmUnrecoverableException {
LOGGER.trace(sessionLabel, "checkOtp: beginning process to check if user OTP setup is required");
SetupOtpProfile setupOtpProfile = null;
final Map<ProfileType, String> profileIDs = selfCachedReference.getProfileIDs();
if (profileIDs.containsKey(ProfileType.UpdateAttributes)) {
setupOtpProfile = pwmApplication.getConfig().getSetupOTPProfiles().get(profileIDs.get(ProfileType.SetupOTPProfile));
}
if (setupOtpProfile == null) {
LOGGER.trace(sessionLabel, "checkOtp: no otp setup profile assigned, user OTP setup is not required");
return false;
}
if (!setupOtpProfile.readSettingAsBoolean(PwmSetting.OTP_ALLOW_SETUP)) {
LOGGER.trace(sessionLabel, "checkOtp: OTP allow setup is not enabled");
return false;
}
final ForceSetupPolicy policy = setupOtpProfile.readSettingAsEnum(PwmSetting.OTP_FORCE_SETUP, ForceSetupPolicy.class);
if (policy == ForceSetupPolicy.SKIP) {
LOGGER.trace(sessionLabel, "checkOtp: OTP force setup policy is set to SKIP, user OTP setup is not required");
return false;
}
final OTPUserRecord otpUserRecord = selfCachedReference.getOtpUserRecord();
final boolean hasStoredOtp = otpUserRecord != null && otpUserRecord.getSecret() != null;
if (hasStoredOtp) {
LOGGER.trace(sessionLabel, "checkOtp: user has existing valid otp record, user OTP setup is not required");
return false;
}
// hasStoredOtp is always true at this point, so if forced then update needed
LOGGER.debug(sessionLabel, "checkOtp: user does not have existing valid otp record, user OTP setup is required");
return policy == ForceSetupPolicy.FORCE || policy == ForceSetupPolicy.FORCE_ALLOW_SKIP;
}
use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.
the class HelpdeskServlet method restValidateOtpCodeRequest.
@ActionHandler(action = "validateOtpCode")
private ProcessStatus restValidateOtpCodeRequest(final PwmRequest pwmRequest) throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException {
final HelpdeskProfile helpdeskProfile = getHelpdeskProfile(pwmRequest);
final Instant startTime = Instant.now();
final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(pwmRequest.readRequestBodyAsString(), HelpdeskVerificationRequestBean.class);
final String userKey = helpdeskVerificationRequestBean.getUserKey();
if (userKey == null || userKey.isEmpty()) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing");
pwmRequest.respondWithError(errorInformation, false);
return ProcessStatus.Halt;
}
final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
if (!helpdeskProfile.readOptionalVerificationMethods().contains(IdentityVerificationMethod.OTP)) {
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password otp verification request, but otp verify is not enabled");
LOGGER.error(pwmRequest, errorInformation);
pwmRequest.respondWithError(errorInformation);
return ProcessStatus.Halt;
}
final String code = helpdeskVerificationRequestBean.getCode();
final OTPUserRecord otpUserRecord = pwmRequest.getPwmApplication().getOtpService().readOTPUserConfiguration(pwmRequest.getSessionLabel(), userIdentity);
try {
final boolean passed = pwmRequest.getPwmApplication().getOtpService().validateToken(pwmRequest.getSessionLabel(), userIdentity, otpUserRecord, code, false);
final HelpdeskVerificationStateBean verificationStateBean = HelpdeskVerificationStateBean.fromClientString(pwmRequest, helpdeskVerificationRequestBean.getVerificationState());
if (passed) {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(AuditEvent.HELPDESK_VERIFY_OTP, pwmSession.getUserInfo().getUserIdentity(), null, userIdentity, pwmSession.getSessionStateBean().getSrcAddress(), pwmSession.getSessionStateBean().getSrcHostname());
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_VERIFY_OTP);
verificationStateBean.addRecord(userIdentity, IdentityVerificationMethod.OTP);
} else {
final PwmSession pwmSession = pwmRequest.getPwmSession();
final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(pwmRequest).createHelpdeskAuditRecord(AuditEvent.HELPDESK_VERIFY_OTP_INCORRECT, pwmSession.getUserInfo().getUserIdentity(), null, userIdentity, pwmSession.getSessionStateBean().getSrcAddress(), pwmSession.getSessionStateBean().getSrcHostname());
pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
}
// add a delay to prevent continuous checks
final long delayMs = Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.HELPDESK_VERIFICATION_INVALID_DELAY_MS));
while (TimeDuration.fromCurrent(startTime).isShorterThan(delayMs)) {
JavaHelper.pause(100);
}
final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
final RestResultBean restResultBean = RestResultBean.withData(responseBean);
pwmRequest.outputJsonResult(restResultBean);
} catch (PwmOperationalException e) {
pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
}
return ProcessStatus.Halt;
}
use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.
the class OtpService method readOTPUserConfiguration.
public OTPUserRecord readOTPUserConfiguration(final SessionLabel sessionLabel, final UserIdentity userIdentity) throws PwmUnrecoverableException, ChaiUnavailableException {
OTPUserRecord otpConfig = null;
final Configuration config = pwmApplication.getConfig();
final Date methodStartTime = new Date();
final List<DataStorageMethod> otpSecretStorageLocations = config.getOtpSecretStorageLocations(PwmSetting.OTP_SECRET_READ_PREFERENCE);
if (otpSecretStorageLocations != null) {
final String userGUID = readGuidIfNeeded(pwmApplication, sessionLabel, otpSecretStorageLocations, userIdentity);
final Iterator<DataStorageMethod> locationIterator = otpSecretStorageLocations.iterator();
while (otpConfig == null && locationIterator.hasNext()) {
final DataStorageMethod location = locationIterator.next();
final OtpOperator operator = operatorMap.get(location);
if (operator != null) {
try {
otpConfig = operator.readOtpUserConfiguration(userIdentity, userGUID);
} catch (Exception e) {
LOGGER.error(sessionLabel, "unexpected error reading stored otp configuration from " + location + " for user " + userIdentity + ", error: " + e.getMessage());
}
} else {
LOGGER.warn(sessionLabel, String.format("storage location %s not implemented", location.toString()));
}
}
}
LOGGER.trace(sessionLabel, "readOTPUserConfiguration completed in " + TimeDuration.fromCurrent(methodStartTime).asCompactString() + (otpConfig == null ? ", no otp record found" : ", recordType=" + otpConfig.getType() + ", identifier=" + otpConfig.getIdentifier() + ", timestamp=" + JavaHelper.toIsoDate(otpConfig.getTimestamp())));
return otpConfig;
}
use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.
the class OtpService method validateToken.
public boolean validateToken(final SessionLabel sessionLabel, final UserIdentity userIdentity, final OTPUserRecord otpUserRecord, final String userInput, final boolean allowRecoveryCodes) throws PwmOperationalException, PwmUnrecoverableException {
boolean otpCorrect = false;
try {
final Base32 base32 = new Base32();
final byte[] rawSecret = base32.decode(otpUserRecord.getSecret());
final Mac mac = Mac.getInstance("HMACSHA1");
mac.init(new SecretKeySpec(rawSecret, ""));
final PasscodeGenerator generator = new PasscodeGenerator(mac, settings.getOtpTokenLength(), settings.getTotpIntervalSeconds());
switch(otpUserRecord.getType()) {
case TOTP:
otpCorrect = generator.verifyTimeoutCode(userInput, settings.getTotpPastIntervals(), settings.getTotpFutureIntervals());
break;
default:
throw new UnsupportedOperationException("OTP type not supported: " + otpUserRecord.getType());
}
} catch (Exception e) {
LOGGER.error(sessionLabel, "error checking otp secret: " + e.getMessage());
}
if (!otpCorrect && allowRecoveryCodes && otpUserRecord.getRecoveryCodes() != null && otpUserRecord.getRecoveryInfo() != null) {
final OTPUserRecord.RecoveryInfo recoveryInfo = otpUserRecord.getRecoveryInfo();
final String userHashedInput = doRecoveryHash(userInput, recoveryInfo);
for (final OTPUserRecord.RecoveryCode code : otpUserRecord.getRecoveryCodes()) {
if (code.getHashCode().equals(userInput) || code.getHashCode().equals(userHashedInput)) {
if (code.isUsed()) {
throw new PwmOperationalException(PwmError.ERROR_OTP_RECOVERY_USED, "recovery code has been previously used");
}
code.setUsed(true);
try {
pwmApplication.getOtpService().writeOTPUserConfiguration(null, userIdentity, otpUserRecord);
} catch (ChaiUnavailableException e) {
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_WRITING_OTP_SECRET, e.getMessage()));
}
otpCorrect = true;
}
}
}
return otpCorrect;
}
use of password.pwm.util.operations.otp.OTPUserRecord in project pwm by pwm-project.
the class RestVerifyOtpServer method doSetOtpDataJson.
@RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
public RestResultBean doSetOtpDataJson(final RestRequest restRequest) throws IOException, PwmUnrecoverableException {
final RestVerifyOtpServer.JsonPutOtpInput jsonInput;
{
final RestVerifyOtpServer.JsonPutOtpInput jsonBody = RestUtility.deserializeJsonBody(restRequest, RestVerifyOtpServer.JsonPutOtpInput.class, RestUtility.Flag.AllowNullReturn);
jsonInput = new RestVerifyOtpServer.JsonPutOtpInput(RestUtility.readValueFromJsonAndParam(jsonBody == null ? null : jsonBody.getToken(), restRequest.readParameterAsString("token"), "token"), RestUtility.readValueFromJsonAndParam(jsonBody == null ? null : jsonBody.getUsername(), restRequest.readParameterAsString("username"), "username"));
}
final TargetUserIdentity targetUserIdentity = RestUtility.resolveRequestedUsername(restRequest, jsonInput.getUsername());
try {
final OtpService otpService = restRequest.getPwmApplication().getOtpService();
final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity());
final boolean verified = otpUserRecord != null && otpService.validateToken(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity(), otpUserRecord, jsonInput.getToken(), false);
StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_VERIFYOTP);
return RestResultBean.forSuccessMessage(verified, restRequest, Message.Success_Unknown);
} catch (ChaiUnavailableException e) {
throw PwmUnrecoverableException.fromChaiException(e);
} catch (PwmOperationalException e) {
final String errorMsg = "unexpected error reading json input: " + e.getMessage();
final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
return RestResultBean.fromError(restRequest, errorInformation);
}
}
Aggregations