Compare commits

..

No commits in common. "3f24a118c526f511748fe1b43565024001840e6a" and "60051f02eb56a87561ad7920a305b100032c1630" have entirely different histories.

3 changed files with 76 additions and 82 deletions

View File

@ -4,7 +4,6 @@ const substituteVariables = require('./utils/variables.js');
const handleWildcards = require('./utils/wildcards.js').handleWildcards; const handleWildcards = require('./utils/wildcards.js').handleWildcards;
const conditions = require('./utils/conditions.js'); const conditions = require('./utils/conditions.js');
const findConditionKey = conditions.findConditionKey; const findConditionKey = conditions.findConditionKey;
const setupConditionKey = conditions.setupConditionKey;
const convertConditionOperator = conditions.convertConditionOperator; const convertConditionOperator = conditions.convertConditionOperator;
const checkArnMatch = require('./utils/checkArnMatch.js'); const checkArnMatch = require('./utils/checkArnMatch.js');
@ -104,7 +103,6 @@ function meetConditions(requestContext, statementCondition, log) {
// operators as keys // operators as keys
const operators = Object.keys(statementCondition); const operators = Object.keys(statementCondition);
const length = operators.length; const length = operators.length;
const map = setupConditionKey(requestContext);
for (let i = 0; i < length; i ++) { for (let i = 0; i < length; i ++) {
const operator = operators[i]; const operator = operators[i];
const hasIfExistsCondition = operator.endsWith('IfExists'); const hasIfExistsCondition = operator.endsWith('IfExists');
@ -139,7 +137,8 @@ function meetConditions(requestContext, statementCondition, log) {
// condition has "ForAnyValue" or "ForAllValues". // condition has "ForAnyValue" or "ForAllValues".
// (see http://docs.aws.amazon.com/IAM/latest/UserGuide/ // (see http://docs.aws.amazon.com/IAM/latest/UserGuide/
// reference_policies_multi-value-conditions.html) // reference_policies_multi-value-conditions.html)
const keyBasedOnRequestContext = findConditionKey(key, map); const keyBasedOnRequestContext =
findConditionKey(key, requestContext);
// Handle IfExists and negation operators // Handle IfExists and negation operators
if ((keyBasedOnRequestContext === undefined || if ((keyBasedOnRequestContext === undefined ||
keyBasedOnRequestContext === null) && keyBasedOnRequestContext === null) &&

View File

@ -6,28 +6,34 @@ const handleWildcards = require('./wildcards.js').handleWildcards;
const checkArnMatch = require('./checkArnMatch.js'); const checkArnMatch = require('./checkArnMatch.js');
const conditions = {}; const conditions = {};
conditions.setupConditionKey = requestContext => { /**
* findConditionKey finds the value of a condition key based on requestContext
* @param {string} key - condition key name
* @param {RequestContext} requestContext - info sent with request
* @return {string} condition key value
*/
conditions.findConditionKey = (key, requestContext) => {
// TODO: Consider combining with findVariable function if no benefit // TODO: Consider combining with findVariable function if no benefit
// to keeping separate // to keeping separate
const headers = requestContext.getHeaders(); const headers = requestContext.getHeaders();
const query = requestContext.getQuery(); const query = requestContext.getQuery();
const requesterInfo = requestContext.getRequesterInfo(); const requesterInfo = requestContext.getRequesterInfo();
const map = {}; const map = new Map();
// Possible AWS Condition keys (http://docs.aws.amazon.com/IAM/latest/ // Possible AWS Condition keys (http://docs.aws.amazon.com/IAM/latest/
// UserGuide/reference_policies_elements.html#AvailableKeys) // UserGuide/reference_policies_elements.html#AvailableKeys)
// aws:CurrentTime Used for date/time conditions // aws:CurrentTime Used for date/time conditions
// (see Date Condition Operators). // (see Date Condition Operators).
map['aws:CurrentTime'] = new Date().toISOString(); map.set('aws:CurrentTime', new Date().toISOString());
// aws:EpochTime Used for date/time conditions // aws:EpochTime Used for date/time conditions
// (see Date Condition Operators). // (see Date Condition Operators).
map['aws:EpochTime'] = Date.now().toString(); map.set('aws:EpochTime', Date.now().toString());
// aws:TokenIssueTime Date/time that temporary security // aws:TokenIssueTime Date/time that temporary security
// credentials were issued (see Date Condition Operators). // credentials were issued (see Date Condition Operators).
// Only present in requests that are signed using temporary security // Only present in requests that are signed using temporary security
// credentials. // credentials.
map['aws:TokenIssueTime'] = requestContext.getTokenIssueTime(); map.set('aws:TokenIssueTime', requestContext.getTokenIssueTime());
// aws:MultiFactorAuthPresent Used to check whether MFA was used // aws:MultiFactorAuthPresent Used to check whether MFA was used
// (see Boolean Condition Operators). // (see Boolean Condition Operators).
// Note: This key is only present if MFA was used. So, the following // Note: This key is only present if MFA was used. So, the following
@ -37,111 +43,104 @@ conditions.setupConditionKey = requestContext => {
// Instead use: // Instead use:
// "Condition" : // "Condition" :
// { "Null" : { "aws:MultiFactorAuthPresent" : true } } // { "Null" : { "aws:MultiFactorAuthPresent" : true } }
map['aws:MultiFactorAuthPresent'] = map.set('aws:MultiFactorAuthPresent',
requestContext.getMultiFactorAuthPresent(); requestContext.getMultiFactorAuthPresent());
// aws:MultiFactorAuthAge Used to check how many seconds since // aws:MultiFactorAuthAge Used to check how many seconds since
// MFA credentials were issued. If MFA was not used, // MFA credentials were issued. If MFA was not used,
// this key is not present // this key is not present
map['aws:MultiFactorAuthAge'] = requestContext.getMultiFactorAuthAge(); map.set('aws:MultiFactorAuthAge', requestContext.getMultiFactorAuthAge());
// aws:principaltype states whether the principal is an account, // aws:principaltype states whether the principal is an account,
// user, federated, or assumed role // user, federated, or assumed role
// Note: Docs for conditions have "PrincipalType" but simulator // Note: Docs for conditions have "PrincipalType" but simulator
// and docs for variables have lowercase // and docs for variables have lowercase
map['aws:principaltype'] = requesterInfo.principaltype; map.set('aws:principaltype', requesterInfo.principaltype);
// aws:Referer Used to check who referred the client browser to // aws:Referer Used to check who referred the client browser to
// the address the request is being sent to. Only supported by some // the address the request is being sent to. Only supported by some
// services, such as S3. Value comes from the referer header in the // services, such as S3. Value comes from the referer header in the
// HTTPS request made to AWS. // HTTPS request made to AWS.
map['aws:referer'] = headers.referer; map.set('aws:referer', headers.referer);
// aws:SecureTransport Used to check whether the request was sent // aws:SecureTransport Used to check whether the request was sent
// using SSL (see Boolean Condition Operators). // using SSL (see Boolean Condition Operators).
map['aws:SecureTransport'] = map.set('aws:SecureTransport',
requestContext.getSslEnabled() ? 'true' : 'false'; requestContext.getSslEnabled() ? 'true' : 'false');
// aws:SourceArn Used check the source of the request, // aws:SourceArn Used check the source of the request,
// using the ARN of the source. N/A here. // using the ARN of the source. N/A here.
map['aws:SourceArn'] = undefined; map.set('aws:SourceArn', undefined);
// aws:SourceIp Used to check the requester's IP address // aws:SourceIp Used to check the requester's IP address
// (see IP Address Condition Operators) // (see IP Address Condition Operators)
map['aws:SourceIp'] = requestContext.getRequesterIp(); map.set('aws:SourceIp', requestContext.getRequesterIp());
// aws:SourceVpc Used to restrict access to a specific // aws:SourceVpc Used to restrict access to a specific
// AWS Virtual Private Cloud. N/A here. // AWS Virtual Private Cloud. N/A here.
map['aws:SourceVpc'] = undefined; map.set('aws:SourceVpc', undefined);
// aws:SourceVpce Used to limit access to a specific VPC endpoint // aws:SourceVpce Used to limit access to a specific VPC endpoint
// N/A here // N/A here
map['aws:SourceVpce'] = undefined; map.set('aws:SourceVpce', undefined);
// aws:UserAgent Used to check the requester's client app. // aws:UserAgent Used to check the requester's client app.
// (see String Condition Operators) // (see String Condition Operators)
map['aws:UserAgent'] = headers['user-agent']; map.set('aws:UserAgent', headers['user-agent']);
// aws:userid Used to check the requester's unique user ID. // aws:userid Used to check the requester's unique user ID.
// (see String Condition Operators) // (see String Condition Operators)
map['aws:userid'] = requesterInfo.userid; map.set('aws:userid', requesterInfo.userid);
// aws:username Used to check the requester's friendly user name. // aws:username Used to check the requester's friendly user name.
// (see String Condition Operators) // (see String Condition Operators)
map['aws:username'] = requesterInfo.username; map.set('aws:username', requesterInfo.username);
// Possible condition keys for S3: // Possible condition keys for S3:
// s3:x-amz-acl is acl request for bucket or object put request // s3:x-amz-acl is acl request for bucket or object put request
map['s3:x-amz-acl'] = headers['x-amz-acl']; map.set('s3:x-amz-acl', headers['x-amz-acl']);
// s3:x-amz-grant-PERMISSION (where permission can be: // s3:x-amz-grant-PERMISSION (where permission can be:
// read, write, read-acp, write-acp or full-control) // read, write, read-acp, write-acp or full-control)
// Value is the value of that header (ex. id of grantee) // Value is the value of that header (ex. id of grantee)
map['s3:x-amz-grant-read'] = headers['x-amz-grant-read']; map.set('s3:x-amz-grant-read', headers['x-amz-grant-read']);
map['s3:x-amz-grant-write'] = headers['x-amz-grant-write']; map.set('s3:x-amz-grant-write', headers['x-amz-grant-write']);
map['s3:x-amz-grant-read-acp'] = headers['x-amz-grant-read-acp']; map.set('s3:x-amz-grant-read-acp', headers['x-amz-grant-read-acp']);
map['s3:x-amz-grant-write-acp'] = headers['x-amz-grant-write-acp']; map.set('s3:x-amz-grant-write-acp', headers['x-amz-grant-write-acp']);
map['s3:x-amz-grant-full-control'] = headers['x-amz-grant-full-control']; map.set('s3:x-amz-grant-full-control', headers['x-amz-grant-full-control']);
// s3:x-amz-copy-source is x-amz-copy-source header if applicable on // s3:x-amz-copy-source is x-amz-copy-source header if applicable on
// a put object // a put object
map['s3:x-amz-copy-source'] = headers['x-amz-copy-source']; map.set('s3:x-amz-copy-source', headers['x-amz-copy-source']);
// s3:x-amz-metadata-directive is x-amz-metadata-directive header if // s3:x-amz-metadata-directive is x-amz-metadata-directive header if
// applicable on a put object copy. Determines whether metadata will // applicable on a put object copy. Determines whether metadata will
// be copied from original object or replaced. Values or "COPY" or // be copied from original object or replaced. Values or "COPY" or
// "REPLACE". Default is "COPY" // "REPLACE". Default is "COPY"
map['s3:x-amz-metadata-directive'] = headers['metadata-directive']; map.set('s3:x-amz-metadata-directive', headers['metadata-directive']);
// s3:x-amz-server-side-encryption -- Used to require that object put // s3:x-amz-server-side-encryption -- Used to require that object put
// use server side encryption. Value is the encryption algo such as // use server side encryption. Value is the encryption algo such as
// "AES256" // "AES256"
map['s3:x-amz-server-side-encryption'] = map.set('s3:x-amz-server-side-encryption',
headers['x-amz-server-side-encryption']; headers['x-amz-server-side-encryption']);
// s3:x-amz-storage-class -- x-amz-storage-class header value // s3:x-amz-storage-class -- x-amz-storage-class header value
// (STANDARD, etc.) // (STANDARD, etc.)
map['s3:x-amz-storage-class'] = headers['x-amz-storage-class']; map.set('s3:x-amz-storage-class', headers['x-amz-storage-class']);
// s3:VersionId -- version id of object // s3:VersionId -- version id of object
map['s3:VersionId'] = headers['x-amz-version-id']; map.set('s3:VersionId', headers['x-amz-version-id']);
// s3:LocationConstraint -- Used to restrict creation of bucket // s3:LocationConstraint -- Used to restrict creation of bucket
// in certain region. Only applicable for CreateBucket // in certain region. Only applicable for CreateBucket
map['s3:LocationConstraint'] = requestContext.getLocationConstraint(); map.set('s3:LocationConstraint', requestContext.getLocationConstraint());
// s3:delimiter is delimiter for listing request // s3:delimiter is delimiter for listing request
map['s3:delimiter'] = query.delimiter; map.set('s3:delimiter', query.delimiter);
// s3:max-keys is max-keys for listing request // s3:max-keys is max-keys for listing request
map['s3:max-keys'] = query['max-keys']; map.set('s3:max-keys', query['max-keys']);
// s3:prefix is prefix for listing request // s3:prefix is prefix for listing request
map['s3:prefix'] = query.prefix; map.set('s3:prefix', query.prefix);
// s3 auth v4 additional condition keys // s3 auth v4 additional condition keys
// (See http://docs.aws.amazon.com/AmazonS3/latest/API/ // (See http://docs.aws.amazon.com/AmazonS3/latest/API/
// bucket-policy-s3-sigv4-conditions.html) // bucket-policy-s3-sigv4-conditions.html)
// s3:signatureversion -- Either "AWS" for v2 or // s3:signatureversion -- Either "AWS" for v2 or
// "AWS4-HMAC-SHA256" for v4 // "AWS4-HMAC-SHA256" for v4
map['s3:signatureversion'] = requestContext.getSignatureVersion(); map.set('s3:signatureversion', requestContext.getSignatureVersion());
// s3:authType -- Method of authentication: either "REST-HEADER", // s3:authType -- Method of authentication: either "REST-HEADER",
// "REST-QUERY-STRING" or "POST" // "REST-QUERY-STRING" or "POST"
map['s3:authType'] = requestContext.getAuthType(); map.set('s3:authType', requestContext.getAuthType());
// s3:signatureAge is the length of time, in milliseconds, // s3:signatureAge is the length of time, in milliseconds,
// that a signature is valid in an authenticated request. So, // that a signature is valid in an authenticated request. So,
// can use this to limit the age to less than 7 days // can use this to limit the age to less than 7 days
map['s3:signatureAge'] = requestContext.getSignatureAge(); map.set('s3:signatureAge', requestContext.getSignatureAge());
// s3:x-amz-content-sha256 - Valid value is "UNSIGNED-PAYLOAD" // s3:x-amz-content-sha256 - Valid value is "UNSIGNED-PAYLOAD"
// so can use this in a deny policy to deny any requests that do not // so can use this in a deny policy to deny any requests that do not
// have a signed payload // have a signed payload
map['s3:x-amz-content-sha256'] = headers['x-amz-content-sha256']; map.set('s3:x-amz-content-sha256', headers['x-amz-content-sha256']);
return map; return map.get(key);
}; };
/**
* findConditionKey finds the value of a condition key based on requestContext
* @param {string} key - condition key name
* @param {map} map - map object
* @return {string} condition key value
*/
conditions.findConditionKey = (key, map) => map[key];
// Wildcards are allowed in certain string comparison and arn comparisons // Wildcards are allowed in certain string comparison and arn comparisons

View File

@ -8,62 +8,59 @@
// the string operators (StringEquals, StringLike, StringNotLike, etc.) // the string operators (StringEquals, StringLike, StringNotLike, etc.)
// or the ARN operators (ArnEquals, ArnLike, etc.). // or the ARN operators (ArnEquals, ArnLike, etc.).
function setupVariable(requestContext) { /**
const map = {}; * findVariable finds the value of a variable based on the requestContext
* @param {string} variable - variable name
* @param {RequestContext} requestContext - info sent with request
* @return {string} variable value
*/
function findVariable(variable, requestContext) {
// See http://docs.aws.amazon.com/IAM/latest/UserGuide/
// reference_policies_variables.html
const headers = requestContext.getHeaders(); const headers = requestContext.getHeaders();
const query = requestContext.getQuery(); const query = requestContext.getQuery();
// HACK!! - this should be fixed properly const requesterInfo = requestContext.getRequesterInfo();
const requesterInfo = requestContext.getRequesterInfo() || {};
const map = new Map();
// aws:CurrentTime can be used for conditions // aws:CurrentTime can be used for conditions
// that check the date and time. // that check the date and time.
map['aws:CurrentTime'] = new Date().toISOString(); map.set('aws:CurrentTime', new Date().toISOString());
// aws:EpochTime for use with date/time conditions // aws:EpochTime for use with date/time conditions
map['aws:EpochTime'] = Date.now(); map.set('aws:EpochTime', Date.now());
// aws:TokenIssueTime is date and time that temp security credentials // aws:TokenIssueTime is date and time that temp security credentials
// were issued. can be used with date/time conditions. // were issued. can be used with date/time conditions.
// this key is only available in requests that are signed using // this key is only available in requests that are signed using
// temporary security credentials. // temporary security credentials.
map['aws:TokenIssueTime'] = requestContext.getTokenIssueTime(); map.set('aws:TokenIssueTime', requestContext.getTokenIssueTime());
// aws:principaltype states whether the principal is an account, // aws:principaltype states whether the principal is an account,
// user, federated, or assumed role // user, federated, or assumed role
map['aws:principaltype'] = requesterInfo.principaltype; map.set('aws:principaltype', requesterInfo.principaltype);
// aws:SecureTransport is boolean value that represents whether the // aws:SecureTransport is boolean value that represents whether the
// request was sent using SSL // request was sent using SSL
map['aws:SecureTransport'] = map.set('aws:SecureTransport',
requestContext.getSslEnabled() ? 'true' : 'false'; requestContext.getSslEnabled() ? 'true' : 'false');
// aws:SourceIp is requester's IP address, for use with IP address // aws:SourceIp is requester's IP address, for use with IP address
// conditions // conditions
map['aws:SourceIp'] = requestContext.getRequesterIp(); map.set('aws:SourceIp', requestContext.getRequesterIp());
// aws:UserAgent is information about the requester's client application // aws:UserAgent is information about the requester's client application
map['aws:UserAgent'] = headers['user-agent']; map.set('aws:UserAgent', headers['user-agent']);
// aws:userid is unique ID for the current user // aws:userid is unique ID for the current user
map['aws:userid'] = requesterInfo.userid; map.set('aws:userid', requesterInfo.userid);
// aws:username is friendly name of the current user // aws:username is friendly name of the current user
map['aws:username'] = requesterInfo.username; map.set('aws:username', requesterInfo.username);
// ec2:SourceInstanceARN is the Amazon EC2 instance from which the // ec2:SourceInstanceARN is the Amazon EC2 instance from which the
// request was made. Present only when the request comes from an Amazon // request was made. Present only when the request comes from an Amazon
// EC2 instance using an IAM role associated with an EC2 // EC2 instance using an IAM role associated with an EC2
// instance profile. N/A here. // instance profile. N/A here.
map['ec2:SourceInstanceARN'] = undefined; map.set('ec2:SourceInstanceARN', undefined);
// s3 - specific: // s3 - specific:
// s3:prefix is prefix for listing request // s3:prefix is prefix for listing request
map['s3:prefix'] = query.prefix; map.set('s3:prefix', query.prefix);
// s3:max-keys is max-keys for listing request // s3:max-keys is max-keys for listing request
map['s3:max-keys'] = query['max-keys']; map.set('s3:max-keys', query['max-keys']);
// s3:x-amz-acl is acl request for bucket or object put request // s3:x-amz-acl is acl request for bucket or object put request
map['s3:x-amz-acl'] = query['x-amz-acl']; map.set('s3:x-amz-acl', query['x-amz-acl']);
return map; return map.get(variable);
}
/**
* findVariable finds the value of a variable based on the requestContext
* @param {string} variable - variable name
* @param {object} map - map object
* @return {string} variable value
*/
function findVariable(variable, map) {
// See http://docs.aws.amazon.com/IAM/latest/UserGuide/
// reference_policies_variables.html
return map[variable];
} }
/** /**
@ -76,7 +73,6 @@ function findVariable(variable, map) {
function substituteVariables(string, requestContext) { function substituteVariables(string, requestContext) {
const arr = string.split(''); const arr = string.split('');
let startOfVariable = arr.indexOf('$'); let startOfVariable = arr.indexOf('$');
const map = setupVariable(requestContext);
while (startOfVariable > -1) { while (startOfVariable > -1) {
if (arr[startOfVariable + 1] !== '{') { if (arr[startOfVariable + 1] !== '{') {
startOfVariable = arr.indexOf('$', startOfVariable + 1); startOfVariable = arr.indexOf('$', startOfVariable + 1);
@ -93,7 +89,7 @@ function substituteVariables(string, requestContext) {
// undefined, leave the original string '${whatever}'. // undefined, leave the original string '${whatever}'.
// This also means that ${*}, ${?} and ${$} will remain as they are // This also means that ${*}, ${?} and ${$} will remain as they are
// here and will be converted as part of the wildcard transformation // here and will be converted as part of the wildcard transformation
const value = findVariable(variableContent, map); const value = findVariable(variableContent, requestContext);
// Length of item being replaced is the variable content plus ${} // Length of item being replaced is the variable content plus ${}
let replacingLength = variableContent.length + 3; let replacingLength = variableContent.length + 3;
if (value !== undefined) { if (value !== undefined) {