All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xlcloud.iam.DecisionResourceClient Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 AMG.lab, a Bull Group Company
 * 
 * Licensed 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.xlcloud.iam;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.xlcloud.config.ConfigParam;
import org.xlcloud.iam.Decision.DecisionAnswer;
import org.xlcloud.logging.LoggingUtils;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

/**
 * Calls OpenAM's decision service in order to determine the access permission
 * 
 * @author Jakub Wachowski, AMG.net
 *
 */
public class DecisionResourceClient {
    
    private final Logger LOG = Logger.getLogger(DecisionResourceClient.class);
    
    @Inject
    @IamRestClient
    private Client client;
    
    public static final String DECISION_SERVICE_URL = IamUtils.getOpenAmUrl() + "/ws/1/entitlement/entitlement";
    public static final String QUERY_PARAM_REALM = "realm";
    public static final String QUERY_PARAM_APPLICATION = "application";
    public static final String QUERY_PARAM_RESOURCE = "resource";
    public static final String ACCEPT_AUTHN_HEADER = "X-Accept-Authentication";
    public static final String QUERY_PARAMS_HEADER = "X-Query-Parameters";
    public static final String AUTHN_SCHEMA = "oauth";
    public static final String AUTHZ_SCHEMA = "oauth";
    public static final String AUTHZ_SCHEMA_SEPARATOR = ":";
    
    private static final String REASON_ATTRIBUTE_KEY = "reason";
    private static final String REASON_RESTRICTED_VALUE = "restricted";
    private static final String DETAIL_ATTRIBUTE_KEY = "details";
    
    @Inject
    @ConfigParam
    private String xlcRealmName;
    
    private String decisionServiceUrl = DECISION_SERVICE_URL;

    @PostConstruct
    public void init() {
        LOG.debug("Using Jersey client " + client.hashCode());
    }
    
    /**
     * Determines whether a given token is allowed to access given resource with given method
     * @param accessToken OAuth2's access token
     * @param method http method (GET, POST, DELETE or PUT)
     * @param resource resource for which access decision is to be made
     * @return true if given access token is allowed to access the resource, false otherwise
     */
    public Decision isAllowed(String accessToken, String method, String resource) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("evaluating access for token=[" + LoggingUtils.maskPartially(accessToken) + "], method=[" + method + "], resource=["
                    + LoggingUtils.maskResource(resource) + "]");
        }
        
        WebResource webResource = client.resource(decisionServiceUrl)
                .queryParam(QUERY_PARAM_REALM, xlcRealmName)
                .queryParam(QUERY_PARAM_APPLICATION, IamUtils.REST_SERVICE_NAME)
                .queryParam(QUERY_PARAM_RESOURCE, resource);
        
        ClientResponse clientResponse = webResource.header(ACCEPT_AUTHN_HEADER, AUTHN_SCHEMA)
                    .header(QUERY_PARAMS_HEADER, AUTHZ_SCHEMA + AUTHZ_SCHEMA_SEPARATOR + accessToken)
                    .get(ClientResponse.class);
        
        Decision decision;
        if(clientResponse.getStatus() == 200) {
            decision = getDecision(clientResponse, method);
        } else {
            String responseBody = null;
            try {
                StringWriter writer = new StringWriter();
                IOUtils.copy(clientResponse.getEntityInputStream(), writer, "UTF-8");
                responseBody = writer.toString();
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Failed to validate entitlements, status code: " + clientResponse.getStatus() + ", response: " + responseBody);
                }
            } catch (IOException e) {
                LOG.error(e.getMessage(), e);
            }
            
            decision = new Decision(DecisionAnswer.FAILED, "Received response with status code " + clientResponse.getStatus()
                    + (responseBody != null ? ": " + responseBody : ""));
        }
        
        if (LOG.isDebugEnabled()) {
            LOG.debug("access for token=[" + LoggingUtils.maskPartially(accessToken) + "], method=[" + method + "], resource=["
                    + LoggingUtils.maskResource(resource) + "] is " + decision);
        }
        return decision;
    }
    
    private Decision getDecision(ClientResponse clientResponse, String method) {
        try {
            boolean answer = false;
            Map> attrs = new HashMap<>();
            Reader reader = new InputStreamReader(clientResponse.getEntityInputStream());
            JSONTokener tokener = new JSONTokener(reader);
            JSONObject root = new JSONObject(tokener);
            JSONObject body = root.getJSONObject("body");
            JSONObject actionsValues = body.getJSONObject("actionsValues");
            
            if (!actionsValues.isNull(method)) {
                answer = actionsValues.getBoolean(method);
            }
            
            JSONObject attributes = body.getJSONObject("attributes");
            @SuppressWarnings( "unchecked" )
            Iterator attrIt = attributes.keys();
            while (attrIt.hasNext()) {
                String key = attrIt.next();
                Set values = new HashSet();
                JSONArray vals = (JSONArray) attributes.get(key);
                for (int i = 0; i < vals.length(); i++) {
                    values.add(vals.getString(i));
                }
                attrs.put(key, values);
            }
            
            if (answer) {
                return new Decision(DecisionAnswer.ALLOW);
            }
            
            if (attrs.get(REASON_ATTRIBUTE_KEY) != null && attrs.get(REASON_ATTRIBUTE_KEY).contains(REASON_RESTRICTED_VALUE)) {
                String details = null;
                if (attrs.containsKey(DETAIL_ATTRIBUTE_KEY)) {
                    details = StringUtils.join(attrs.get(DETAIL_ATTRIBUTE_KEY).toArray(),';');
                }
                return new Decision(DecisionAnswer.RESTRICTED,details);
            }
            return new Decision(DecisionAnswer.DENY);
        } catch (JSONException e) {
            return new Decision(DecisionAnswer.FAILED);
        }
    }
    
    /**
     * Gets the value of {@link #decisionServiceUrl}.
     * @return value of {@link #decisionServiceUrl}
     */
    public String getDecisionServiceUrl() {
        return decisionServiceUrl;
    }
    
    /**
     * Sets the value of {@link #decisionServiceUrl}.
     * @param decisionServiceUrl - value
     */
    public void setDecisionServiceUrl(String decisionServiceUrl) {
        this.decisionServiceUrl = decisionServiceUrl;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy