| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.fineract.infrastructure.security.api; |
| |
| import com.google.gson.Gson; |
| import com.sun.jersey.core.util.Base64; |
| import io.swagger.v3.oas.annotations.Operation; |
| import io.swagger.v3.oas.annotations.media.Content; |
| import io.swagger.v3.oas.annotations.media.Schema; |
| import io.swagger.v3.oas.annotations.responses.ApiResponse; |
| import io.swagger.v3.oas.annotations.responses.ApiResponses; |
| import io.swagger.v3.oas.annotations.tags.Tag; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Set; |
| import javax.ws.rs.Consumes; |
| import javax.ws.rs.DefaultValue; |
| import javax.ws.rs.POST; |
| import javax.ws.rs.Path; |
| import javax.ws.rs.Produces; |
| import javax.ws.rs.QueryParam; |
| import javax.ws.rs.core.MediaType; |
| import org.apache.fineract.infrastructure.core.data.EnumOptionData; |
| import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; |
| import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants; |
| import org.apache.fineract.infrastructure.security.data.AuthenticatedUserData; |
| import org.apache.fineract.infrastructure.security.service.SpringSecurityPlatformSecurityContext; |
| import org.apache.fineract.infrastructure.security.service.TwoFactorUtils; |
| import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; |
| import org.apache.fineract.useradministration.data.RoleData; |
| import org.apache.fineract.useradministration.domain.AppUser; |
| import org.apache.fineract.useradministration.domain.Role; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.beans.factory.annotation.Qualifier; |
| import org.springframework.context.annotation.Profile; |
| import org.springframework.context.annotation.Scope; |
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
| import org.springframework.security.authentication.dao.DaoAuthenticationProvider; |
| import org.springframework.security.core.Authentication; |
| import org.springframework.security.core.GrantedAuthority; |
| import org.springframework.stereotype.Component; |
| |
| @Component |
| @Scope("singleton") |
| @Profile("basicauth") |
| @Path("/authentication") |
| @Tag(name = "Authentication HTTP Basic", description = "An API capability that allows client applications to verify authentication details using HTTP Basic Authentication.") |
| public class AuthenticationApiResource { |
| |
| public static class AuthenticateRequest { |
| |
| public String username; |
| public String password; |
| } |
| |
| private final DaoAuthenticationProvider customAuthenticationProvider; |
| private final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService; |
| private final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext; |
| private final TwoFactorUtils twoFactorUtils; |
| private final ClientReadPlatformService clientReadPlatformService; |
| |
| @Autowired |
| public AuthenticationApiResource( |
| @Qualifier("customAuthenticationProvider") final DaoAuthenticationProvider customAuthenticationProvider, |
| final ToApiJsonSerializer<AuthenticatedUserData> apiJsonSerializerService, |
| final SpringSecurityPlatformSecurityContext springSecurityPlatformSecurityContext, TwoFactorUtils twoFactorUtils, |
| ClientReadPlatformService aClientReadPlatformService) { |
| this.customAuthenticationProvider = customAuthenticationProvider; |
| this.apiJsonSerializerService = apiJsonSerializerService; |
| this.springSecurityPlatformSecurityContext = springSecurityPlatformSecurityContext; |
| this.twoFactorUtils = twoFactorUtils; |
| clientReadPlatformService = aClientReadPlatformService; |
| } |
| |
| @POST |
| @Consumes({ MediaType.APPLICATION_JSON }) |
| @Produces({ MediaType.APPLICATION_JSON }) |
| @Operation(summary = "Verify authentication", description = "Authenticates the credentials provided and returns the set roles and permissions allowed.") |
| @ApiResponses({ |
| @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AuthenticationApiResourceSwagger.PostAuthenticationResponse.class))), |
| @ApiResponse(responseCode = "400", description = "Unauthenticated. Please login") }) |
| public String authenticate(final String apiRequestBodyAsJson, |
| @QueryParam("returnClientList") @DefaultValue("false") boolean returnClientList) { |
| // TODO FINERACT-819: sort out Jersey so JSON conversion does not have |
| // to be done explicitly via GSON here, but implicit by arg |
| AuthenticateRequest request = new Gson().fromJson(apiRequestBodyAsJson, AuthenticateRequest.class); |
| if (request == null) { |
| throw new IllegalArgumentException( |
| "Invalid JSON in BODY (no longer URL param; see FINERACT-726) of POST to /authentication: " + apiRequestBodyAsJson); |
| } |
| if (request.username == null || request.password == null) { |
| throw new IllegalArgumentException("Username or Password is null in JSON (see FINERACT-726) of POST to /authentication: " |
| + apiRequestBodyAsJson + "; username=" + request.username + ", password=" + request.password); |
| } |
| |
| final Authentication authentication = new UsernamePasswordAuthenticationToken(request.username, request.password); |
| final Authentication authenticationCheck = this.customAuthenticationProvider.authenticate(authentication); |
| |
| final Collection<String> permissions = new ArrayList<>(); |
| AuthenticatedUserData authenticatedUserData = new AuthenticatedUserData(request.username, permissions); |
| |
| if (authenticationCheck.isAuthenticated()) { |
| final Collection<GrantedAuthority> authorities = new ArrayList<>(authenticationCheck.getAuthorities()); |
| for (final GrantedAuthority grantedAuthority : authorities) { |
| permissions.add(grantedAuthority.getAuthority()); |
| } |
| |
| final byte[] base64EncodedAuthenticationKey = Base64.encode(request.username + ":" + request.password); |
| |
| final AppUser principal = (AppUser) authenticationCheck.getPrincipal(); |
| final Collection<RoleData> roles = new ArrayList<>(); |
| final Set<Role> userRoles = principal.getRoles(); |
| for (final Role role : userRoles) { |
| roles.add(role.toData()); |
| } |
| |
| final Long officeId = principal.getOffice().getId(); |
| final String officeName = principal.getOffice().getName(); |
| |
| final Long staffId = principal.getStaffId(); |
| final String staffDisplayName = principal.getStaffDisplayName(); |
| |
| final EnumOptionData organisationalRole = principal.organisationalRoleData(); |
| |
| boolean isTwoFactorRequired = twoFactorUtils.isTwoFactorAuthEnabled() |
| && !principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION); |
| Long userId = principal.getId(); |
| if (this.springSecurityPlatformSecurityContext.doesPasswordHasToBeRenewed(principal)) { |
| authenticatedUserData = new AuthenticatedUserData(request.username, userId, |
| new String(base64EncodedAuthenticationKey, StandardCharsets.UTF_8), isTwoFactorRequired); |
| } else { |
| |
| authenticatedUserData = new AuthenticatedUserData(request.username, officeId, officeName, staffId, staffDisplayName, |
| organisationalRole, roles, permissions, principal.getId(), |
| new String(base64EncodedAuthenticationKey, StandardCharsets.UTF_8), isTwoFactorRequired, |
| returnClientList ? clientReadPlatformService.retrieveUserClients(userId) : null); |
| } |
| |
| } |
| |
| return this.apiJsonSerializerService.serialize(authenticatedUserData); |
| } |
| } |