Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

allowing custom groups to upload videos #318

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ Parameters:
Description: ProjectName
AllowedPattern: "[a-zA-Z][a-zA-Z0-9-_]*"
Default: DefaultName
<% if (props.permissions && props.permissions.permissionSchema.includes('any')) { %>
<% if (props.permissions &&( props.permissions.permissionSchema.includes('any') || props.permissions.selectedGroups)) { %>
pPolicyName:
Type: String
Description: Policy name for allowing uploads from all auth users
Default: S3UploadPolicy\
<% } -%>
<% if (props.permissions && props.permissions.selectedGroups) { %>
UserPoolId:
Type: String
Description: UserPool id to grant permission to groups
Default: NONE
<% } -%>
authRoleName:
Type: String
Expand All @@ -30,13 +36,23 @@ Resources:
AllowedOrigins: ['*']
ExposedHeaders: ['x-amz-server-side-encryption', 'x-amz-request-id', 'x-amz-id-2', 'ETag']
MaxAge: '3000'
<% if (props.permissions && props.permissions.permissionSchema.includes('any')) { %>
<% if (props.permissions && (props.permissions.permissionSchema.includes('any') || props.permissions.selectedGroups)) { %>
UploadPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Ref pPolicyName
Roles:
<% if (props.permissions && props.permissions.permissionSchema.includes('any')) { %>
- !Ref authRoleName
<% } -%>
<% if (props.permissions && props.permissions.selectedGroups) { %>
<% props.permissions.selectedGroups.forEach(group => { -%>
- !Join
- ''
- - !Ref UserPoolId
- '-<%= group %>GroupRole'
<% }) -%>
<% } -%>
PolicyDocument:
Version: '2012-10-17'
Statement:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ Parameters:
Type: String
Description: API Endpoint URL
Default: NONE
<% } -%>
<% if (props.parameters && props.parameters.UserPoolId) { -%>
UserPoolId:
Type: String
Description: UserPool Id
Default: NONE
<% } -%>
s3UUID:
Type: String
Expand Down Expand Up @@ -107,9 +113,12 @@ Resources:
TemplateURL: !Sub "https://s3.amazonaws.com/${pS3}/${pSourceFolder}/S3InputBucket.template"
Parameters:
authRoleName: !Ref authRoleName
<% if (props.permissions && props.permissions.permissionSchema.includes('any')) { %>
<% if (props.permissions && (props.permissions.permissionSchema.includes('any') || props.permissions.selectedGroups)) { %>
pPolicyName: !Sub "${AWS::StackName}-s3UploadPermissions"
<% } %>
<% if (props.permissions && props.permissions.selectedGroups) { %>
UserPoolId: !Ref UserPoolId
<% } %>
pBucketName: !Ref pS3InputName
rS3OutputBucket:
Type: AWS::CloudFormation::Stack
Expand Down
18 changes: 8 additions & 10 deletions provider-utils/awscloudformation/schemas/schema.graphql.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ type VodAsset @model (subscriptions: {level: public})
<% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%>
{allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
<% } -%>
<% if (locals.permissions && locals.permissions.permissionSchema.includes("admin")) { -%>
{allow: groups, groups:["Admin"], operations: [create, update, delete, read]},
<% } -%>
<% if (!locals.permissions) { -%>
{allow: groups, groups:["Admin"], operations: [create, update, delete, read]},
<% if (locals.permissions && locals.permissions.selectedGroups) { -%>
<% locals.permissions.selectedGroups.forEach(group => { -%>
{allow: groups, groups:["<%= group %>"], operations: [create, update, delete, read]},
<% }) -%>
<% } -%>
{allow: private, operations: [read]}
]
Expand All @@ -31,11 +30,10 @@ type VideoObject @model
<% if (locals.permissions && locals.permissions.permissionSchema.includes("any")) { -%>
{allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
<% } -%>
<% if (locals.permissions && locals.permissions.permissionSchema.includes("admin")) { -%>
{allow: groups, groups:["Admin"], operations: [create, update, delete, read]},
<% } -%>
<% if (!locals.permissions) { -%>
{allow: groups, groups:["Admin"], operations: [create, update, delete, read]},
<% if (locals.permissions && locals.permissions.selectedGroups) { -%>
<% locals.permissions.selectedGroups.forEach(group => { -%>
{allow: groups, groups:["<%= group %>"], operations: [create, update, delete, read]},
<% }) -%>
<% } -%>
{allow: private, operations: [read]}
]
Expand Down
168 changes: 87 additions & 81 deletions provider-utils/awscloudformation/service-walkthroughs/vod-push.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { generateKeyPairSync } = require('crypto');
const headlessMode = require('../utils/headless-mode');
const question = require('../../vod-questions.json');
const { getAWSConfig } = require('../utils/get-aws');
const { generateIAMAdmin, generateIAMAdminPolicy } = require('./vod-roles');
const { generateIAMGroupPolicy } = require('./vod-roles');

module.exports = {
serviceQuestions,
Expand Down Expand Up @@ -61,7 +61,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc
nameDict = await inquirer.prompt(nameProject);
props.shared = nameDict;
const uuid = Math.random().toString(36).substring(2, 6)
+ Math.random().toString(36).substring(2, 6);
+ Math.random().toString(36).substring(2, 6);
props.shared.bucketInput = `${nameDict.resourceName.toLowerCase()}-${projectDetails.localEnvInfo.envName}-input-${uuid}`.slice(0, 63);
props.shared.bucketOutput = `${nameDict.resourceName.toLowerCase()}-${projectDetails.localEnvInfo.envName}-output-${uuid}`.slice(0, 63);
}
Expand Down Expand Up @@ -285,6 +285,21 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc
},
};

let authName = getAuthName(context);
if (!authName) {
context.print.warning('You have no auth projects.');
}
else {
props.parameters.UserPoolId = {
'Fn::GetAtt': [
`auth${authName}`,
'Outputs.UserPoolId',
],
};

}


if (cmsResponse.enableCMS) {
let apiName = getAPIName(context);
if (apiName === '') {
Expand All @@ -299,6 +314,7 @@ async function serviceQuestions(context, options, defaultValuesFilename, resourc
}

await createCMS(context, apiName, props);

props.parameters.GraphQLAPIId = {
'Fn::GetAtt': [
`api${apiName}`,
Expand Down Expand Up @@ -388,7 +404,7 @@ async function createCDNEnvVars(context, options, resourceName, aws) {
aws = await provider.getConfiguredAWSClient(context);
}
const uuid = Math.random().toString(36).substring(2, 6)
+ Math.random().toString(36).substring(2, 6);
+ Math.random().toString(36).substring(2, 6);
const secretName = `${resourceName}-${projectDetails.localEnvInfo.envName}-pem-${uuid}`.slice(0, 63);
const rPublicName = `rCloudFrontPublicKey${projectDetails.localEnvInfo.envName}${uuid}`.slice(0, 63);
const publicKeyName = `${resourceName}-${projectDetails.localEnvInfo.envName}-publickey-${uuid}`.slice(0, 63);
Expand Down Expand Up @@ -426,6 +442,16 @@ async function createCMS(context, apiName, props) {
},
},
];

if ((await getUserPoolGroups(context)).length > 0) {
permissions[0].choices.unshift({
"name": "Users in specific groups can upload videos",
"value": "selectGroups",
"next": "selectGroups",
"checked": true,
"ignore": true
})
}
const cmsEdit = [
{
type: question.overrideSchema.type,
Expand Down Expand Up @@ -453,6 +479,27 @@ async function createCMS(context, apiName, props) {
const permissionsResponse = await inquirer.prompt(permissions);
props.permissions = permissionsResponse;

if (props.permissions.permissionSchema.includes('selectGroups')) {

const selectGroups = [
{
type: question.selectGroups.type,
name: question.selectGroups.key,
message: question.selectGroups.question,
choices: await generateGroupOptions(context),
validate(answer) {
if (answer.length < 1) {
return 'You must choose at least one group';
}
return true;
},
},
];

const selectGroupsResponse = await inquirer.prompt(selectGroups);
props.permissions = { ...props.permissions, selectedGroups: selectGroupsResponse.selectGroups };
}

if (fs.existsSync(`${resourceDir}/schema.graphql`)) {
const currentSchema = fs.readFileSync(`${resourceDir}/schema.graphql`);
if (!currentSchema.includes('videoObject') && !currentSchema.includes('vodAsset')) {
Expand Down Expand Up @@ -480,9 +527,7 @@ async function createCMS(context, apiName, props) {
}
// TODO: Add check if they switched schemas
}
if (props.permissions.permissionSchema.includes('admin')) {
authGroupHack(context, props.shared.bucketInput);
}

createDependency(context, props, apiName);
}

Expand Down Expand Up @@ -545,8 +590,23 @@ function getAPIName(context) {
}
return apiName;
}
function getAuthName(context) {
const { amplifyMeta } = context.amplify.getProjectDetails();
let authName = '';

async function authGroupHack(context, bucketName) {
if (amplifyMeta.auth) {
const categoryResources = amplifyMeta.auth;
Object.keys(categoryResources).forEach((resource) => {
if (categoryResources[resource].service === 'Cognito') {
authName = resource;
}
});
}
return authName;
}


async function getUserPoolGroups(context) {
const userPoolGroupFile = path.join(
context.amplify.pathManager.getBackendDirPath(),
'auth',
Expand All @@ -556,86 +616,32 @@ async function authGroupHack(context, bucketName) {

const amplifyMeta = context.amplify.getProjectMeta();

if (!('auth' in amplifyMeta) || Object.keys(amplifyMeta.auth).length === 0) {
context.print.error('You have no auth projects. Moving on.');
return;
}

let resourceName = '';

Object.keys(amplifyMeta.auth).forEach((authCategory) => {
if (amplifyMeta.auth[authCategory].service === 'Cognito') {
resourceName = authCategory;
}
});

if (fs.existsSync(userPoolGroupFile)) {
const userPoolGroup = JSON.parse(fs.readFileSync(userPoolGroupFile));
if (userPoolGroup.length === 0) {
userPoolGroup.push(generateIAMAdmin(resourceName, bucketName));
} else {
userPoolGroup.forEach((userGroup, index) => {
if (userGroup.groupName === 'Admin') {
if (!('customPolicies' in userGroup)) {
userGroup.customPolicies = [];
}
let userPoolGroup = []

const policy = generateIAMAdminPolicy(resourceName, bucketName);
if (!userGroup.customPolicies.some(
existingPolicy => existingPolicy.PolicyName === policy.PolicyName,
)) {
userGroup.customPolicies.push(policy);
}
return;
}
if (userPoolGroup.length === index + 1) {
userPoolGroup.push(generateIAMAdmin(resourceName, bucketName));
}
});
if (('auth' in amplifyMeta) && Object.keys(amplifyMeta.auth).length > 0) {
if (fs.existsSync(userPoolGroupFile)) {
userPoolGroup = JSON.parse(fs.readFileSync(userPoolGroupFile));
}
updateUserPoolGroups(context, userPoolGroup);
} else {
const admin = generateIAMAdmin(resourceName, bucketName);
const userPoolGroupList = [admin];
updateUserPoolGroups(context, userPoolGroupList);
context.amplify.updateamplifyMetaAfterResourceAdd('auth', 'userPoolGroups', {
service: 'Cognito-UserPool-Groups',
providerPlugin: 'awscloudformation',
dependsOn: [
{
category: 'auth',
resourceName,
attributes: ['UserPoolId', 'AppClientIDWeb', 'AppClientID', 'IdentityPoolId'],
},
],
});
}
return userPoolGroup
}

async function generateGroupOptions(context) {
const userPoolGroup = await getUserPoolGroups(context, true)

function updateUserPoolGroups(context, userPoolGroupList) {
if (userPoolGroupList && userPoolGroupList.length > 0) {
const userPoolGroupFile = path.join(
context.amplify.pathManager.getBackendDirPath(),
'auth',
'userPoolGroups',
'user-pool-group-precedence.json',
);

const userPoolGroupParams = path.join(context.amplify.pathManager.getBackendDirPath(), 'auth', 'userPoolGroups', 'parameters.json');
let groupOptions = []

/* eslint-disable */
const groupParams = {
AuthRoleArn: {
'Fn::GetAtt': ['AuthRole', 'Arn'],
},
UnauthRoleArn: {
'Fn::GetAtt': ['UnauthRole', 'Arn'],
},
};
/* eslint-enable */

fs.outputFileSync(userPoolGroupParams, JSON.stringify(groupParams, null, 4));
fs.outputFileSync(userPoolGroupFile, JSON.stringify(userPoolGroupList, null, 4));
if (userPoolGroup.length === 0) {
context.print.error('You have no cognito groups');
} else {
userPoolGroup.forEach((userGroup) => {
groupOptions.push({
"name": userGroup.groupName,
"value": userGroup.groupName,
"ignore": true
})
});
}
return groupOptions
}

21 changes: 5 additions & 16 deletions provider-utils/awscloudformation/service-walkthroughs/vod-roles.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
function generateIAMAdmin(resourceName, bucketName) {
const admin = {
groupName: 'Admin',
precedence: 1,
customPolicies: [],
};
admin.customPolicies.push(generateIAMAdminPolicy(resourceName, bucketName));
return admin;
}

function generateIAMAdminPolicy(resourceName, bucketName) {
const adminPolicy = {
PolicyName: `${resourceName}-admin-group-policy`,
function generateIAMGroupPolicy(resourceName, groupName, bucketName) {
const groupPolicy = {
PolicyName: `${resourceName}-vod-${groupName.toLowerCase()}-group-policy`,
PolicyDocument: {
Version: '2012-10-17',
Statement: [
Expand All @@ -26,10 +16,9 @@ function generateIAMAdminPolicy(resourceName, bucketName) {
],
},
};
return adminPolicy;
return groupPolicy;
}

module.exports = {
generateIAMAdmin,
generateIAMAdminPolicy,
generateIAMGroupPolicy,
};
Loading