/*
* 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;
}
}