From 980d76fd525051a60e9b0e22d27e959baf7abae4 Mon Sep 17 00:00:00 2001 From: Forrest Date: Mon, 6 Feb 2023 11:41:02 -0800 Subject: [PATCH] survey nearly done, needs wiring --- lunatrace/bsl/frontend/src/api/generated.ts | 88 +++++++++++++++++- .../api/graphql/getCurrentUserInfo.graphql | 1 + .../bsl/frontend/src/contexts/UserContext.tsx | 37 +++----- .../src/pages/homepage/AuthenticatedHome.tsx | 71 +++++++++------ .../src/pages/homepage/NewCustomerSurvey.tsx | 89 +++++++++++++++++++ lunatrace/bsl/frontend/src/types/user.ts | 4 + .../lunatrace/tables/public_users.yaml | 21 +++++ .../down.sql | 2 + .../up.sql | 3 + 9 files changed, 263 insertions(+), 53 deletions(-) create mode 100644 lunatrace/bsl/frontend/src/pages/homepage/NewCustomerSurvey.tsx create mode 100644 lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/down.sql create mode 100644 lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/up.sql diff --git a/lunatrace/bsl/frontend/src/api/generated.ts b/lunatrace/bsl/frontend/src/api/generated.ts index 2fa3995af..9ee4176e5 100644 --- a/lunatrace/bsl/frontend/src/api/generated.ts +++ b/lunatrace/bsl/frontend/src/api/generated.ts @@ -3847,6 +3847,10 @@ export type Mutation_Root = { update_settings?: Maybe; /** update single row of the table: "settings" */ update_settings_by_pk?: Maybe; + /** update data of the table: "users" */ + update_users?: Maybe; + /** update single row of the table: "users" */ + update_users_by_pk?: Maybe; }; @@ -4155,6 +4159,30 @@ export type Mutation_RootUpdate_Settings_By_PkArgs = { pk_columns: Settings_Pk_Columns_Input; }; + +/** mutation root */ +export type Mutation_RootUpdate_UsersArgs = { + _append?: InputMaybe; + _delete_at_path?: InputMaybe; + _delete_elem?: InputMaybe; + _delete_key?: InputMaybe; + _prepend?: InputMaybe; + _set?: InputMaybe; + where: Users_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Users_By_PkArgs = { + _append?: InputMaybe; + _delete_at_path?: InputMaybe; + _delete_elem?: InputMaybe; + _delete_key?: InputMaybe; + _prepend?: InputMaybe; + _set?: InputMaybe; + pk_columns: Users_Pk_Columns_Input; +}; + /** column ordering options */ export enum Order_By { /** in ascending order, nulls last */ @@ -7769,6 +7797,18 @@ export type Users = { /** An object relationship */ kratos_identity?: Maybe; role: Scalars['user_role']; + survey?: Maybe; +}; + + +/** LunaTrace users, identified by their various auth identifiers (ex. github, kratos, etc.) */ +export type UsersSurveyArgs = { + path?: InputMaybe; +}; + +/** append existing jsonb value of filtered columns with new jsonb value */ +export type Users_Append_Input = { + survey?: InputMaybe; }; /** Boolean expression to filter rows from the table "users". All fields are combined with a logical 'AND'. */ @@ -7781,6 +7821,31 @@ export type Users_Bool_Exp = { kratos_id?: InputMaybe; kratos_identity?: InputMaybe; role?: InputMaybe; + survey?: InputMaybe; +}; + +/** delete the field or element with specified path (for JSON arrays, negative integers count from the end) */ +export type Users_Delete_At_Path_Input = { + survey?: InputMaybe>; +}; + +/** delete the array element with specified index (negative integers count from the end). throws an error if top level container is not an array */ +export type Users_Delete_Elem_Input = { + survey?: InputMaybe; +}; + +/** delete key/value pair or string element. key/value pairs are matched based on their key value */ +export type Users_Delete_Key_Input = { + survey?: InputMaybe; +}; + +/** response of any mutation on the table "users" */ +export type Users_Mutation_Response = { + __typename?: 'users_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']; + /** data from the rows affected by the mutation */ + returning: Array; }; /** Ordering options when selecting data from "users". */ @@ -7790,6 +7855,17 @@ export type Users_Order_By = { kratos_id?: InputMaybe; kratos_identity?: InputMaybe; role?: InputMaybe; + survey?: InputMaybe; +}; + +/** primary key columns input for table: users */ +export type Users_Pk_Columns_Input = { + id: Scalars['uuid']; +}; + +/** prepend existing jsonb value of filtered columns with new jsonb value */ +export type Users_Prepend_Input = { + survey?: InputMaybe; }; /** select columns of table "users" */ @@ -7801,9 +7877,16 @@ export enum Users_Select_Column { /** column name */ KratosId = 'kratos_id', /** column name */ - Role = 'role' + Role = 'role', + /** column name */ + Survey = 'survey' } +/** input type for updating data in table "users" */ +export type Users_Set_Input = { + survey?: InputMaybe; +}; + /** Boolean expression to compare columns of type "uuid". All fields are combined with logical 'AND'. */ export type Uuid_Comparison_Exp = { _eq?: InputMaybe; @@ -9103,7 +9186,7 @@ export type GetCurrentUserInfoQueryVariables = Exact<{ }>; -export type GetCurrentUserInfoQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', role: any }> }; +export type GetCurrentUserInfoQuery = { __typename?: 'query_root', users: Array<{ __typename?: 'users', role: any, survey?: any | null }> }; export type GetCweDetailsQueryVariables = Exact<{ id: Scalars['Int']; @@ -9549,6 +9632,7 @@ export const GetCurrentUserInfoDocument = ` query GetCurrentUserInfo($kratos_id: uuid!) { users(where: {kratos_id: {_eq: $kratos_id}}) { role + survey } } `; diff --git a/lunatrace/bsl/frontend/src/api/graphql/getCurrentUserInfo.graphql b/lunatrace/bsl/frontend/src/api/graphql/getCurrentUserInfo.graphql index ade83807c..677f0b849 100644 --- a/lunatrace/bsl/frontend/src/api/graphql/getCurrentUserInfo.graphql +++ b/lunatrace/bsl/frontend/src/api/graphql/getCurrentUserInfo.graphql @@ -1,5 +1,6 @@ query GetCurrentUserInfo($kratos_id: uuid!) { users(where: {kratos_id: {_eq: $kratos_id}}) { role + survey } } diff --git a/lunatrace/bsl/frontend/src/contexts/UserContext.tsx b/lunatrace/bsl/frontend/src/contexts/UserContext.tsx index acc31ac90..abf45defb 100644 --- a/lunatrace/bsl/frontend/src/contexts/UserContext.tsx +++ b/lunatrace/bsl/frontend/src/contexts/UserContext.tsx @@ -11,25 +11,29 @@ * limitations under the License. * */ +import { skipToken } from '@reduxjs/toolkit/query/react'; import React, { useEffect } from 'react'; import { api, GetCurrentUserInfoQuery } from '../api/generated'; import useAppSelector from '../hooks/useAppSelector'; import { selectKratosId } from '../store/slices/authentication'; +import { User } from '../types/user'; interface UserProviderState { isAdmin: boolean; + user: User | null; } const initialState = { isAdmin: false, + user: null, }; const UserContext = React.createContext(initialState); -function userIsAdmin(data: GetCurrentUserInfoQuery | undefined): boolean { +function getUserInfo(data: GetCurrentUserInfoQuery | undefined): UserProviderState { if (!data || data.users.length === 0) { - return false; + return initialState; } const users = data.users; @@ -41,35 +45,20 @@ function userIsAdmin(data: GetCurrentUserInfoQuery | undefined): boolean { const user = users[0]; const userRole = user.role; - return userRole === 'lunatrace_admin'; + return { + user, + isAdmin: userRole === 'lunatrace_admin', + }; } function UserProvider({ children }: { children: React.ReactNode }) { const userId = useAppSelector(selectKratosId); - const [trigger, result] = api.useLazyGetCurrentUserInfoQuery(); + const { data } = api.useGetCurrentUserInfoQuery(userId ? { kratos_id: userId } : skipToken); - useEffect(() => { - if (userId) { - void trigger({ - kratos_id: userId, - }); - } - }, [userId]); + const userInfo = getUserInfo(data); - const { data } = result; - - const isAdmin = userIsAdmin(data); - - return ( - - {children} - - ); + return {children}; } export { UserProvider, UserContext }; diff --git a/lunatrace/bsl/frontend/src/pages/homepage/AuthenticatedHome.tsx b/lunatrace/bsl/frontend/src/pages/homepage/AuthenticatedHome.tsx index cf1c0b5d3..f13de1141 100644 --- a/lunatrace/bsl/frontend/src/pages/homepage/AuthenticatedHome.tsx +++ b/lunatrace/bsl/frontend/src/pages/homepage/AuthenticatedHome.tsx @@ -12,49 +12,66 @@ * */ import React, { useContext } from 'react'; -import { Col, Container, Row } from 'react-bootstrap'; +import { Col, Container, Row, Spinner } from 'react-bootstrap'; import { Helmet } from 'react-helmet-async'; -import { ConditionallyRender } from '../../components/utils/ConditionallyRender'; import { LunaTraceIntroVideo } from '../../components/LunaTraceIntroVideo'; +import { ConditionallyRender } from '../../components/utils/ConditionallyRender'; +import { UserContext } from '../../contexts/UserContext'; import { WizardOpenContext } from '../../contexts/WizardContext'; +import { NewCustomerSurvey } from './NewCustomerSurvey'; import { RecentGuidesCard } from './cards/RecentGuides'; import { RecentProjectsCard } from './cards/RecentProjects'; import { SetupWizard } from './cards/SetupWizard'; export const AuthenticatedHome: React.FunctionComponent = (_props) => { const wizardOpen = useContext(WizardOpenContext); + + const { user } = useContext(UserContext); + + if (!user) { + return ; + } + + const surveyNotComplete = !user.survey; + return ( <> -
-

Welcome to LunaTrace

- - {wizardOpen ? ( -

+ {surveyNotComplete ? ( + + ) : ( + <> + {' '} +

+

Welcome to LunaTrace

+ {wizardOpen ? ( +

+ {' '} + You are almost on your way to finding vulnerabilities with LunaTrace! Follow the steps below to finish + setting up your account. +

+ ) : ( +

View your existing projects in the sidebar.

+ )} + +
+ {' '} - You are almost on your way to finding vulnerabilities with LunaTrace! Follow the steps below to finish - setting up your account. -

- ) : ( -

View your existing projects in the sidebar.

- )} - -
- - {' '} - - - - - - - - - - + + + + + + + + + + + + )}
); diff --git a/lunatrace/bsl/frontend/src/pages/homepage/NewCustomerSurvey.tsx b/lunatrace/bsl/frontend/src/pages/homepage/NewCustomerSurvey.tsx new file mode 100644 index 000000000..678ca50b6 --- /dev/null +++ b/lunatrace/bsl/frontend/src/pages/homepage/NewCustomerSurvey.tsx @@ -0,0 +1,89 @@ +/* + * Copyright by LunaSec (owned by Refinery Labs, Inc) + * + * Licensed under the Business Source License v1.1 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * https://github.com/lunasec-io/lunasec/blob/master/licenses/BSL-LunaTrace.txt + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import React, { FormEvent, useState } from 'react'; +import { Button, Card, Col, FloatingLabel, Form, Row, Spinner } from 'react-bootstrap'; + +import { User } from '../../types/user'; + +export const NewCustomerSurvey: React.FunctionComponent<{ user: User }> = ({ user }) => { + const [survey, setSurvey] = useState({ organization: '', role: '', hear_about_us: '' }); + + function submitForm(e: FormEvent) { + e.preventDefault(); + console.log(e); + } + return ( + <> +
+

Welcome to LunaTrace

+

Answer a few quick questions first.

+
+ + +
void submitForm(e)}> + + + + + setSurvey({ ...survey, organization: e.target.value })} + required={true} + placeholder="enter project name" + /> + + + + + + + + + setSurvey({ ...survey, organization: e.target.value })} + required={true} + placeholder="enter project name" + /> + + + + + + + + + setSurvey({ ...survey, organization: e.target.value })} + required={true} + placeholder="enter project name" + /> + + + + + + + + + +
+ +
+ + ); +}; diff --git a/lunatrace/bsl/frontend/src/types/user.ts b/lunatrace/bsl/frontend/src/types/user.ts index 60803912d..9747e6eb5 100644 --- a/lunatrace/bsl/frontend/src/types/user.ts +++ b/lunatrace/bsl/frontend/src/types/user.ts @@ -11,7 +11,11 @@ * limitations under the License. * */ +import { GetCurrentUserInfoQuery } from '../api/generated'; + export interface ImpersonateUser { id: string; name: string; } + +export type User = NonNullable['users'][number]; diff --git a/lunatrace/bsl/hasura/metadata/databases/lunatrace/tables/public_users.yaml b/lunatrace/bsl/hasura/metadata/databases/lunatrace/tables/public_users.yaml index 01df7c4a4..04392bfcb 100644 --- a/lunatrace/bsl/hasura/metadata/databases/lunatrace/tables/public_users.yaml +++ b/lunatrace/bsl/hasura/metadata/databases/lunatrace/tables/public_users.yaml @@ -23,6 +23,7 @@ select_permissions: - id - kratos_id - role + - survey filter: {} - role: user permission: @@ -31,6 +32,7 @@ select_permissions: - id - kratos_id - role + - survey filter: _or: - id: @@ -55,3 +57,22 @@ update_permissions: - kratos_id filter: {} check: null + - role: user + permission: + columns: + - survey + filter: + _or: + - id: + _eq: X-Hasura-Real-User-Id + - _exists: + _table: + name: users + schema: public + _where: + _and: + - id: + _eq: X-Hasura-Real-User-Id + - role: + _eq: lunatrace_admin + check: null diff --git a/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/down.sql b/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/down.sql new file mode 100644 index 000000000..b8c8f1bc9 --- /dev/null +++ b/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/down.sql @@ -0,0 +1,2 @@ + +ALTER TABLE public.users DROP COLUMN survey; diff --git a/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/up.sql b/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/up.sql new file mode 100644 index 000000000..ee2010156 --- /dev/null +++ b/lunatrace/bsl/hasura/migrations/lunatrace/1675284578762_user_signup_questions/up.sql @@ -0,0 +1,3 @@ + +ALTER TABLE public.users ADD COLUMN survey jsonb CHECK (length(survey::text) < 10000) NULL; +