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

Multiple owner of a board #524

Merged
merged 67 commits into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
2e0f7ae
Add tab styling from admin
Collatty Oct 25, 2021
02d7466
any --> DocumentData
Collatty Oct 25, 2021
574f415
Add shared boards
Collatty Oct 25, 2021
2134dcf
Add shared with me tab to my boards
Collatty Oct 25, 2021
d93cd59
Add notification to tab
Collatty Oct 27, 2021
de336f8
Use entur notification
Collatty Oct 28, 2021
aa51bd9
Add new tab for board sharing in settings
magnusrand Oct 28, 2021
06feae4
Add list showing board owners emails and access
magnusrand Oct 28, 2021
787e640
Add functionality for adding users to board in settings
magnusrand Oct 29, 2021
89c4550
Fix linter issues
magnusrand Oct 29, 2021
94e1325
Merge branch 'add-more-owners' of https://github.com/entur/tavla into…
magnusrand Oct 29, 2021
dbe8192
Fix linter errors from merge
magnusrand Oct 29, 2021
5b98e82
Add button to remove self from board
magnusrand Nov 1, 2021
773238a
Move and improve fetch logic for sharedBoards
magnusrand Nov 1, 2021
aba0187
Add check for if user is already added to owner or request
magnusrand Nov 2, 2021
c0e9de5
Add functionality for accepting a board request
magnusrand Nov 2, 2021
4427a87
Add functionality to decline board request
magnusrand Nov 3, 2021
16114b2
Add loading indication to 'add to boards' button
magnusrand Nov 4, 2021
6eb68ed
Restructure Modals to a unified location
magnusrand Nov 4, 2021
a6ea468
Add remove from board option to overflow menu
magnusrand Nov 4, 2021
9bf1f47
Show MyBoards if a request is available
magnusrand Nov 4, 2021
7c8c95c
Restructure MyBoards
magnusrand Nov 4, 2021
5c3662b
Add Error screen for when no board requests are active
magnusrand Nov 4, 2021
cf1ec66
Fix prop bug
magnusrand Nov 4, 2021
0e3304d
Update no-boards message
magnusrand Nov 5, 2021
0b07503
Make link in shareTab copyable
magnusrand Nov 5, 2021
3efd3d0
Make owner and request list update on snapshot in share settings
magnusrand Nov 5, 2021
0c482f3
Add modal for when user is not owner of board and enters shareTab
magnusrand Nov 8, 2021
60347c3
Change "Del tavle" to "Kopier lenke" for disambiguation
magnusrand Nov 8, 2021
bb5aa4b
Grammer
magnusrand Nov 8, 2021
bcb7c27
Refactor ShareTab into separate components
magnusrand Nov 8, 2021
d03f117
clean up
magnusrand Nov 8, 2021
df93153
Merge branch 'master' into add-more-owners
magnusrand Nov 8, 2021
dbc4ba5
Fix broken BoardOwnersList
magnusrand Nov 8, 2021
5b3f087
Remove duplicate code
magnusrand Nov 8, 2021
7c83d7b
Fix add new owner request results in duplicate
magnusrand Nov 9, 2021
b2c130a
Fix copydata when creating new URL for board that changed in the back…
magnusrand Nov 9, 2021
b622ec7
Rewrite new functions to use async await
magnusrand Nov 9, 2021
b159a67
Small fixes
magnusrand Nov 9, 2021
4a4bbb5
Fix change boardName overwriting data
magnusrand Nov 9, 2021
5f5d9d8
Fix changing boardname without typing anything resulting in empty boa…
magnusrand Nov 9, 2021
dd6652b
Fix remove self from tavle
magnusrand Nov 9, 2021
6d88b34
Fix PR comments
magnusrand Nov 9, 2021
fb1fee2
Remove settings from ShareTab
magnusrand Nov 10, 2021
5381bf3
Rewrite ShareTab-useEffect to use fewer function calls
magnusrand Nov 10, 2021
4d0f512
tavlen -> tavla
magnusrand Nov 10, 2021
c0c7c7a
Rewrite logical expressions
magnusrand Nov 10, 2021
bb50848
Re-add 'type'
magnusrand Nov 10, 2021
73c1227
Rewrite AddNewOwnersInput for subcollection-invites
magnusrand Nov 12, 2021
fad8abb
Rewrite BoardOwnerList to work with sub-collection invites
magnusrand Nov 15, 2021
7882409
Make sharedBoards display boards from sub-collection
magnusrand Nov 16, 2021
39ae2bf
Rewrite invite accept to work with subcollections and clean-up code
magnusrand Nov 18, 2021
3c15891
Make custom-url work with new subcollections
magnusrand Nov 18, 2021
e6bfb02
Remove ownerRequestRecipients and ownerRequests
magnusrand Nov 18, 2021
5a13c6e
Make shareTab lock work correctly again
magnusrand Nov 18, 2021
0b8a1c7
Add time shared to sharedBoardCard and move createTimeString to utils
magnusrand Nov 18, 2021
213c658
Fix PR comments
magnusrand Nov 18, 2021
e50d14c
Update days of the week to include "dag"
magnusrand Nov 19, 2021
0a8ca45
Update firestore security rules to cover new invite subcollection
magnusrand Nov 19, 2021
d2d1812
Refactor variables
magnusrand Nov 19, 2021
e5a6cb1
Change order of functions when fetching boards
magnusrand Nov 19, 2021
dec4729
Merge branch 'master' into add-more-owners
magnusrand Nov 19, 2021
b9bab8c
Fix typo
magnusrand Nov 19, 2021
3130b78
Remove OwnerRequest type
magnusrand Nov 19, 2021
003f168
Merge branch 'master' into add-more-owners
magnusrand Nov 22, 2021
13ee9e1
FIx typo and other PR comments
magnusrand Nov 22, 2021
9507fc1
Add client security checking for user owning invite
magnusrand Nov 23, 2021
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
41 changes: 30 additions & 11 deletions firestore.rules
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
rules_version = '2';

service cloud.firestore {
match /databases/{database}/documents/settings/{document} {
function hasValidDescription() {
return !('description' in request.resource.data) || request.resource.data.description.size() <= 50;
}

function hasEditAccess() {
return request.auth != null && (resource.data.owners.size() == 0 || !('owners' in resource.data)
|| resource.data.owners.hasAny([request.auth.uid]));
match /databases/{database}/documents {
match /settings/{document} {
function hasValidDescription() {
return !('description' in request.resource.data) || request.resource.data.description.size() <= 50;
}

function hasEditAccess() {
return request.auth != null && (resource.data.owners.size() == 0 || !('owners' in resource.data)
|| resource.data.owners.hasAny([request.auth.uid]));
}

allow read;
allow create: if request.auth != null && hasValidDescription();
allow update: if request.auth != null && hasEditAccess() && hasValidDescription();
allow delete: if request.auth != null && hasEditAccess();

// This match applies to invites fetched for a specific board
match /invites/{invite} {
function settingsData() {
return get(/databases/$(database)/documents/settings/$(document)).data
}

allow read, create, delete: if settingsData().owners.hasAny([request.auth.uid]);
allow delete: if resource.data.reciever == request.auth.token.email;
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
}
}

allow read;
allow create: if request.auth != null && hasValidDescription();
allow update: if request.auth != null && hasEditAccess() && hasValidDescription();
allow delete: if request.auth != null && hasEditAccess();
// This match applies to invites fetched through a collection group query
match /{somePath=**}/invites/{invite} {
allow read: if resource.data.reciever == request.auth.token.email;
}

}
}
87 changes: 87 additions & 0 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,90 @@ export const scheduledDeleteOfDocumentsSetToBeDeleted = region('us-central1')
}
return null
})

export const getOwnersDataByBoardIdAsOwner = https.onCall(
async (data, context) => {
if (!context.auth) {
throw new https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.',
)
}

const boardRef = getFirestore().collection('settings').doc(data.boardId)
const boardSnapshot = await boardRef.get()
if (!boardSnapshot.exists) {
throw new https.HttpsError(
'invalid-argument',
'Requested board was not found.',
)
}
const boardOwners = boardSnapshot.data()?.owners
if (!boardOwners.includes(context.auth?.uid)) {
throw new https.HttpsError(
'permission-denied',
'You need to be an owner of this board to get data about other owners.',
)
}

const boardOwnersMapped = boardOwners.map((id: string) => ({ uid: id }))

try {
const usersResult = await getAuth().getUsers(boardOwnersMapped)
const ownersData = usersResult.users.map((user) => ({
uid: user.uid,
email: user.email,
}))
return ownersData
} catch {
throw new https.HttpsError(
'invalid-argument',
'Could not get requested data about owners.',
)
}
},
)

export const addInvitedUserToBoardOwners = https.onCall(
async (data, context) => {
if (!context.auth) {
throw new https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.',
)
}

const boardRef = getFirestore().collection('settings').doc(data.boardId)
const boardSnapshot = await boardRef.get()
if (!boardSnapshot.exists) {
throw new https.HttpsError(
'invalid-argument',
'Requested board was not found.',
)
}
const boardOwners = boardSnapshot.data()?.owners

await getFirestore()
.collection('settings/' + data.boardId + '/invites')
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
.where('reciever', '==', context.auth?.token.email)
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
.limit(1)
.get()
.then((inviteDoc) => inviteDoc.docs[0].id)
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
.catch(() => {
throw new https.HttpsError(
'permission-denied',
'Requested user does not have an invite for this board',
)
})

if (!boardOwners.includes(context.auth?.uid)) {
const ownersUpdated: string[] = [...boardOwners, context.auth?.uid]
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
boardRef.update({
owners: ownersUpdated,
})
}

if (data.accept) {
magnusrand marked this conversation as resolved.
Show resolved Hide resolved
}
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { GridContainer, GridItem } from '@entur/grid'
import { PrimaryButton, SecondaryButton } from '@entur/button'
import { useToast } from '@entur/alert'

import CloseButton from '../../../../../components/LoginModal/CloseButton/CloseButton'
import sikkerhetBom from '../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../assets/images/sikkerhet_bom@2x.png'

import sikkerhetBom from '../../../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../../../assets/images/sikkerhet_bom@2x.png'
import { deleteTavle } from '../../settings/FirestoreStorage'

import { deleteTavle } from '../../../../../settings/FirestoreStorage'
import CloseButton from './LoginModal/CloseButton/CloseButton'

import './styles.scss'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { Modal } from '@entur/modal'
import { GridContainer, GridItem } from '@entur/grid'
import { PrimaryButton } from '@entur/button'

import Check from '../../assets/images/check.png'
import retinaCheck from '../../assets/images/check@2x.png'
import Check from '../../../assets/images/check.png'
import retinaCheck from '../../../assets/images/check@2x.png'

import { analytics } from '../../firebase-init'
import { useUser } from '../../auth'
import { useSettingsContext } from '../../settings'
import { analytics } from '../../../firebase-init'
import { useUser } from '../../../auth'
import { useSettingsContext } from '../../../settings'

import CloseButton from '../../components/LoginModal/CloseButton/CloseButton'
import LoginModal from '../../components/LoginModal'
import CloseButton from '../LoginModal/CloseButton/CloseButton'
import LoginModal from '../LoginModal'

import './styles.scss'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../assets/styles/import.scss';
@import '../../../assets/styles/import.scss';

.lock-modal.eds-modal__content {
padding-top: 0.8rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { PrimaryButton } from '@entur/button'
import { Heading3, Link } from '@entur/typography'
import { SmallExpandableAlertBox } from '@entur/alert'

import { auth } from '../../../auth'
import { useFormFields } from '../../../utils'
import sikkerhetBom from '../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../assets/images/sikkerhet_bom@2x.png'
import { auth } from '../../../../auth'
import { useFormFields } from '../../../../utils'
import sikkerhetBom from '../../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../../assets/images/sikkerhet_bom@2x.png'

import { ModalType } from '..'
import CloseButton from '../CloseButton/CloseButton'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Heading3, Paragraph } from '@entur/typography'
import { ModalType } from '..'
import CloseButton from '../CloseButton/CloseButton'

import Check from '../../../assets/images/check.png'
import retinaCheck from '../../../assets/images/check@2x.png'
import Check from '../../../../assets/images/check.png'
import retinaCheck from '../../../../assets/images/check@2x.png'

export interface UserResetPassword {
email: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { PrimaryButton, SecondaryButton } from '@entur/button'

import { ModalType, LoginCase } from '..'

import sikkerhetBom from '../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../assets/images/sikkerhet_bom@2x.png'
import sikkerhetBom from '../../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../../assets/images/sikkerhet_bom@2x.png'

interface Props {
setModalType: Dispatch<SetStateAction<ModalType>>
Expand All @@ -26,6 +26,8 @@ const description = (loginCase: LoginCase): string => {
return 'For å sette en personlig Tavla-lenke til avgangstavla, må du ha en konto.'
case 'error':
return 'For å redigere denne tavla, må du først logge inn på kontoen den tilhører.'
case 'share':
return 'For å dele en tavle med andre, må du eie den og være logget inn.'
default:
return 'Logg inn for å få tilgang på ekstra funksjonalitet, som å se dine tavler eller last opp logo.'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { EmailIcon, BackArrowIcon } from '@entur/icons'
import { PrimaryButton } from '@entur/button'
import { Heading3, Paragraph } from '@entur/typography'

import { auth } from '../../../auth'
import { useFormFields } from '../../../utils'
import sikkerhetBom from '../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../assets/images/sikkerhet_bom@2x.png'
import { auth } from '../../../../auth'
import { useFormFields } from '../../../../utils'
import sikkerhetBom from '../../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../../assets/images/sikkerhet_bom@2x.png'
import { ModalType } from '..'

import CloseButton from '../CloseButton/CloseButton'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { EmailIcon, ClosedLockIcon, BackArrowIcon } from '@entur/icons'
import { PrimaryButton } from '@entur/button'
import { Heading2, Link } from '@entur/typography'

import { auth } from '../../../auth'
import { useFormFields } from '../../../utils'
import sikkerhetBom from '../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../assets/images/sikkerhet_bom@2x.png'
import { auth } from '../../../../auth'
import { useFormFields } from '../../../../utils'
import sikkerhetBom from '../../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../../assets/images/sikkerhet_bom@2x.png'

import { ModalType } from '../index'
import CloseButton from '../CloseButton/CloseButton'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import type { User } from 'firebase/auth'
import { useToast } from '@entur/alert'
import { Modal } from '@entur/modal'

import { useUser } from '../../auth'
import { usePrevious } from '../../utils'
import { useUser } from '../../../auth'
import { usePrevious } from '../../../utils'

import EmailLogin from './EmailLogin'
import LoginOptions from './LoginOptions'
Expand All @@ -22,6 +22,7 @@ export type LoginCase =
| 'mytables'
| 'logo'
| 'link'
| 'share'
| 'error'
| 'default'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import './../../assets/styles/import.scss';
@import '../../../assets/styles/import.scss';

.eds-modal__overlay {
background: var(--tavla-modal-overlay-color);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import { Heading3, Paragraph } from '@entur/typography'
import { GridContainer, GridItem } from '@entur/grid'
import { PrimaryButton, SecondaryButton } from '@entur/button'

import { analytics } from '../../firebase-init'
import { useUser } from '../../auth'
import { useSettingsContext } from '../../settings'
import { analytics } from '../../../firebase-init'
import { useUser } from '../../../auth'
import { useSettingsContext } from '../../../settings'

import LoginModal from '../../components/LoginModal'
import CloseButton from '../../components/LoginModal/CloseButton/CloseButton'
import LoginModal from '../LoginModal'
import CloseButton from '../LoginModal/CloseButton/CloseButton'

import sikkerhetBom from '../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../assets/images/sikkerhet_bom@2x.png'
import sikkerhetBom from '../../../assets/images/sikkerhet_bom.png'
import retinaSikkerhetBom from '../../../assets/images/sikkerhet_bom@2x.png'

import { getDocumentId } from '../../utils'
import { getDocumentId } from '../../../utils'

import './styles.scss'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../assets/styles/import.scss';
@import '../../../assets/styles/import.scss';

.mine-tavler-modal.eds-modal__content {
padding: 0.8rem 1.5rem 1.5rem;
Expand Down
Loading