Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions components/db/testimony/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ export const resolveReport = httpsCallable<report.Request, report.Response>(
functions,
"adminResolveReport"
)
export const resolveReportv2 = httpsCallable<report.Request, report.Response>(
functions,
"adminResolveReportv2"
)
3 changes: 2 additions & 1 deletion components/moderation/ListProfiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Internal } from "components/links"
import { ButtonGroup } from "@mui/material"
import { Role } from "components/auth"
import { createFakeOrg } from "components/moderation"
import { createFakeOrgv2 } from "components/moderation"
import { loremIpsum } from "lorem-ipsum"
import { nanoid } from "nanoid"

Expand All @@ -46,7 +47,7 @@ const UserRoleToolBar = () => {
const fullName = loremIpsum({ count: 2, units: "words" })
const email = `${uid}@example.com`

await createFakeOrg({ uid, fullName, email })
await createFakeOrgv2({ uid, fullName, email })

if (filterValues["role"] === "organization")
setFilters({ role: "pendingUpgrade" }, [])
Expand Down
3 changes: 2 additions & 1 deletion components/moderation/RemoveTestimony.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Card, CardContent, CardHeader, Stack } from "@mui/material"
import { deleteTestimony } from "components/api/delete-testimony"
import { resolveReport } from "components/db"
import { resolveReportv2 } from "components/db"
import { getAuth } from "firebase/auth"
import { doc, getDoc } from "firebase/firestore"
import { Timestamp } from "functions/src/firebase"
Expand All @@ -24,7 +25,7 @@ export const onSubmitReport = async (
testimonyId: string,
refresh: () => void
) => {
const r = await resolveReport({
const r = await resolveReportv2({
reportId,
resolution,
reason
Expand Down
3 changes: 2 additions & 1 deletion components/moderation/setUp/CreateMockReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useReportTestimony } from "components/api/report"
import { Testimony } from "components/db"
import { auth, firestore } from "components/firebase"
import { createFakeTestimony } from "components/moderation"
import { createFakeTestimonyv2 } from "components/moderation"
import { doc, getDoc } from "firebase/firestore"
import { loremIpsum } from "lorem-ipsum"
import { nanoid } from "nanoid"
Expand All @@ -21,7 +22,7 @@ export const CreateMockReport = () => {
const fullName = loremIpsum({ count: 2, units: "words" })
const email = `${uid}@example.com`

const result = await createFakeTestimony({
const result = await createFakeTestimonyv2({
uid,
fullName,
email
Expand Down
13 changes: 13 additions & 0 deletions components/moderation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export const modifyAccount = httpsCallable<{ uid: string; role: Role }, void>(
functions,
"modifyAccount"
)
export const modifyAccountv2 = httpsCallable<{ uid: string; role: Role }, void>(
functions,
"modifyAccountv2"
)

type Request = { uid: string; fullName: string; email: string }
type Response = { uid: string; tid: string }
Expand All @@ -41,7 +45,16 @@ export const createFakeOrg = httpsCallable<Request, void>(
"createFakeOrg"
)

export const createFakeOrgv2 = httpsCallable<Request, void>(
functions,
"createFakeOrg"
)

export const createFakeTestimony = httpsCallable<Request, Response>(
functions,
"createFakeTestimony"
)
export const createFakeTestimonyv2 = httpsCallable<Request, Response>(
functions,
"createFakeTestimonyv2"
)
31 changes: 30 additions & 1 deletion functions/src/auth/createFakeOrg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as functions from "firebase-functions"
import { checkAdmin, checkAuth } from "../common"
import { checkAdmin, checkAdminv2, checkAuth, checkAuthv2 } from "../common"
import { auth, db } from "../firebase"
import { onCall, CallableRequest } from "firebase-functions/v2/https"

// for populating admin module for testing & demonstration
//@TODO: remove
Expand Down Expand Up @@ -32,3 +33,31 @@ export const createFakeOrg = functions.https.onCall(async (data, context) => {

return { ...authUser, uid: userRecord.uid }
})

export const createFakeOrgv2 = onCall(async (request: CallableRequest) => {
checkAuthv2(request, false)
checkAdminv2(request)

const { uid, fullName, email } = request.data

const newUser = {
uid,
fullName,
email,
password: "password",
public: true,
role: "pendingUpgrade"
}

const role = "pendingUpgrade"
const userRecord = await auth.createUser(newUser)

await auth.setCustomUserClaims(newUser.uid, { role })
await db.doc(`/profiles/${newUser.uid}`).set(newUser)

const authUser = (await db.doc(`/profiles/${newUser.uid}`).get()).data()

console.log(authUser)

return { ...authUser, uid: userRecord.uid }
})
53 changes: 51 additions & 2 deletions functions/src/auth/createFakeTestimony.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as functions from "firebase-functions"
import { checkAdmin, checkAuth } from "../common"
import * as functions from "firebase-functions/v1"
import { checkAdmin, checkAdminv2, checkAuth, checkAuthv2 } from "../common"
import { auth, db } from "../firebase"
import { Testimony } from "../testimony/types"
import { Timestamp } from "../firebase"
import { onCall, CallableRequest } from "firebase-functions/v2/https"

// for populating admin module for testing & demonstration--alert--no auth checked here.
//@TODO: remove
Expand Down Expand Up @@ -54,3 +55,51 @@ export const createFakeTestimony = functions.https.onCall(
return { uid: uid, tid: id }
}
)

export const createFakeTestimonyv2 = onCall(
async (request: CallableRequest) => {
console.log("running fake testimony")
checkAuthv2(request, false)
checkAdminv2(request)

const { uid, fullName, email } = request.data

const author = {
uid,
fullName,
email,
password: "password",
public: true,
role: "user"
}

await auth.createUser({ uid })

await db.doc(`profiles/${uid}`).set(author)

const id = `${uid}ttmny`

const testimony: Testimony = {
id,
authorUid: author.uid,
authorDisplayName: "none",
authorRole: "user",
billTitle: "An act",
version: 2,
billId: "H1002",
publishedAt: Timestamp.now(),
court: 192,
position: "oppose",
fullName: fullName,
content: fullName + " " + fullName + " " + fullName + " " + fullName,
public: true,
updatedAt: Timestamp.now()
}

const testRef = db.doc(`users/${uid}/publishedTestimony/${id}`)

await testRef.set(testimony)

return { uid: uid, tid: id }
}
)
20 changes: 19 additions & 1 deletion functions/src/auth/modifyAccount.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as functions from "firebase-functions"
import { db, auth } from "../firebase"
import { z } from "zod"
import { checkRequestZod, checkAuth, checkAdmin } from "../common"
import {
checkRequestZod,
checkAuth,
checkAdmin,
checkAuthv2,
checkAdminv2
} from "../common"
import { setRole } from "."
import { onCall, CallableRequest } from "firebase-functions/v2/https"

import { ZRole } from "./types"

Expand All @@ -21,3 +28,14 @@ export const modifyAccount = functions.https.onCall(async (data, context) => {

await setRole({ role, auth, db, uid })
})

export const modifyAccountv2 = onCall(async (request: CallableRequest) => {
checkAuthv2(request, false)
checkAdminv2(request)

const { uid, role } = checkRequestZod(Request, request.data)

console.log(`Setting role for ${uid} to ${role}`)

await setRole({ role, auth, db, uid })
})
46 changes: 43 additions & 3 deletions functions/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { FieldValue } from "@google-cloud/firestore"
import axios from "axios"
import { https, logger } from "firebase-functions"
import { CallableRequest } from "firebase-functions/v2/https"
import {
CallableRequest,
HttpsError as HttpsErrorV2
} from "firebase-functions/v2/https"
import {
Null,
Nullish,
Expand Down Expand Up @@ -39,7 +42,7 @@ export function checkRequestZod<T extends ZodTypeAny>(

/** Return the authenticated user's id or fail if they are not authenticated. */
export function checkAuth(
context: https.CallableContext | CallableRequest,
context: https.CallableContext,
checkEmailVerification = false
) {
const uid = context.auth?.uid
Expand All @@ -59,21 +62,58 @@ export function checkAuth(
return uid
}

/** Return the authenticated user's id or fail if they are not authenticated. (Firebase v2 compatible) */
export function checkAuthv2(
request: CallableRequest,
checkEmailVerification = false
) {
const uid = request.auth?.uid

if (!uid) {
throw failv2("unauthenticated", "Caller must be signed in")
}

if (checkEmailVerification && process.env.FUNCTIONS_EMULATOR !== "true") {
const email_verified = request.auth?.token?.email_verified

if (!email_verified) {
throw failv2("permission-denied", "You must verify an account first")
}
}

return uid
}

/**
* Checks that the caller is an admin.
*/
export function checkAdmin(context: https.CallableContext | CallableRequest) {
export function checkAdmin(context: https.CallableContext) {
const callerRole = context.auth?.token.role
if (callerRole !== "admin") {
throw fail("permission-denied", "You must be an admin")
}
}

/**
* Checks that the caller is an admin. (Firebase v2 compatible)
*/
export function checkAdminv2(request: CallableRequest) {
const callerRole = request.auth?.token.role
if (callerRole !== "admin") {
throw failv2("permission-denied", "You must be an admin")
}
}

/** Constructs a new HTTPS error */
export function fail(code: https.FunctionsErrorCode, message: string) {
return new https.HttpsError(code, message)
}

/** Constructs a new HTTPS error (Firebase v2 compatible) */
export function failv2(code: string, message: string) {
return new HttpsErrorV2(code as any, message)
}

/** Catch handler to log axios errors and return undefined. */
export const logFetchError = (label: string, id?: string) => (e: any) => {
if (axios.isAxiosError(e)) {
Expand Down
12 changes: 9 additions & 3 deletions functions/src/events/scrapeEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { onCall, CallableRequest } from "firebase-functions/v2/https"
import { DateTime } from "luxon"
import { JSDOM } from "jsdom"
import { AssemblyAI } from "assemblyai"
import { checkAuth, checkAdmin, logFetchError } from "../common"
import {
checkAuth,
checkAdmin,
logFetchError,
checkAdminv2,
checkAuthv2
} from "../common"
import { db, storage, Timestamp } from "../firebase"
import * as api from "../malegislature"
import {
Expand Down Expand Up @@ -482,8 +488,8 @@ export const scrapeSingleHearingv2 = onCall(
async (request: CallableRequest) => {
// Require admin authentication
// Check how to integrate the new object with these helper functions
checkAuth(request, false)
checkAdmin(request)
checkAuthv2(request, false)
checkAdminv2(request)

const { eventId } = request.data

Expand Down
12 changes: 10 additions & 2 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export { modifyAccount, createFakeOrg, createFakeTestimony } from "./auth"
export {
modifyAccount,
modifyAccountv2,
createFakeOrg,
createFakeOrgv2,
createFakeTestimony,
createFakeTestimonyv2
} from "./auth"
export {
backfillTestimonyCounts,
fetchBillBatch,
Expand Down Expand Up @@ -38,7 +45,8 @@ export {
publishTestimony,
syncTestimonyToSearchIndex,
upgradeTestimonySearchIndex,
resolveReport as adminResolveReport
resolveReport as adminResolveReport,
resolveReportv2 as adminResolveReportv2
} from "./testimony"
export {
publishNotifications,
Expand Down
Loading
Loading