/* * Copyright 2011 Cloud.com, Inc. * * 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 com.cloud.bridge.service.core.s3; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions; public class S3BucketPolicy { /** * 'NORESULT' is returned when no applicable statement can be found to evaluate * for the S3 access request. If no evaluated statement results to true then the * default deny result is returned (allow ACL definitions to override it). */ public enum PolicyAccess { ALLOW, DEFAULT_DENY, DENY } private List<S3PolicyStatement> statementList = new ArrayList<S3PolicyStatement>(); private String bucketName = null; private String id = null; public S3BucketPolicy() { } public S3PolicyStatement[] getStatements() { return statementList.toArray(new S3PolicyStatement[0]); } public void addStatement(S3PolicyStatement param) { statementList.add( param ); } public String getBucketName() { return bucketName; } public void setBucketName(String param) { bucketName = param; } public String getId() { return id; } public void setId(String param) { id = param; } /** * This function evaluates all applicable policy statements. Following the "evaluation logic" * as defined by Amazon the type of access derived from the policy is returned. * * @param context - parameters from either the REST or SOAP request * @param objectToAccess - key to the S3 object in the bucket associated by this policy, should be * null if access is just to the bucket. * @param userAccount - the user performing the access request * @return PolicyAccess type * @throws Exception */ public PolicyAccess eval(S3PolicyContext context, String userAccount) throws Exception { PolicyAccess result = PolicyAccess.DEFAULT_DENY; Iterator<S3PolicyStatement> itr = statementList.iterator(); while( itr.hasNext()) { S3PolicyStatement oneStatement = itr.next(); if (statementIsRelevant( oneStatement, context.getKeyName(), userAccount, context.getRequestedAction())) { // -> a missing condition block means the statement is true S3PolicyConditionBlock block = oneStatement.getConditionBlock(); if (null == block || block.isTrue( context, oneStatement.getSid())) { result = oneStatement.getEffect(); if (PolicyAccess.DENY == result) return result; } } } return result; } /** * To support debugging we print out what the parsing process has resulted in. */ public String toString() { StringBuffer value = new StringBuffer(); Iterator<S3PolicyStatement> itr = statementList.iterator(); value.append( "Bucket Policy for: " + bucketName + " \n" ); if (null != id) value.append( "Id: " + id + "\n" ); while( itr.hasNext()) { S3PolicyStatement oneStatement = itr.next(); value.append( oneStatement.toString()); value.append( "\n" ); } return value.toString(); } /** * Does the Policy Statement have anything to do with the requested access by the user? * * @return true - statement is relevant, false it is not */ private boolean statementIsRelevant( S3PolicyStatement oneStatement, String objectToAccess, String userAccount, PolicyActions operationRequested ) { String path = null; // [A] Is the userAccount one of the principals of the policy statement? S3PolicyPrincipal principals = oneStatement.getPrincipals(); if (null == principals || !principals.contains( userAccount )) return false; //System.out.println( "Statement: " + oneStatement.getSid() + " principal matches"); // [B] Is the operationRequested included in the policy statement? // -> if the value in "NotAction:" matches that requested then the statement does not apply // (i.e., "refers to all actions other" than defined). PolicyActions notActions = oneStatement.getNotAction(); //System.out.println( "Statement: NotAction:" + notActions + " op requested: " + operationRequested ); if ( PolicyActions.UnknownAction != notActions ) { if (notActions == operationRequested) return false; } else { S3PolicyAction actions = oneStatement.getActions(); if (null == actions || !actions.contains( operationRequested )) return false; //System.out.println( "Statement: " + oneStatement.getSid() + " action matches"); } // [C] Does the objectToAccess included in the resource of the policy statement? // -> is it just the bucket being accessed? if ( null == objectToAccess ) path = bucketName; else path = new String( bucketName + "/" + objectToAccess ); if (!oneStatement.containsResource( path )) return false; //System.out.println( "Statement: " + oneStatement.getSid() + " is relevant to access request"); return true; } }