Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base url #40

Closed
wants to merge 14 commits into from
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ testem.log
# System files
.DS_Store
Thumbs.db

# Ignore all files in the migrations directory
/supabase/migrations/*
!supabase/migrations/*/
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,18 @@
<hr>

<h1>Documentation</h1>
<p></p>Document public functions (especially in services).</p>
<p>Document public functions (especially in services).</p>
<hr>

<h1>Database security</h1>
<p>Supabase generates a public api for all database elements in the public schema. Therefore, database tables including data which should not be accessible by everyone, should be added to a non-public layer (hidden layer).</p>
<p>The public layer is solely used to store invoker database functions ("invoker" ensures that database users can only use database elements according to their role and associated rights). These functions can access the hidden table layer. Therefore, the column access is controlled by the access given to database functions.</p>
<p>Row level access is controlled by row security rules. Authenticated userIds should not be handed over to functions as function argument. Instead, supabase helper functions like auth.uid() should be used directly in database functions.</p>
<hr>

<h1>Testing Approach</h1>
<p>The project uses no unit tests so far. However, all features should be committed with a working end-to-end test that covers at least the expected positive base-line scenario, e.g. negative tests are not required.</p>
<p>The project uses no unit tests so far. However, all features should be committed with a working Cypress end-to-end test that covers at least the expected positive base-line scenario, e.g. the workflows possible in the GUI</p>
<p>To facilitate database security, negative scenarios (e.g. database calls which should not return results because they are not allowed) should be tested using Cypress api call tests.</p>
<hr>

<h1>Recommended "Definition of Done" Checklist</h1>
Expand Down
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumWarning": "2kb",
"maximumError": "1mb"
},
{
Expand Down
5 changes: 3 additions & 2 deletions copy_sql_files_to_migration_folder.bat
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ REM Delete all files in destination
del /q "%destination_folder%\*.*"

REM Use the copy command to copy the migration files to the destination folder
copy "%~dp0supabase\migrations\01_core\*" "%destination_folder%"
copy "%~dp0supabase\migrations\01_migration_preparation_and_schemas\*" "%destination_folder%"
copy "%~dp0supabase\migrations\02_profile\*" "%destination_folder%"
copy "%~dp0supabase\migrations\03_profile-follow\*" "%destination_folder%"
copy "%~dp0supabase\migrations\04_search\*" "%destination_folder%"
copy "%~dp0supabase\migrations\05_assistant\*" "%destination_folder%"
copy "%~dp0supabase\migrations\06_notifications\*" "%destination_folder%"
copy "%~dp0supabase\migrations\98_access_grants\*" "%destination_folder%"
copy "%~dp0supabase\migrations\99_seed\*" "%destination_folder%"

REM Check if the copy operation was successful
Expand All @@ -30,7 +31,7 @@ if errorlevel 1 (
pause

@REM Apply migrations remote
@REM echo Y | npx supabase db reset --linked
echo Y | npx supabase db reset --linked
supabase db push --include-all
pause

Expand Down
2 changes: 1 addition & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export default defineConfig({
},
baseUrl: 'http://localhost:4200',
defaultCommandTimeout: 10000
},
}
});
2 changes: 2 additions & 0 deletions cypress/e2e/02. auth.cy.ts → cypress/e2e/01.1 auth.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Sizes.forEach((size: Size): void => {
})

it('sign in.', (): void => {
cy.visit('landing/sign-in');
cy.signIn(newUser);
cy.url().should('contain', 'profile')
})
Expand All @@ -40,6 +41,7 @@ Sizes.forEach((size: Size): void => {
// })

it('sign out.', (): void => {
cy.visit('landing/sign-in');
cy.signIn(newUser);
cy.signOut(newUser)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Sizes.forEach((size: Size): void => {

beforeEach((): void => {
cy.viewport(size.width, size.height)
cy.visit('landing/sign-in');
cy.signIn(profile1);
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Sizes.forEach((size: Size): void => {

beforeEach((): void => {
cy.viewport(size.width, size.height)
cy.visit('landing/sign-in');
cy.signIn(seedWriteUser);
})

Expand All @@ -23,14 +24,15 @@ Sizes.forEach((size: Size): void => {
cy.getDataCy('edit-instruction')
.shouldBeVisible()

cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/profiles').as('updateProfile')
// cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/profiles').
cy.interceptSupabaseCall('update_user').as('updateProfile')
cy.getDataCy('firstName').clear()
cy.getDataCy('firstName').type(newFirstName)
cy.getDataCy('lastName').clear()
cy.getDataCy('lastName').type(newLastName)
cy.getDataCy('update').click()

cy.contains('Successful Update')
cy.contains('Successful')
.should('be.visible')
cy.wait('@updateProfile')
})
Expand Down
47 changes: 47 additions & 0 deletions cypress/e2e/02.2 profile negative api tests.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {supabaseClient} from "../../src/app/auth/supabase-client";
import {POSTGRES_ERRORS} from "../fixtures/postgres_errors";

describe(`Negative api tests for profile_counter table show that `, async () => {
let user_id: string | undefined;
let token: string | undefined;
const TEST_ID = '42e58ca1-2eb8-4651-93c2-cefba2e32f42';

beforeEach(async (): Promise<void> => {
cy.visit('landing/sign-in');
const response = await supabaseClient.auth.signInWithPassword(
{
email: 'follow@seed.com',
password: '12345678',
}
)
user_id = response.data.user?.id
token = response.data.session?.access_token
expect(user_id).to.be.not.null
expect(token).to.be.not.null
})

it('a non authenticated user can not update a profile', async (): Promise<void> => {
await supabaseClient.auth.signOut()

const response = await supabaseClient
.rpc('update_user')
expect(response.data).to.be.null
expect(response.error?.code).to.be.equal(POSTGRES_ERRORS.noPermission)
})

it('an authenticated user can only update its own profile', async (): Promise<void> => {
const response = await supabaseClient
// @ts-ignore
.rpc('update_user', {user_id: TEST_ID})
expect(response.data).to.be.null
expect(response.error?.code).to.be.equal(POSTGRES_ERRORS.function_not_existing)
})

it('a non authenticated user can not view profiles', async (): Promise<void> => {
await supabaseClient.auth.signOut()
const response = await supabaseClient
.rpc('select_user', {user_id: TEST_ID})
expect(response.data).to.be.null
expect(response.error?.code).to.be.equal(POSTGRES_ERRORS.noPermission)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {Size, Sizes} from "../fixtures/size";
const readUser: ProfileTest = seedReadUser2;
const followUser: ProfileTest = seedProfileFollowUser;
const followingUser: ProfileTest = seedProfileFollowingUser

// ATTENTION
// These test depend on the search and auth tests.
// These test depend on each other, e.g. unfollow test only work if follow test works

Sizes.forEach((size: Size): void => {
describe(`Profile follow tests with screen size ${size.width} show that users can `, () => {
beforeEach((): void => {
cy.visit('landing/sign-in');
cy.signIn(followUser);
})

Expand Down Expand Up @@ -57,17 +57,17 @@ Sizes.forEach((size: Size): void => {

cy.getDataCy('followButton')
.shouldBeVisible()
.should('have.text', 'UNFOLLOW')
.should('have.text', 'UNFOLLOW ')
.click()

cy.contains('Successful Update')
cy.contains('Successful')
.should('be.visible')

// cy.wait('@unfollowTransaction')

cy.getDataCy('followButton')
.shouldBeVisible()
.should('have.text', 'FOLLOW')
.should('have.text', 'FOLLOW ')

cy.getDataCy('followerCounter')
.shouldBeVisible()
Expand All @@ -91,7 +91,8 @@ Sizes.forEach((size: Size): void => {
cy.contains(followUser.first_name as string)
.click();

cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/rpc/select_following_of_user').as('loadFollowingOfUser')
cy.interceptSupabaseCall('select_following_of_user').as('loadFollowingOfUser')

cy.getDataCy('nav-follower-edit')
.shouldBeVisible()
.click()
Expand All @@ -105,7 +106,7 @@ Sizes.forEach((size: Size): void => {
.shouldBeVisible()
.contains(followingUser.first_name as string)

cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/rpc/unfollow_transaction').as('unfollowUser')
cy.interceptSupabaseCall('unfollow_transaction').as('unfollowUser')
cy.getDataCy('following_remove')
// cy.contains(followingUser.first_name)
// .find('[data-cy="following-remove"]')
Expand All @@ -128,7 +129,7 @@ Sizes.forEach((size: Size): void => {
.shouldBeVisible()
.click();

cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/rpc/select_follower_of_user').as('loadFollowerOfUser')
cy.interceptSupabaseCall('select_follower_of_user').as('loadFollowerOfUser')
cy.getDataCy('nav-follower-edit')
.shouldBeVisible()
.click()
Expand All @@ -141,7 +142,7 @@ Sizes.forEach((size: Size): void => {
cy.getDataCy('follower_first_name')
.shouldBeVisible()

cy.intercept('POST', 'https://abcwkgkiztruxwvfwabf.supabase.co/rest/v1/rpc/unfollow_transaction').as('unfollowUser')
cy.interceptSupabaseCall('remove_follower_transaction').as('unfollowUser')
cy.getDataCy('follower_remove')
// cy.contains(followUser.first_name)
// .find('[data-cy="follower-remove"]')
Expand Down
Loading
Loading