Skip to content

Commit

Permalink
bring in other pentest branch to test batch function
Browse files Browse the repository at this point in the history
  • Loading branch information
anyoussefinia committed Dec 13, 2024
2 parents e475895 + 8a3debe commit a10c404
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 148 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
name: ${{ startsWith(github.ref_name, 'snyk-') && 'snyk' || github.ref_name }}
url: "https://onemac.cms.gov"
steps:
- name: Check GITHUB_REF
run: echo "GITHUB_REF is $GITHUB_REF"
- name: set branch_name
run: echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
- name: Check branch name is a legal serverless stage name
Expand Down
File renamed without changes.
70 changes: 70 additions & 0 deletions services/admin/handlers/batchUpdateCognitoUsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import AWS from "aws-sdk";
const cognito = new AWS.CognitoIdentityServiceProvider();
import { getUser } from "../../app-api/getUser";

async function updateUserAttribute(userPoolId, username, roles) {
const params = {
UserPoolId: userPoolId,
Username: username,
UserAttributes: [
{
Name: 'custom:user_roles',
Value: JSON.stringify(roles)
}
]
};

await cognito.adminUpdateUserAttributes(params).promise();
}

async function processCognitoUsers() {
const userPoolId = process.env.USER_POOL_ID;
console.log("user pool id: ", userPoolId)
let paginationToken = null;
let counter = 0;
let hasRolesCounter = 0;
let noRolesCounter =0;
do {
const params = {
UserPoolId: userPoolId,
AttributesToGet: ['email'],
PaginationToken: paginationToken
};

const listUsersResponse = await cognito.listUsers(params).promise();
console.log(listUsersResponse.Users.length + " users found")

for (const user of listUsersResponse.Users) {
const emailAttribute = user.Attributes.find(attr => attr.Name === 'email');
if (emailAttribute) {
const userEmail = emailAttribute.Value;

try {
const externalUser = await getUser(userEmail);
let roles = [""];
if (externalUser.roleList) {
roles = externalUser.roleList.map(role => role.role);
hasRolesCounter ++;
} else {
noRolesCounter ++
}

await updateUserAttribute(userPoolId, user.Username, roles);
} catch (error) {
console.error(`Error processing user ${userEmail}:`, error);
}
}
counter++;
}

paginationToken = listUsersResponse.PaginationToken;
} while (paginationToken);
console.log(counter+ "users modified, "+ hasRolesCounter + "users had roles and "+ noRolesCounter + " users had no roles")
}


export const main = async () => {
await processCognitoUsers().catch(console.error);
console.log("function complete")
}

27 changes: 20 additions & 7 deletions services/admin/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ frameworkVersion: "3"

useDotenv: true

variablesResolutionMode: 20210326

package:
individually: true

Expand All @@ -13,15 +15,17 @@ plugins:
- serverless-s3-bucket-helper

custom:
stage: ${opt:stage, 'dev'} # Ensure the 'stage' is being passed correctly
stage: ${opt:stage, self:provider.stage}
iamPermissionsBoundaryPolicy: ${ssm:/configuration/${self:custom.stage}/iam/permissionsBoundaryPolicy, ssm:/configuration/default/iam/permissionsBoundaryPolicy, ""}
oneMacTableName: onemac-${self:custom.stage}-one
userPoolName: ${self:custom.stage}-user-pool
userPoolId: ${cf:ui-auth-${self:custom.stage}.UserPoolId}

provider:
name: aws
runtime: nodejs20.x
region: us-east-1
stage: ${self:custom.stage}
stage: dev
iam:
role:
path: ${ssm:/configuration/${self:custom.stage}/iam/path, ssm:/configuration/default/iam/path, "/"}
Expand All @@ -39,17 +43,27 @@ provider:
- arn:aws:dynamodb:*:*:table/onemac-develop-one
- arn:aws:dynamodb:*:*:table/${self:custom.oneMacTableName}
- arn:aws:dynamodb:*:*:table/${self:custom.oneMacTableName}/index/*
- Effect: Allow
Action:
- cognito-idp:ListUsers
- cognito-idp:AdminUpdateUserAttributes
Resource: arn:aws:cognito-idp:${self:provider.region}:*:userpool/${self:custom.userPoolId}

environment:
NODE_OPTIONS: '--enable-source-maps'
oneMacTableName: ${self:custom.oneMacTableName}

USER_POOL_ID: ${self:custom.userPoolId}
layers:
- ${cf:aws-sdk-v2-layer-${self:custom.stage}.AwsSdkV2LambdaLayerQualifiedArn}
- ${cf:aws-sdk-v2-layer-${self:custom.stage}.AwsSdkV2LambdaLayerQualifiedArn}


functions:
addPropertyToJWT:
handler: ./handlers/addPropertyToJWT.handler
batchUpdateCognitoUsers:
handler: ./handlers/batchUpdateCognitoUsers.main
timeout: 360

addRolesToJWT:
handler: ./handlers/addRolesToJWT.handler
timeout: 360

resetData:
Expand Down Expand Up @@ -90,4 +104,3 @@ functions:
insertNotification:
handler: ./handlers/insertNotification.main
timeout: 180

2 changes: 1 addition & 1 deletion services/app-api/getDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const getDetails = async (event) => {
if (!userRoleObj.isCMSUser && result.Item.reviewTeam)
delete result.Item.reviewTeam;

result.Item.actions = getActionsForPackage(
result.Item.actions = await getActionsForPackage(
result.Item.componentType,
originalStatus,
!!result.Item.latestRaiResponseTimestamp,
Expand Down
182 changes: 89 additions & 93 deletions services/app-api/getMyPackages.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,106 +19,102 @@ export const getMyPackages = async (email, group) => {
if (!email) return RESPONSE_CODE.USER_NOT_FOUND;
if (!group) return RESPONSE_CODE.DATA_MISSING;

return getUser(email)
.then((user) => {
if (!user) throw RESPONSE_CODE.USER_NOT_AUTHORIZED;
try {
const user = await getUser(email);
if (!user) throw RESPONSE_CODE.USER_NOT_AUTHORIZED;

const userRoleObj = getUserRoleObj(user.roleList);
const territoryList = getActiveTerritories(user.roleList);
const statusMap = userRoleObj.isCMSUser
? cmsStatusUIMap
: stateStatusUIMap;
const userRoleObj = getUserRoleObj(user.roleList);
const territoryList = getActiveTerritories(user.roleList);
const statusMap = userRoleObj.isCMSUser ? cmsStatusUIMap : stateStatusUIMap;

if (
!userRoleObj.canAccessDashboard ||
(Array.isArray(territoryList) && territoryList.length === 0)
) {
throw RESPONSE_CODE.USER_NOT_AUTHORIZED;
}
if (
!userRoleObj.canAccessDashboard ||
(Array.isArray(territoryList) && territoryList.length === 0)
) {
throw RESPONSE_CODE.USER_NOT_AUTHORIZED;
}

const baseParams = {
TableName: process.env.oneMacTableName,
IndexName: "GSI1",
ExclusiveStartKey: null,
ScanIndexForward: false,
ProjectionExpression:
"componentId,componentType,currentStatus,submissionTimestamp,latestRaiResponseTimestamp,lastActivityTimestamp,submitterName,submitterEmail,waiverAuthority, cpocName, reviewTeam, subStatus, finalDispositionDate",
};
const grouppk = "OneMAC#" + group;
let paramList = [];
if (territoryList[0] !== "N/A") {
paramList = territoryList.map((territory) => {
return {
...baseParams,
KeyConditionExpression: "GSI1pk = :pk AND begins_with(GSI1sk,:t1)",
ExpressionAttributeValues: {
":pk": grouppk,
":t1": territory,
},
};
});
} else {
paramList = [
{
...baseParams,
KeyConditionExpression: "GSI1pk = :pk",
ExpressionAttributeValues: {
":pk": grouppk,
},
const baseParams = {
TableName: process.env.oneMacTableName,
IndexName: "GSI1",
ExclusiveStartKey: null,
ScanIndexForward: false,
ProjectionExpression:
"componentId,componentType,currentStatus,submissionTimestamp,latestRaiResponseTimestamp,lastActivityTimestamp,submitterName,submitterEmail,waiverAuthority, cpocName, reviewTeam, subStatus, finalDispositionDate",
};
const grouppk = "OneMAC#" + group;
let paramList = [];
if (territoryList[0] !== "N/A") {
paramList = territoryList.map((territory) => {
return {
...baseParams,
KeyConditionExpression: "GSI1pk = :pk AND begins_with(GSI1sk,:t1)",
ExpressionAttributeValues: {
":pk": grouppk,
":t1": territory,
},
];
}
};
});
} else {
paramList = [
{
...baseParams,
KeyConditionExpression: "GSI1pk = :pk",
ExpressionAttributeValues: {
":pk": grouppk,
},
},
];
}

return Promise.all(
paramList.map(async (params) => {
const promiseItems = [];
do {
const results = await dynamoDb.query(params);
results.Items.map((oneItem) => {
oneItem.actions = getActionsForPackage(
oneItem.componentType,
oneItem.currentStatus,
!!oneItem.latestRaiResponseTimestamp,
oneItem.subStatus,
userRoleObj,
"package"
);
if (oneItem.waiverAuthority)
oneItem.temporaryExtensionType = oneItem.waiverAuthority.slice(
0,
7
);
// Using a for...of loop to ensure async operations are awaited correctly
const allItems = [];
for (const params of paramList) {
const promiseItems = [];
do {
const results = await dynamoDb.query(params);
for (const oneItem of results.Items) {
oneItem.actions = await getActionsForPackage(
oneItem.componentType,
oneItem.currentStatus,
!!oneItem.latestRaiResponseTimestamp,
oneItem.subStatus,
userRoleObj,
"package"
);
if (oneItem.waiverAuthority)
oneItem.temporaryExtensionType = oneItem.waiverAuthority.slice(
0,
7
);

if (statusMap[oneItem.subStatus]) {
oneItem.subStatus = statusMap[oneItem.subStatus];
}
if (statusMap[oneItem.subStatus]) {
oneItem.subStatus = statusMap[oneItem.subStatus];
}

if (!statusMap[oneItem.currentStatus])
console.log(
"%s status of %s not mapped!",
oneItem.pk,
oneItem.currentStatus
);
else {
oneItem.currentStatus = statusMap[oneItem.currentStatus];
if (
oneItem.currentStatus !== Workflow.ONEMAC_STATUS.INACTIVATED
)
promiseItems.push(oneItem);
}
});
params.ExclusiveStartKey = results.LastEvaluatedKey;
} while (params.ExclusiveStartKey);
return promiseItems;
})
).then((values) => {
return values.flat();
});
})
.catch((error) => {
console.log("error is: ", error);
return error;
});
if (!statusMap[oneItem.currentStatus])
console.log(
"%s status of %s not mapped!",
oneItem.pk,
oneItem.currentStatus
);
else {
oneItem.currentStatus = statusMap[oneItem.currentStatus];
if (oneItem.currentStatus !== Workflow.ONEMAC_STATUS.INACTIVATED)
promiseItems.push(oneItem);
}
}
params.ExclusiveStartKey = results.LastEvaluatedKey;
} while (params.ExclusiveStartKey);

allItems.push(...promiseItems);
}

return allItems; // Flattened list of all items
} catch (error) {
console.log("error is: ", error);
return error;
}
};

// get the approver list for a rols and possibly a territory
Expand Down
9 changes: 9 additions & 0 deletions services/app-api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions services/app-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"xstate": "^4.26.0"
},
"dependencies": {
"@launchdarkly/node-server-sdk": "^9.7.2",
"cmscommonlib": "file:../common",
"date-fns": "^2.16.1",
"dynamodb-local": "^0.0.32",
Expand Down
2 changes: 2 additions & 0 deletions services/app-api/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ custom:
applicationEndpoint: ${cf:ui-${self:custom.stage}.ApplicationEndpointUrl, "onemac.cms.gov"}
attachmentsBucket: ${cf:uploads-${self:custom.stage}.AttachmentsBucketName}
attachmentsBucketArn: ${cf:uploads-${self:custom.stage}.AttachmentsBucketArn}
launchDarklySdkKey: ${ssm:/configuration/${self:custom.stage}/launchdarkly/sdkkey, ssm:/configuration/default/launchdarkly/sdkkey}
warmupEnabled:
production: true
development: false
Expand Down Expand Up @@ -87,6 +88,7 @@ provider:
applicationEndpoint: ${self:custom.applicationEndpoint}
attachmentsBucket: ${self:custom.attachmentsBucket}
configurationSetName: 'email-${self:custom.stage}-configuration'
launchDarklySdkKey: ${self:custom.launchDarklySdkKey}
tracing:
apiGateway: true
lambda: true
Expand Down
Loading

0 comments on commit a10c404

Please sign in to comment.