Compare commits
1 Commits
developmen
...
dev/ft/use
Author | SHA1 | Date |
---|---|---|
Rahul Padigela | 2c2626132d |
|
@ -293,13 +293,13 @@
|
||||||
"description": "The request signature we calculated does not match the signature you provided."
|
"description": "The request signature we calculated does not match the signature you provided."
|
||||||
},
|
},
|
||||||
"_comment" : {
|
"_comment" : {
|
||||||
"note" : "This is an AWS S3 specific error. We are opting to use the more general 'ServiceUnavailable' error used throughout AWS (IAM/EC2) to have uniformity of error messages even though we are potentially compromising S3 compatibility.",
|
"note" : "This is an AWS S3 specific error. We are opting to use the more general 'ServiceUnavailable' error used throughout AWS (IAM/EC2) to have uniformity of error messages even though we are potentially compromising S3 compatibility.",
|
||||||
"ServiceUnavailable": {
|
"ServiceUnavailable": {
|
||||||
"code": 503,
|
"code": 503,
|
||||||
"description": "Reduce your request rate."
|
"description": "Reduce your request rate."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ServiceUnavailable": {
|
"ServiceUnavailable": {
|
||||||
"code": 503,
|
"code": 503,
|
||||||
"description": "The request has failed due to a temporary failure of the server."
|
"description": "The request has failed due to a temporary failure of the server."
|
||||||
},
|
},
|
||||||
|
@ -550,7 +550,7 @@
|
||||||
"SecretKeyDoesNotExist": {
|
"SecretKeyDoesNotExist": {
|
||||||
"description": "secret key does not exist",
|
"description": "secret key does not exist",
|
||||||
"code": 5030
|
"code": 5030
|
||||||
},
|
},
|
||||||
"InvalidRegion": {
|
"InvalidRegion": {
|
||||||
"description": "Region was not provided or is not recognized by the system",
|
"description": "Region was not provided or is not recognized by the system",
|
||||||
"code": 5031
|
"code": 5031
|
||||||
|
@ -583,15 +583,15 @@
|
||||||
"BadUrl": {
|
"BadUrl": {
|
||||||
"description": "url not ok",
|
"description": "url not ok",
|
||||||
"code": 5038
|
"code": 5038
|
||||||
},
|
},
|
||||||
"BadClientIdList": {
|
"BadClientIdList": {
|
||||||
"description": "client id list not ok'",
|
"description": "client id list not ok'",
|
||||||
"code": 5039
|
"code": 5039
|
||||||
},
|
},
|
||||||
"BadThumbprintList": {
|
"BadThumbprintList": {
|
||||||
"description": "thumbprint list not ok'",
|
"description": "thumbprint list not ok'",
|
||||||
"code": 5040
|
"code": 5040
|
||||||
},
|
},
|
||||||
"BadObject": {
|
"BadObject": {
|
||||||
"description": "Object not ok'",
|
"description": "Object not ok'",
|
||||||
"code": 5041
|
"code": 5041
|
||||||
|
@ -600,12 +600,12 @@
|
||||||
"BadRole": {
|
"BadRole": {
|
||||||
"description": "role not ok",
|
"description": "role not ok",
|
||||||
"code": 5042
|
"code": 5042
|
||||||
},
|
},
|
||||||
"_comment": "#### SamlpErrors ####",
|
"_comment": "#### SamlpErrors ####",
|
||||||
"BadSamlp": {
|
"BadSamlp": {
|
||||||
"description": "samlp not ok",
|
"description": "samlp not ok",
|
||||||
"code": 5043
|
"code": 5043
|
||||||
},
|
},
|
||||||
"BadMetadataDocument": {
|
"BadMetadataDocument": {
|
||||||
"description": "metadata document not ok",
|
"description": "metadata document not ok",
|
||||||
"code": 5044
|
"code": 5044
|
||||||
|
@ -671,5 +671,46 @@
|
||||||
"NotEnoughMapsInConfig:": {
|
"NotEnoughMapsInConfig:": {
|
||||||
"description": "NotEnoughMapsInConfig",
|
"description": "NotEnoughMapsInConfig",
|
||||||
"code": 400
|
"code": 400
|
||||||
}
|
},
|
||||||
|
"_comment": "--------------------- Policies ---------------------",
|
||||||
|
"InvalidPolicyJSON": {
|
||||||
|
"description": "Policy contains JSON errors",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"InvalidPolicyVersion": {
|
||||||
|
"description": "Version field must be a valid string",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"InvalidPolicyEffect": {
|
||||||
|
"description": "Invalid value for Effect",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"InvalidPolicyAction": {
|
||||||
|
"description": "Invalid service prefix for Action",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"InvalidPolicyResource": {
|
||||||
|
"description": "Invalid value for Resource",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"MissingPolicyVersion": {
|
||||||
|
"description": "Missing required field Version",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"MissingPolicyStatement": {
|
||||||
|
"description": "Missing required field Statement",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"MissingPolicyAction": {
|
||||||
|
"description": "Missing required field Action",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"MissingPolicyEffect": {
|
||||||
|
"description": "Missing required field Effect",
|
||||||
|
"code": 400
|
||||||
|
},
|
||||||
|
"MissingPolicyResource": {
|
||||||
|
"description": "Missing required field Resource",
|
||||||
|
"code": 400
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
'use strict'; // eslint-disable-line strict
|
||||||
|
|
||||||
|
const Ajv = require('ajv');
|
||||||
|
const userPolicySchema = require('./userPolicySchema');
|
||||||
|
const errors = require('../errors');
|
||||||
|
|
||||||
|
const ajValidate = new Ajv({ allErrors: true });
|
||||||
|
// compiles schema to functions and caches them for all cases
|
||||||
|
const userPolicyValidate = ajValidate.compile(userPolicySchema);
|
||||||
|
|
||||||
|
// parse ajv errors and build list of erros
|
||||||
|
function _parseErrors(ajvErrors) {
|
||||||
|
let parsedErr;
|
||||||
|
ajvErrors.some(err => {
|
||||||
|
const resource = err.dataPath.replace('.', '');
|
||||||
|
if (err.keyword === 'required' && err.params) {
|
||||||
|
const field = err.params.missingProperty;
|
||||||
|
if (field === 'Version') {
|
||||||
|
parsedErr = errors.MissingPolicyVersion;
|
||||||
|
} else if (field === 'Statement') {
|
||||||
|
parsedErr = errors.MissingPolicyStatement;
|
||||||
|
} else if (field === 'Action') {
|
||||||
|
parsedErr = errors.MissingPolicyAction;
|
||||||
|
} else if (field === 'Effect') {
|
||||||
|
parsedErr = errors.MissingPolicyEffect;
|
||||||
|
} else if (field === 'Resource') {
|
||||||
|
parsedErr = errors.MissingPolicyResource;
|
||||||
|
} else {
|
||||||
|
parsedErr = errors.InvalidPolicyDocument;
|
||||||
|
}
|
||||||
|
} else if (err.keyword === 'minItems' && resource === 'Statement') {
|
||||||
|
parsedErr = errors.InvalidPolicyStatement;
|
||||||
|
} else if (err.keyword === 'pattern') {
|
||||||
|
parsedErr = errors.InvalidPolicyDocument;
|
||||||
|
} else if (err.keyword === 'type') {
|
||||||
|
// skip if it's Statement as it does not have enough
|
||||||
|
// error context
|
||||||
|
if (resource === 'Version') {
|
||||||
|
parsedErr = errors.PolicyInvalidVersion;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsedErr = errors.InvalidPolicyDocument;
|
||||||
|
}
|
||||||
|
return parsedErr instanceof Error;
|
||||||
|
});
|
||||||
|
return parsedErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// parse JSON safely without throwing an exception
|
||||||
|
function _safeJSONParse(s) {
|
||||||
|
let res;
|
||||||
|
try {
|
||||||
|
res = JSON.parse(s);
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// validates policy using the validation schema
|
||||||
|
function _validatePolicy(type, policy) {
|
||||||
|
if (type === 'user') {
|
||||||
|
const parseRes = _safeJSONParse(policy);
|
||||||
|
if (parseRes instanceof Error) {
|
||||||
|
return { error: errors.PolicyInvalidJSON, valid: false };
|
||||||
|
}
|
||||||
|
userPolicyValidate(parseRes);
|
||||||
|
if (userPolicyValidate.errors) {
|
||||||
|
return { error: _parseErrors(userPolicyValidate.errors),
|
||||||
|
valid: false };
|
||||||
|
}
|
||||||
|
return { error: null, valid: true };
|
||||||
|
}
|
||||||
|
// todo: add support for resource policies
|
||||||
|
return { error: errors.NotImplemented, valid: false };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @typedef ValidationResult
|
||||||
|
* @type Object
|
||||||
|
* @property {Array|null} error - list of validation errors or null
|
||||||
|
* @property {Bool} valid - true/false depending on the validation result
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Validates user policy
|
||||||
|
* @param {String} policy - policy json
|
||||||
|
* @returns {Object} - returns object with properties error and value
|
||||||
|
* @returns {ValidationResult} - result of the validation
|
||||||
|
*/
|
||||||
|
function validateUserPolicy(policy) {
|
||||||
|
return _validatePolicy('user', policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates resource policy
|
||||||
|
* @param {String} policy - policy json
|
||||||
|
* @returns {Object} - returns object with properties error and value
|
||||||
|
* @returns {ValidationResult} - result of the validation
|
||||||
|
*/
|
||||||
|
function validateResourcePolicy(policy) {
|
||||||
|
return _validatePolicy('resource', policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validateUserPolicy,
|
||||||
|
validateResourcePolicy,
|
||||||
|
};
|
|
@ -0,0 +1,401 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AWS Policy schema.",
|
||||||
|
"description": "This schema describes user policy per AWS policy grammar rules",
|
||||||
|
"definitions": {
|
||||||
|
"actionItem": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[^*:]+:([^:])+|^\\*{1}$"
|
||||||
|
},
|
||||||
|
"resourceItem": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^\\*|arn:aws(:(\\*{1}|[a-z0-9\\*\\-]{2,})*){2}:.*$"
|
||||||
|
},
|
||||||
|
"conditions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"StringEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringEqualsIgnoreCase": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotEqualsIgnoreCase": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringLike": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotLike": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericNotEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericLessThan": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericLessThanEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericGreaterThan": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericGreaterThanEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateNotEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateLessThan": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateLessThanEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateGreaterThan": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateGreaterThanEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Bool": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"BinaryEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"IpAddress": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NotIpAddress": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnNotEquals": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnLike": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnNotLike": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Null": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringEqualsIgnoreCaseIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotEqualsIgnoreCaseIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringLikeIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"StringNotLikeIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericNotEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericLessThanIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericLessThanEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericGreaterThanIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NumericGreaterThanEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateNotEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateLessThanIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateLessThanEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateGreaterThanIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DateGreaterThanEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"BoolIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"BinaryEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"IpAddressIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NotIpAddressIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnNotEqualsIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnLikeIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ArnNotLikeIfExists": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"Version": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"2012-10-17"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Statement": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": [
|
||||||
|
"array"
|
||||||
|
],
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Sid": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "[a-zA-Z0-9]+"
|
||||||
|
},
|
||||||
|
"Effect": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Allow",
|
||||||
|
"Deny"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Action": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NotAction": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Resource": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NotResource": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"$ref": "#/definitions/conditions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"Action",
|
||||||
|
"Resource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"Action",
|
||||||
|
"NotResource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"NotAction",
|
||||||
|
"Resource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"NotAction",
|
||||||
|
"NotResource"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": [
|
||||||
|
"object"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Sid": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "[a-zA-Z0-9]+"
|
||||||
|
},
|
||||||
|
"Effect": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Allow",
|
||||||
|
"Deny"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Action": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NotAction": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/actionItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Resource": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NotResource": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/resourceItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"$ref": "#/definitions/conditions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"Action",
|
||||||
|
"Effect",
|
||||||
|
"Resource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Action",
|
||||||
|
"Effect",
|
||||||
|
"NotResource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"NotAction",
|
||||||
|
"Resource"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"required": [
|
||||||
|
"Effect",
|
||||||
|
"NotAction",
|
||||||
|
"NotResource"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Version",
|
||||||
|
"Statement"
|
||||||
|
]
|
||||||
|
}
|
|
@ -10,10 +10,11 @@
|
||||||
"author": "Giorgio Regni",
|
"author": "Giorgio Regni",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/scality/Arsenal/issues"
|
"url": "https://github.com/scality/Arsenal/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/scality/Arsenal#readme",
|
"homepage": "https://github.com/scality/Arsenal#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ajv": "^4.1.3",
|
||||||
"utf8": "~2.1.1"
|
"utf8": "~2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
'use strict'; // eslint-disable-line strict
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const policyValidator = require('../../../lib/policy/policyValidator');
|
||||||
|
const errors = require('../../../lib/errors');
|
||||||
|
const validateUserPolicy = policyValidator.validateUserPolicy;
|
||||||
|
const successRes = { error: null, valid: true };
|
||||||
|
const samplePolicy = {
|
||||||
|
Version: '2012-10-17',
|
||||||
|
Statement: {
|
||||||
|
Sid: 'FooBar1234',
|
||||||
|
Effect: 'Allow',
|
||||||
|
Action: 's3:PutObject',
|
||||||
|
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||||
|
Condition: { NumericLessThanEquals: { 's3:max-keys': '10' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let policy;
|
||||||
|
|
||||||
|
function failRes(error) {
|
||||||
|
return { error, valid: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(input, expected) {
|
||||||
|
const result = validateUserPolicy(JSON.stringify(input));
|
||||||
|
assert.deepStrictEqual(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
policy = JSON.parse(JSON.stringify(samplePolicy));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Invalid JSON', () => {
|
||||||
|
it('should return error for invalid JSON', () => {
|
||||||
|
const result = validateUserPolicy('{"Version":"2012-10-17",' +
|
||||||
|
'"Statement":{"Effect":"Allow""Action":"s3:PutObject",' +
|
||||||
|
'"Resource":"arn:aws:s3*"}}');
|
||||||
|
assert.deepStrictEqual(result, failRes(errors.PolicyInvalidJSON));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Version', () => {
|
||||||
|
it('should validate with version date 2012-10-17', () => {
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error for other dates', () => {
|
||||||
|
policy.Version = '2012-11-17';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error if Version field is missing', () => {
|
||||||
|
policy.Version = undefined;
|
||||||
|
check(policy, failRes(errors.MissingPolicyVersion));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement', () => {
|
||||||
|
it('should succeed for a valid object', () => {
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for a valid array', () => {
|
||||||
|
policy.Statement = [
|
||||||
|
{
|
||||||
|
Effect: 'Allow',
|
||||||
|
Action: 's3:PutObject',
|
||||||
|
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Effect: 'Deny',
|
||||||
|
Action: 's3:DeleteObject',
|
||||||
|
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for undefined', () => {
|
||||||
|
policy.Statement = undefined;
|
||||||
|
check(policy, failRes(errors.MissingPolicyStatement));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for an empty list', () => {
|
||||||
|
policy.Statement = [];
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for an empty object', () => {
|
||||||
|
policy.Statement = {};
|
||||||
|
check(policy, failRes(errors.MissingPolicyAction));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for missing a required field - Action', () => {
|
||||||
|
delete policy.Statement.Action;
|
||||||
|
check(policy, failRes(errors.MissingPolicyAction));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for missing a required field - Effect', () => {
|
||||||
|
delete policy.Statement.Effect;
|
||||||
|
check(policy, failRes(errors.MissingPolicyEffect));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for missing a required field - Resource', () => {
|
||||||
|
delete policy.Statement.Resource;
|
||||||
|
check(policy, failRes(errors.MissingPolicyResource));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error for missing multiple required fields', () => {
|
||||||
|
delete policy.Statement.Effect;
|
||||||
|
delete policy.Statement.Resource;
|
||||||
|
check(policy, failRes(errors.MissingPolicyEffect));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed with optional fields missing - Sid, Condition', () => {
|
||||||
|
delete policy.Statement.Sid;
|
||||||
|
delete policy.Statement.Condition;
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement::Sid_block', () => {
|
||||||
|
it('should succeed if Sid is any alphanumeric string', () => {
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if Sid is not a string', () => {
|
||||||
|
policy.Statement.Sid = 1234;
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement::Effect_block', () => {
|
||||||
|
it('should succeed for Allow', () => {
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for Deny', () => {
|
||||||
|
policy.Statement.Effect = 'Deny';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for strings other than Allow/Deny', () => {
|
||||||
|
policy.Statement.Effect = 'Reject';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if Effect is not a string', () => {
|
||||||
|
policy.Statement.Effect = 1;
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement::Action_block', () => {
|
||||||
|
it('should succeed for foo:bar', () => {
|
||||||
|
policy.Statement.Action = 'foo:bar';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for foo:*', () => {
|
||||||
|
policy.Statement.Action = 'foo:*';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for *', () => {
|
||||||
|
policy.Statement.Action = '*';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for **', () => {
|
||||||
|
policy.Statement.Action = '**';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for foobar', () => {
|
||||||
|
policy.Statement.Action = 'foobar';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement::Resource_block', () => {
|
||||||
|
it('should succeed for arn:aws:s3:::*', () => {
|
||||||
|
policy.Statement.Resource = 'arn:aws:s3:::*';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for arn:aws:s3:::test/home/${aws:username}', () => {
|
||||||
|
policy.Statement.Resource = 'arn:aws:s3:::test/home/${aws:username}';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for arn:aws:ec2:us-west-1:1234qwerty:volume/*', () => {
|
||||||
|
policy.Statement.Resource = 'arn:aws:ec2:us-west-1:1234qwerty:volume/*';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for *', () => {
|
||||||
|
policy.Statement.Resource = '*';
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for ec2:us-west-1:1234qwerty:volume/*', () => {
|
||||||
|
policy.Statement.Resource = 'ec2:us-west-1:1234qwerty:volume/*';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Policies validation - Statement::Condition_block', () => {
|
||||||
|
it('should succeed for single Condition', () => {
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should succeed for multiple Conditions', () => {
|
||||||
|
policy.Statement.Condition = {
|
||||||
|
StringNotLike: { 's3:prefix': ['Development/*'] },
|
||||||
|
Null: { 's3:prefix': false },
|
||||||
|
};
|
||||||
|
check(policy, successRes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when Condition is not an Object', () => {
|
||||||
|
policy.Statement.Condition = 'NumericLessThanEquals';
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for an invalid Condition', () => {
|
||||||
|
policy.Statement.Condition = {
|
||||||
|
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||||
|
};
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail when one of the multiple conditions is invalid', () => {
|
||||||
|
policy.Statement.Condition = {
|
||||||
|
Null: { 's3:prefix': false },
|
||||||
|
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||||
|
};
|
||||||
|
check(policy, failRes(errors.InvalidPolicyDocument));
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue