Skip to content

Commit

Permalink
Merge pull request #149 from cisagov/feature/rsc
Browse files Browse the repository at this point in the history
Merge ReadySetCyber branch for login testing
  • Loading branch information
schmelz21 authored Apr 9, 2024
2 parents 6cc642f + 25d5e5a commit 8ff7790
Show file tree
Hide file tree
Showing 28 changed files with 1,700 additions and 55 deletions.
2 changes: 1 addition & 1 deletion backend/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ app.get('/', handlerToExpress(healthcheck));
app.post('/auth/login', handlerToExpress(auth.login));
app.post('/auth/callback', handlerToExpress(auth.callback));
app.post('/users/register', handlerToExpress(users.register));
app.post('/readysetcyber/register', handlerToExpress(users.RSCRegister));

const checkUserLoggedIn = async (req, res, next) => {
req.requestContext = {
Expand Down Expand Up @@ -276,7 +277,6 @@ app.use(
const authenticatedNoTermsRoute = express.Router();
authenticatedNoTermsRoute.use(checkUserLoggedIn);
authenticatedNoTermsRoute.get('/users/me', handlerToExpress(users.me));
// authenticatedNoTermsRoute.post('/users/register', handlerToExpress(users.register));
authenticatedNoTermsRoute.post(
'/users/me/acceptTerms',
handlerToExpress(users.acceptTerms)
Expand Down
103 changes: 103 additions & 0 deletions backend/src/api/assessments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { validateBody, wrapHandler, NotFound, Unauthorized } from './helpers';
import { Assessment, connectToDatabase } from '../models';
import { isUUID } from 'class-validator';

/**
* @swagger
*
* /assessments:
* post:
* description: Save an RSC assessment to the XFD database.
* tags:
* - Assessments
*/
export const createAssessment = wrapHandler(async (event) => {
const body = await validateBody(Assessment, event.body);

await connectToDatabase();

const assessment = Assessment.create(body);
await Assessment.save(assessment);

return {
statusCode: 200,
body: JSON.stringify(assessment)
};
});

/**
* @swagger
*
* /assessments:
* get:
* description: Lists all assessments for the logged-in user.
* tags:
* - Assessments
*/
export const list = wrapHandler(async (event) => {
const userId = event.requestContext.authorizer!.id;

if (!userId) {
return Unauthorized;
}

await connectToDatabase();

const assessments = await Assessment.find({
where: { user: userId }
});

return {
statusCode: 200,
body: JSON.stringify(assessments)
};
});

/**
* @swagger
*
* /assessments/{id}:
* get:
* description: Return user responses and questions organized by category for a specific assessment.
* parameters:
* - in: path
* name: id
* description: Assessment id
* tags:
* - Assessments
*/
export const get = wrapHandler(async (event) => {
const assessmentId = event.pathParameters?.id;

if (!assessmentId || !isUUID(assessmentId)) {
return NotFound;
}

await connectToDatabase();

const assessment = await Assessment.findOne(assessmentId, {
relations: [
'responses',
'responses.question',
'responses.question.category'
]
});

if (!assessment) {
return NotFound;
}

const responsesByCategory = assessment.responses.reduce((acc, response) => {
const categoryName = response.question.category.name;
if (!acc[categoryName]) {
acc[categoryName] = [];
}
acc[categoryName].push(response);
return acc;
}, {});

return {
statusCode: 200,
body: JSON.stringify(responsesByCategory)
};
});
73 changes: 73 additions & 0 deletions backend/src/api/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,30 @@ If you encounter any difficulties, please feel free to reply to this email (or s
);
};

const sendRSCInviteEmail = async (email: string) => {
const staging = process.env.NODE_ENV !== 'production';

await sendEmail(
email,
'ReadySetCyber Dashboard Invitation',
`Hi there,
You've been invited to join ReadySetCyber Dashboard. To accept the invitation and start using your Dashboard, sign on at ${process.env.FRONTEND_DOMAIN}/readysetcyber/create-account.
Crossfeed access instructions:
1. Visit ${process.env.FRONTEND_DOMAIN}/readysetcyber/create-account.
2. Select "Create Account."
3. Enter your email address and a new password for Crossfeed.
4. A confirmation code will be sent to your email. Enter this code when you receive it.
5. You will be prompted to enable MFA. Scan the QR code with an authenticator app on your phone, such as Microsoft Authenticator. Enter the MFA code you see after scanning.
6. After configuring your account, you will be redirected to Crossfeed.
For more information on using Crossfeed, view the Crossfeed user guide at https://docs.crossfeed.cyber.dhs.gov/user-guide/quickstart/.
If you encounter any difficulties, please feel free to reply to this email (or send an email to ${process.env.CROSSFEED_SUPPORT_EMAIL_REPLYTO}).`
);
};
/**
* @swagger
*
Expand Down Expand Up @@ -892,3 +916,52 @@ export const updateV2 = wrapHandler(async (event) => {
}
return NotFound;
});

/**
* @swagger
*
* /readysetcyber/register:
* post:
* description: New ReadySetCyber user registration.
* tags:
* - RSCUsers
*/
export const RSCRegister = wrapHandler(async (event) => {
const body = await validateBody(NewUser, event.body);
const newRSCUser = {
firstName: body.firstName,
lastName: body.lastName,
email: body.email.toLowerCase(),
userType: UserType.READY_SET_CYBER
};

await connectToDatabase();

// Check if user already exists
let user = await User.findOne({
email: newRSCUser.email
});
if (user) {
console.log('User already exists.');
return {
statusCode: 422,
body: 'User email already exists. Registration failed.'
};
// Create if user does not exist
} else {
user = await User.create(newRSCUser);
await User.save(user);
// Send email notification
if (process.env.IS_LOCAL!) {
console.log('Cannot send invite email while running on local.');
} else {
await sendRSCInviteEmail(user.email);
}
}

const savedUser = await User.findOne(user.id);
return {
statusCode: 200,
body: JSON.stringify(savedUser)
};
});
36 changes: 36 additions & 0 deletions backend/src/models/assessment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm';
import { Response } from './response';
import { User } from './user';

@Entity()
export class Assessment extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;

@Column()
rscId: string;

@Column()
type: string;

@ManyToOne(() => User, (user) => user.assessments)
user: User;

@OneToMany(() => Response, (response) => response.assessment)
responses: Response[];
}
26 changes: 26 additions & 0 deletions backend/src/models/category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
BaseEntity,
Column,
Entity,
OneToMany,
PrimaryGeneratedColumn
} from 'typeorm';
import { Question } from './question';

@Entity()
export class Category extends BaseEntity {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column()
name: string;

@Column({ nullable: true })
number: string;

@Column({ nullable: true })
shortName: string;

@OneToMany(() => Question, (question) => question.category)
questions: Question[];
}
44 changes: 27 additions & 17 deletions backend/src/models/connection.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { createConnection, Connection } from 'typeorm';
import {
// Models for the Crossfeed database
ApiKey,
Assessment,
Category,
Cpe,
Cve,
Domain,
Service,
Vulnerability,
Scan,
Organization,
User,
OrganizationTag,
Question,
Resource,
Response,
Role,
SavedSearch,
Scan,
ScanTask,
Service,
User,
Vulnerability,
Webpage,
ApiKey,
SavedSearch,
OrganizationTag,
Cpe,
Cve,

// Models for the Mini Data Lake database
CertScan,
Expand Down Expand Up @@ -110,20 +115,25 @@ const connectDb = async (logging?: boolean) => {
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: [
ApiKey,
Assessment,
Category,
Cpe,
Cve,
Domain,
Service,
Vulnerability,
Scan,
Organization,
User,
OrganizationTag,
Question,
Resource,
Response,
Role,
ScanTask,
Webpage,
ApiKey,
SavedSearch,
OrganizationTag
Scan,
ScanTask,
Service,
User,
Vulnerability,
Webpage
],
synchronize: false,
name: 'default',
Expand Down
25 changes: 15 additions & 10 deletions backend/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
export * from './domain';
export * from './cve';
export * from './cpe';
export * from './service';
export * from './api-key';
export * from './assessment';
export * from './category';
export * from './connection';
export * from './vulnerability';
export * from './scan';
export * from './cpe';
export * from './cve';
export * from './domain';
export * from './organization';
export * from './user';
export * from './organization-tag';
export * from './question';
export * from './resource';
export * from './response';
export * from './role';
export * from './saved-search';
export * from './scan';
export * from './scan-task';
export * from './service';
export * from './user';
export * from './vulnerability';
export * from './webpage';
export * from './api-key';
export * from './saved-search';
export * from './organization-tag';
// Mini data lake models
export * from './mini_data_lake/cert_scans';
export * from './mini_data_lake/cidrs';
Expand Down
Loading

0 comments on commit 8ff7790

Please sign in to comment.