Search in sources :

Example 1 with DuoSecurityUserAccount

use of org.apereo.cas.adaptors.duo.DuoSecurityUserAccount in project cas by apereo.

the class BaseDuoSecurityAuthenticationService method getUserAccount.

@Override
public DuoSecurityUserAccount getUserAccount(final String username) {
    if (!properties.isAccountStatusEnabled()) {
        LOGGER.debug("Checking Duo Security for user's [{}] account status is disabled", username);
        val account = new DuoSecurityUserAccount(username);
        account.setStatus(DuoSecurityUserAccountStatus.AUTH);
        return account;
    }
    val userAccountCachedMap = userAccountCache.asMap();
    if (userAccountCachedMap.containsKey(username)) {
        val account = userAccountCachedMap.get(username);
        LOGGER.debug("Found cached duo user account [{}]", account);
        return account;
    }
    val account = new DuoSecurityUserAccount(username);
    account.setStatus(DuoSecurityUserAccountStatus.AUTH);
    try {
        val userRequest = buildHttpPostUserPreAuthRequest(username);
        signHttpUserPreAuthRequest(userRequest);
        LOGGER.debug("Contacting Duo to inquire about username [{}]", username);
        val userResponse = getHttpResponse(userRequest);
        val jsonResponse = URLDecoder.decode(userResponse, StandardCharsets.UTF_8.name());
        LOGGER.debug("Received Duo response [{}]", jsonResponse);
        val result = MAPPER.readTree(jsonResponse);
        if (!result.has(RESULT_KEY_STAT)) {
            LOGGER.warn("Duo response was received in unknown format: [{}]", jsonResponse);
            throw new DuoWebException("Invalid response format received from Duo");
        }
        if (result.get(RESULT_KEY_STAT).asText().equalsIgnoreCase("OK")) {
            val response = result.get(RESULT_KEY_RESPONSE);
            val authResult = response.get(RESULT_KEY_RESULT).asText().toUpperCase();
            val status = DuoSecurityUserAccountStatus.valueOf(authResult);
            account.setStatus(status);
            account.setMessage(response.get(RESULT_KEY_STATUS_MESSAGE).asText());
            if (status == DuoSecurityUserAccountStatus.ENROLL) {
                val enrollUrl = response.get(RESULT_KEY_ENROLL_PORTAL_URL).asText();
                account.setEnrollPortalUrl(enrollUrl);
            }
        } else {
            val code = result.get(RESULT_KEY_CODE).asInt();
            if (code > RESULT_CODE_ERROR_THRESHOLD) {
                LOGGER.warn("Duo returned a failure response with code: [{}]. Duo will be considered unavailable", result.get(RESULT_KEY_MESSAGE));
                throw new DuoWebException("Duo returned code 500: " + result.get(RESULT_KEY_MESSAGE));
            }
            LOGGER.warn("Duo returned an Invalid response with message [{}] and detail [{}] " + "when determining user account. This maybe a configuration error in the admin request and Duo will " + "still be considered available.", result.hasNonNull(RESULT_KEY_MESSAGE) ? result.get(RESULT_KEY_MESSAGE).asText() : StringUtils.EMPTY, result.hasNonNull(RESULT_KEY_MESSAGE_DETAIL) ? result.get(RESULT_KEY_MESSAGE_DETAIL).asText() : StringUtils.EMPTY);
        }
    } catch (final Exception e) {
        LOGGER.warn("Reaching Duo has failed with error: [{}]", e.getMessage(), e);
        account.setStatus(DuoSecurityUserAccountStatus.UNAVAILABLE);
    }
    userAccountCachedMap.put(account.getUsername(), account);
    LOGGER.debug("Fetched and cached duo user account [{}]", account);
    return account;
}
Also used : lombok.val(lombok.val) DuoSecurityUserAccount(org.apereo.cas.adaptors.duo.DuoSecurityUserAccount) DuoWebException(com.duosecurity.duoweb.DuoWebException) DuoWebException(com.duosecurity.duoweb.DuoWebException)

Example 2 with DuoSecurityUserAccount

use of org.apereo.cas.adaptors.duo.DuoSecurityUserAccount in project cas by apereo.

the class DuoSecurityAdminApiEndpoint method getUser.

/**
 * Fetch duo user account from admin api.
 *
 * @param username   the username
 * @param providerId the provider id
 * @return the map
 */
@GetMapping(path = "/{username}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Fetch Duo Security user account from Duo Admin API", parameters = { @Parameter(name = "username", required = true, in = ParameterIn.PATH), @Parameter(name = "providerId") })
public Map<String, DuoSecurityUserAccount> getUser(@PathVariable("username") final String username, @RequestParam(required = false) final String providerId) {
    val results = new LinkedHashMap<String, DuoSecurityUserAccount>();
    val providers = applicationContext.getBeansOfType(DuoSecurityMultifactorAuthenticationProvider.class).values();
    providers.stream().filter(Objects::nonNull).map(DuoSecurityMultifactorAuthenticationProvider.class::cast).filter(provider -> StringUtils.isBlank(providerId) || provider.matches(providerId)).filter(provider -> provider.getDuoAuthenticationService().getAdminApiService().isPresent()).forEach(Unchecked.consumer(p -> {
        val duoService = p.getDuoAuthenticationService().getAdminApiService().get();
        duoService.getDuoSecurityUserAccount(username).ifPresent(user -> results.put(p.getId(), user));
    }));
    return results;
}
Also used : lombok.val(lombok.val) DuoSecurityMultifactorAuthenticationProvider(org.apereo.cas.adaptors.duo.authn.DuoSecurityMultifactorAuthenticationProvider) CasConfigurationProperties(org.apereo.cas.configuration.CasConfigurationProperties) PathVariable(org.springframework.web.bind.annotation.PathVariable) PostMapping(org.springframework.web.bind.annotation.PostMapping) RequestParam(org.springframework.web.bind.annotation.RequestParam) Unchecked(org.jooq.lambda.Unchecked) MediaType(org.springframework.http.MediaType) lombok.val(lombok.val) StringUtils(org.apache.commons.lang3.StringUtils) ApplicationContext(org.springframework.context.ApplicationContext) DuoSecurityMultifactorAuthenticationProvider(org.apereo.cas.adaptors.duo.authn.DuoSecurityMultifactorAuthenticationProvider) ParameterIn(io.swagger.v3.oas.annotations.enums.ParameterIn) BaseCasActuatorEndpoint(org.apereo.cas.web.BaseCasActuatorEndpoint) Parameter(io.swagger.v3.oas.annotations.Parameter) RestControllerEndpoint(org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint) LinkedHashMap(java.util.LinkedHashMap) Objects(java.util.Objects) Operation(io.swagger.v3.oas.annotations.Operation) List(java.util.List) Map(java.util.Map) GetMapping(org.springframework.web.bind.annotation.GetMapping) DuoSecurityUserAccount(org.apereo.cas.adaptors.duo.DuoSecurityUserAccount) Objects(java.util.Objects) LinkedHashMap(java.util.LinkedHashMap) GetMapping(org.springframework.web.bind.annotation.GetMapping) Operation(io.swagger.v3.oas.annotations.Operation)

Example 3 with DuoSecurityUserAccount

use of org.apereo.cas.adaptors.duo.DuoSecurityUserAccount in project cas by apereo.

the class DuoSecurityAdminApiEndpoint method createBypassCodes.

/**
 * Create bypass codes.
 *
 * @param username   the username
 * @param providerId the provider id
 * @param userId     the user id
 * @return the map
 */
@Operation(summary = "Create bypass codes using Duo Admin API", parameters = { @Parameter(name = "username", required = true), @Parameter(name = "providerId"), @Parameter(name = "userId") })
@PostMapping(path = "/bypassCodes", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, List<Long>> createBypassCodes(@RequestParam(value = "username", required = false) final String username, @RequestParam(value = "providerId", required = false) final String providerId, @RequestParam(value = "userId", required = false) final String userId) {
    val results = new LinkedHashMap<String, List<Long>>();
    val providers = applicationContext.getBeansOfType(DuoSecurityMultifactorAuthenticationProvider.class).values();
    providers.stream().filter(Objects::nonNull).map(DuoSecurityMultifactorAuthenticationProvider.class::cast).filter(provider -> StringUtils.isBlank(providerId) || provider.matches(providerId)).filter(provider -> provider.getDuoAuthenticationService().getAdminApiService().isPresent()).forEach(Unchecked.consumer(p -> {
        val duoService = p.getDuoAuthenticationService().getAdminApiService().get();
        val uid = StringUtils.isBlank(userId) ? duoService.getDuoSecurityUserAccount(username).map(DuoSecurityUserAccount::getUserId).orElse(StringUtils.EMPTY) : userId;
        if (StringUtils.isNotBlank(uid)) {
            val codes = duoService.createDuoSecurityBypassCodesFor(uid);
            results.put(p.getId(), codes);
        }
    }));
    return results;
}
Also used : lombok.val(lombok.val) DuoSecurityMultifactorAuthenticationProvider(org.apereo.cas.adaptors.duo.authn.DuoSecurityMultifactorAuthenticationProvider) CasConfigurationProperties(org.apereo.cas.configuration.CasConfigurationProperties) PathVariable(org.springframework.web.bind.annotation.PathVariable) PostMapping(org.springframework.web.bind.annotation.PostMapping) RequestParam(org.springframework.web.bind.annotation.RequestParam) Unchecked(org.jooq.lambda.Unchecked) MediaType(org.springframework.http.MediaType) lombok.val(lombok.val) StringUtils(org.apache.commons.lang3.StringUtils) ApplicationContext(org.springframework.context.ApplicationContext) DuoSecurityMultifactorAuthenticationProvider(org.apereo.cas.adaptors.duo.authn.DuoSecurityMultifactorAuthenticationProvider) ParameterIn(io.swagger.v3.oas.annotations.enums.ParameterIn) BaseCasActuatorEndpoint(org.apereo.cas.web.BaseCasActuatorEndpoint) Parameter(io.swagger.v3.oas.annotations.Parameter) RestControllerEndpoint(org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint) LinkedHashMap(java.util.LinkedHashMap) Objects(java.util.Objects) Operation(io.swagger.v3.oas.annotations.Operation) List(java.util.List) Map(java.util.Map) GetMapping(org.springframework.web.bind.annotation.GetMapping) DuoSecurityUserAccount(org.apereo.cas.adaptors.duo.DuoSecurityUserAccount) Objects(java.util.Objects) LinkedHashMap(java.util.LinkedHashMap) PostMapping(org.springframework.web.bind.annotation.PostMapping) Operation(io.swagger.v3.oas.annotations.Operation)

Example 4 with DuoSecurityUserAccount

use of org.apereo.cas.adaptors.duo.DuoSecurityUserAccount in project cas by apereo.

the class DefaultDuoSecurityAdminApiService method mapDuoSecurityUserAccount.

private static DuoSecurityUserAccount mapDuoSecurityUserAccount(final JSONObject userJson) throws JSONException {
    val user = new DuoSecurityUserAccount(userJson.getString("username"));
    user.setStatus(DuoSecurityUserAccountStatus.from(userJson.getString("status")));
    FunctionUtils.doIfNotNull(userJson.get("email"), value -> user.addAttribute("email", value.toString()));
    FunctionUtils.doIfNotNull(userJson.getString("user_id"), value -> user.addAttribute("user_id", value));
    FunctionUtils.doIfNotNull(userJson.get("firstname"), value -> user.addAttribute("firstname", value.toString()));
    FunctionUtils.doIfNotNull(userJson.get("lastname"), value -> user.addAttribute("lastname", value.toString()));
    FunctionUtils.doIfNotNull(userJson.get("realname"), value -> user.addAttribute("realname", value.toString()));
    FunctionUtils.doIfNotNull(userJson.getBoolean("is_enrolled"), value -> user.addAttribute("is_enrolled", value.toString()));
    FunctionUtils.doIfNotNull(userJson.getLong("last_login"), value -> user.addAttribute("last_login", value.toString()));
    FunctionUtils.doIfNotNull(userJson.getLong("created"), value -> user.addAttribute("created", value.toString()));
    FunctionUtils.doIfNotNull(userJson.optString("alias1"), value -> user.addAttribute("alias1", value));
    FunctionUtils.doIfNotNull(userJson.optString("alias2"), value -> user.addAttribute("alias2", value));
    if (user.getStatus() != DuoSecurityUserAccountStatus.DENY && !user.isEnrolled()) {
        user.setStatus(DuoSecurityUserAccountStatus.ENROLL);
    }
    mapUserPhones(userJson, user);
    mapUserGroups(userJson, user);
    return user;
}
Also used : lombok.val(lombok.val) DuoSecurityUserAccount(org.apereo.cas.adaptors.duo.DuoSecurityUserAccount)

Example 5 with DuoSecurityUserAccount

use of org.apereo.cas.adaptors.duo.DuoSecurityUserAccount in project cas by apereo.

the class DuoSecurityDetermineUserAccountActionTests method verifyOperation.

@SneakyThrows
private static void verifyOperation(final DuoSecurityUserAccountStatus status, final String eventId) {
    val context = new MockRequestContext();
    val request = new MockHttpServletRequest();
    val response = new MockHttpServletResponse();
    context.setExternalContext(new ServletExternalContext(new MockServletContext(), request, response));
    WebUtils.putServiceIntoFlowScope(context, CoreAuthenticationTestUtils.getWebApplicationService());
    val authentication = CoreAuthenticationTestUtils.getAuthentication();
    WebUtils.putAuthentication(authentication, context);
    val account = new DuoSecurityUserAccount(authentication.getPrincipal().getId());
    account.setStatus(status);
    account.setEnrollPortalUrl("https://example.org");
    val duoService = mock(DuoSecurityAuthenticationService.class);
    when(duoService.getUserAccount(anyString())).thenReturn(account);
    val provider = mock(DuoSecurityMultifactorAuthenticationProvider.class);
    when(provider.getId()).thenReturn(DuoSecurityMultifactorAuthenticationProperties.DEFAULT_IDENTIFIER);
    when(provider.getDuoAuthenticationService()).thenReturn(duoService);
    when(provider.getRegistrationUrl()).thenReturn("https://registration.duo.com");
    when(provider.matches(anyString())).thenReturn(Boolean.TRUE);
    val applicationContext = new StaticApplicationContext();
    applicationContext.refresh();
    ApplicationContextProvider.holdApplicationContext(applicationContext);
    ApplicationContextProvider.registerBeanIntoApplicationContext(applicationContext, MultifactorAuthenticationPrincipalResolver.identical(), UUID.randomUUID().toString());
    WebUtils.putMultifactorAuthenticationProviderIdIntoFlowScope(context, provider);
    TestMultifactorAuthenticationProvider.registerProviderIntoApplicationContext(applicationContext, provider);
    val action = new DuoSecurityDetermineUserAccountAction();
    val event = action.execute(context);
    assertEquals(eventId, event.getId());
}
Also used : lombok.val(lombok.val) StaticApplicationContext(org.springframework.context.support.StaticApplicationContext) MockHttpServletRequest(org.springframework.mock.web.MockHttpServletRequest) ServletExternalContext(org.springframework.webflow.context.servlet.ServletExternalContext) DuoSecurityUserAccount(org.apereo.cas.adaptors.duo.DuoSecurityUserAccount) MockRequestContext(org.springframework.webflow.test.MockRequestContext) MockHttpServletResponse(org.springframework.mock.web.MockHttpServletResponse) MockServletContext(org.apereo.cas.util.MockServletContext) SneakyThrows(lombok.SneakyThrows)

Aggregations

lombok.val (lombok.val)6 DuoSecurityUserAccount (org.apereo.cas.adaptors.duo.DuoSecurityUserAccount)6 Operation (io.swagger.v3.oas.annotations.Operation)2 Parameter (io.swagger.v3.oas.annotations.Parameter)2 ParameterIn (io.swagger.v3.oas.annotations.enums.ParameterIn)2 LinkedHashMap (java.util.LinkedHashMap)2 List (java.util.List)2 Map (java.util.Map)2 Objects (java.util.Objects)2 StringUtils (org.apache.commons.lang3.StringUtils)2 DuoSecurityMultifactorAuthenticationProvider (org.apereo.cas.adaptors.duo.authn.DuoSecurityMultifactorAuthenticationProvider)2 CasConfigurationProperties (org.apereo.cas.configuration.CasConfigurationProperties)2 BaseCasActuatorEndpoint (org.apereo.cas.web.BaseCasActuatorEndpoint)2 Unchecked (org.jooq.lambda.Unchecked)2 RestControllerEndpoint (org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint)2 ApplicationContext (org.springframework.context.ApplicationContext)2 MediaType (org.springframework.http.MediaType)2 GetMapping (org.springframework.web.bind.annotation.GetMapping)2 PathVariable (org.springframework.web.bind.annotation.PathVariable)2 PostMapping (org.springframework.web.bind.annotation.PostMapping)2