package com.citruspay.adminsite.subscription.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import com.citruspay.adminsite.subscription.response.bean.MemberInfoResponse;
import com.citruspay.adminsite.subscription.service.TokenValidatorExtendedProcess;
@Service
@Path("/v2/")
public class TokenValidatorService {
private final TokenStore tStore;
@Autowired
private ResourceServerTokenServices tokenServices;
private static final Logger logger = LoggerFactory.getLogger(TokenValidatorService.class);
private final TokenValidatorExtendedProcess tokenValidatorExtendedProcess;
@Autowired
public TokenValidatorService(TokenStore tStore, TokenValidatorExtendedProcess tokenValidatorExtendedProcess) {
this.tStore = tStore;
this.tokenValidatorExtendedProcess = tokenValidatorExtendedProcess;
}
@GET
@Path("token/validate")
@Produces(MediaType.APPLICATION_JSON)
public Response accessible(@HeaderParam("OwnerAuthorization") String auth,
@HeaderParam("OwnerScope") String scope) {
if (StringUtils.isBlank(auth) || StringUtils.isBlank(scope) || !StringUtils.startsWith(auth, "Bearer ")) {
return unauthorized(Response.Status.BAD_REQUEST, "either token or scope is null");
}
OAuth2AccessToken accessToken = tStore.readAccessToken(auth.substring(7));
if (null == accessToken || null == accessToken.getScope() || accessToken.isExpired()) {
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Token");
}
if (accessToken.getScope().contains(scope)) {
return Response.ok("{\"expiration\": \"" + accessToken.getExpiration() + "\"}", MediaType.APPLICATION_JSON)
.build();
}
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied");
}
@Context
private HttpServletRequest serveletRequest;
@GET
@Path("token/validate/extended")
@Produces(MediaType.APPLICATION_JSON)
public Response validate(@HeaderParam("OwnerAuthorization") String auth, @HeaderParam("OwnerScope") String scopecsv,
@HeaderParam("OwnerRole") String role, @HeaderParam("httpMethod") String httpMethod,
@HeaderParam("attributeCSV") String attributeCSV) {
logger.info("token/validate/extended validation started for token with scope : {}, role : {}", new Object[] { scopecsv, role });
logger.info("***************************************************"); Enumeration<String> headerNames = serveletRequest.getHeaderNames();
while(headerNames.hasMoreElements()) {
 String headerName = (String)headerNames.nextElement();
 logger.info("-->"+headerName+": "+serveletRequest.getHeader(headerName)); Â
}
if (StringUtils.isBlank(auth) || StringUtils.isEmpty(scopecsv)
|| !StringUtils.startsWithIgnoreCase(auth, "Bearer ")) {
logger.error("OwnerAuthorization is not valid or OwnerScope not valid for token : {}",
new Object[] { auth });
return unauthorized(Response.Status.BAD_REQUEST, "either token or scope is null");
}
OAuth2Authentication authentication = null;
String accessTokenVal = (auth.replaceAll("\\s+", " ").split(" "))[1];
OAuth2AccessToken accessToken = null;
try {
accessToken = tStore.readAccessToken(accessTokenVal);
authentication = tokenServices.loadAuthentication(accessTokenVal);
String clientId = authentication.getOAuth2Request().getClientId();
logger.info("client id for token is {}.", new Object[] { clientId }); } catch (Exception e) {
logger.error("AccessToken not Authenticated due to : {}", new Object[] { e });
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Token");
}
try {
String[] scopeArray = scopecsv.split(",");
if (isAccessGranted(authentication, scopeArray, role, attributeCSV)) {
String principal = authentication.getName();
logger.info("principal found while validating token .Principal is {}", new Object[] { principal });
String clientId = authentication.getOAuth2Request().getClientId();
MemberInfoResponse memberInfoResponse = tokenValidatorExtendedProcess
.getMemberInfoResponse(accessToken.getExpiresIn(), principal, clientId,scopecsv);
if (memberInfoResponse == null) {
logger.info("Member info not found for principal : {}", new Object[] { principal }); logger.info("Scope is {} for token.", new Object[] { scopecsv }); String grant_type=authentication.getOAuth2Request().getRequestParameters().get("grant_type");
boolean isImplicitorAdminToken="implicit".equals(grant_type);
return Response.ok(new MemberInfoResponse(clientId,scopecsv,accessToken.getExpiresIn(),isImplicitorAdminToken,""), MediaType.APPLICATION_JSON).build();
} else {
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
String roles="";
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().contains("ROLE")) {
roles+=authority.getAuthority();
}
}
boolean isAdminToken= StringUtils.contains(roles, "ROLE_CITRUSPAY_MERCHANT_SUPERVISOR");
memberInfoResponse.setImplicitType(isAdminToken);
return Response.ok(memberInfoResponse, MediaType.APPLICATION_JSON).build();
}
} else {
throw new AccessDeniedException("Access Denied");
}
} catch (Exception e) {
logger.error("Resource not accessible for Access Token {} due to : {}", new Object[] { auth }, e);
return unauthorized(Response.Status.UNAUTHORIZED, "Access Denied, Invalid Authorities");
}
}
private boolean isAccessGranted(OAuth2Authentication authentication, String[] scopeArray, String role,
String attributeCSV) {
Set<String> scopeSet = authentication.getOAuth2Request().getScope();
// String role=authentication.getDetails();
int grant = 0;
int denied = 0;
boolean scopeMatched = false;
outer_loop: for (String s : scopeArray) {
for (String t : scopeSet) {
if (s.equals(t.trim())) {
scopeMatched = true;
break outer_loop;
}
}
}
if (scopeMatched) {
grant++;
} else {
logger.error("Scope Required Not Present in Access Token expected : {} , got : {}", new Object[] { scopeArray, scopeSet });
denied++;
}
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
if (!StringUtils.isBlank(role)) {
boolean rolePresent = false;
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equalsIgnoreCase(role)) {
grant++;
rolePresent = true;
break;
}
}
if (!rolePresent) {
logger.error("Role {} Not Present in Access Token",role);
denied++;
}
}
if (attributeCSV != null) {
String[] attributeArray = attributeCSV.split(",");
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
if (attributeArray.length > 0 && !StringUtils.isEmpty(attributeCSV)) {
for (String attr : attributeArray) {
attributes.add(new SecurityConfig(attr));
AuthenticatedVoter authenticationVoter = new AuthenticatedVoter();
int result = authenticationVoter.vote(authentication, null, attributes);
if (result == 1) {
grant++;
} else {
logger.error("Authentication Voter failed the authentication");
denied++;
}
}
}
}
if (grant > denied) {
return true;
}
return false;
}
private Response unauthorized(Response.Status status, String message) {
return Response.status(status).entity("{\"error\": \"" + message + "\"}").type(MediaType.APPLICATION_JSON)
.build();
}
}