race condition security patch
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { Comment } from "$lib/models/comment";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { currentUser } from "./user_store";
|
||||
|
||||
export const comments: Writable<Comment[]> = writable([])
|
||||
|
||||
@@ -28,11 +28,12 @@ export async function comments_index(trail: Trail) {
|
||||
}
|
||||
|
||||
export async function comments_create(comment: Comment) {
|
||||
if (!pb.authStore.record) {
|
||||
throw new Error("Unauthenticated");
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
comment.author = pb.authStore.record!.id;
|
||||
comment.author = user.authStore.record!.id;
|
||||
|
||||
let r = await fetch('/api/v1/comment', {
|
||||
method: 'PUT',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Integration } from "$lib/models/integration";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { currentUser } from "./user_store";
|
||||
|
||||
export const integrations: Writable<Integration[]> = writable([])
|
||||
|
||||
@@ -25,11 +25,12 @@ export async function integrations_index(f: (url: RequestInfo | URL, config?: Re
|
||||
}
|
||||
|
||||
export async function integrations_create(integration: Integration) {
|
||||
if (!pb.authStore.record) {
|
||||
throw new Error("Unauthenticated");
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
integration.user = pb.authStore.record!.id;
|
||||
integration.user = user.id;
|
||||
|
||||
let r = await fetch('/api/v1/integration', {
|
||||
method: 'PUT',
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { List, type ListFilter, ExpandType, ExpandTypeToString } from "$lib/models/list";
|
||||
import { ExpandType, ExpandTypeToString, List, type ListFilter } from "$lib/models/list";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { fetchGPX } from "./trail_store";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import type { Hits } from "meilisearch";
|
||||
import { type AuthRecord, type ListResult } from "pocketbase";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { fetchGPX } from "./trail_store";
|
||||
import { currentUser } from "./user_store";
|
||||
|
||||
let lists: List[] = []
|
||||
export const list: Writable<List | null> = writable(null)
|
||||
export const listTrail: Writable<Trail | null> = writable(null);
|
||||
|
||||
export async function lists_index(filter?: ListFilter, page: number = 1, perPage: number = 5,
|
||||
export async function lists_index(filter?: ListFilter, page: number = 1, perPage: number = 5,
|
||||
f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch,
|
||||
e: ExpandType = ExpandType.All) {
|
||||
const filterText = filter ? buildFilterText(filter) : ""
|
||||
const user = get(currentUser)
|
||||
|
||||
const filterText = filter ? buildFilterText(user, filter) : ""
|
||||
|
||||
const r = await f('/api/v1/list?' + new URLSearchParams({
|
||||
sort: `${filter?.sortOrder ?? "-"}${filter?.sort ?? "name"}`,
|
||||
@@ -42,8 +44,9 @@ export async function lists_index(filter?: ListFilter, page: number = 1, perPage
|
||||
|
||||
|
||||
export async function lists_search_filter(filter: ListFilter, page: number = 1, perPage: number = 5, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch): Promise<ListResult<List>> {
|
||||
const user = get(currentUser)
|
||||
|
||||
const filterText = buildSearchFilterText(filter)
|
||||
const filterText = buildSearchFilterText(user, filter)
|
||||
|
||||
let r = await f("/api/v1/search/lists", {
|
||||
method: "POST",
|
||||
@@ -96,7 +99,7 @@ export async function lists_search_filter(filter: ListFilter, page: number = 1,
|
||||
|
||||
export async function lists_show(id: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch
|
||||
, e: ExpandType = ExpandType.All) {
|
||||
|
||||
|
||||
const r = await f(`/api/v1/list/${id}?` + new URLSearchParams({
|
||||
expand: ExpandTypeToString(e)
|
||||
}), {
|
||||
@@ -125,11 +128,11 @@ export async function lists_show(id: string, f: (url: RequestInfo | URL, config?
|
||||
}
|
||||
|
||||
export async function lists_create(list: List, avatar?: File) {
|
||||
if (!pb.authStore.record) {
|
||||
throw new Error("Unauthenticated");
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
list.author = pb.authStore.record!.id;
|
||||
list.author = user.id;
|
||||
|
||||
let r = await fetch('/api/v1/list', {
|
||||
method: 'PUT',
|
||||
@@ -240,24 +243,24 @@ export async function lists_delete(list: List) {
|
||||
}
|
||||
|
||||
|
||||
function buildFilterText(filter: ListFilter): string {
|
||||
function buildFilterText(user: AuthRecord | undefined, filter: ListFilter): string {
|
||||
let filterText = `(name~"${filter.q}"||description~"${filter.q}")`
|
||||
if (filter.author?.length) {
|
||||
filterText += `&&author="${filter.author}"`
|
||||
}
|
||||
if (pb.authStore.record) {
|
||||
if (user) {
|
||||
if (filter.public === false && filter.shared === false) {
|
||||
filterText += `&&author="${pb.authStore.record.id}"`
|
||||
filterText += `&&author="${user.id}"`
|
||||
} else if (filter.public === true && filter.shared === false) {
|
||||
filterText += `&&(public=true||list_share_via_list.user!="${pb.authStore.record.id}"||author="${pb.authStore.record.id}")`
|
||||
filterText += `&&(public=true||list_share_via_list.user!="${user.id}"||author="${user.id}")`
|
||||
} else if (filter.public === false && filter.shared === true) {
|
||||
filterText += `&&(public=false||list_share_via_list.user="${pb.authStore.record.id}"||author="${pb.authStore.record.id}")`
|
||||
filterText += `&&(public=false||list_share_via_list.user="${user.id}"||author="${user.id}")`
|
||||
}
|
||||
}
|
||||
return filterText
|
||||
}
|
||||
|
||||
function buildSearchFilterText(filter: ListFilter): string {
|
||||
function buildSearchFilterText(user: AuthRecord, filter: ListFilter): string {
|
||||
let filterText: string = "";
|
||||
|
||||
if (filter.author?.length) {
|
||||
@@ -272,17 +275,17 @@ function buildSearchFilterText(filter: ListFilter): string {
|
||||
if (filter.public !== undefined) {
|
||||
filterText += `(public = ${filter.public}`
|
||||
|
||||
if (!filter.author?.length || filter.author == pb.authStore.record?.id) {
|
||||
filterText += ` OR author = ${pb.authStore.record?.id}`
|
||||
if (!filter.author?.length || filter.author == user?.id) {
|
||||
filterText += ` OR author = ${user?.id}`
|
||||
}
|
||||
filterText += ")"
|
||||
}
|
||||
|
||||
if (filter.shared !== undefined) {
|
||||
if (filter.shared === true) {
|
||||
filterText += ` OR shares = ${pb.authStore.record?.id}`
|
||||
filterText += ` OR shares = ${user?.id}`
|
||||
} else {
|
||||
filterText += ` AND NOT shares = ${pb.authStore.record?.id}`
|
||||
filterText += ` AND NOT shares = ${user?.id}`
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { SummitLog, type SummitLogFilter } from "$lib/models/summit_log";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { fetchGPX } from "./trail_store";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { type ListResult } from "pocketbase";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { currentUser } from "./user_store";
|
||||
|
||||
export const summitLog: Writable<SummitLog> = writable(new SummitLog(new Date().toISOString().substring(0, 10)));
|
||||
export const summitLogs: Writable<SummitLog[]> = writable([]);
|
||||
@@ -47,7 +46,12 @@ export async function summit_logs_index(author: string, filter?: SummitLogFilter
|
||||
}
|
||||
|
||||
export async function summit_logs_create(summitLog: SummitLog, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
summitLog.author = pb.authStore.record!.id
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
summitLog.author = user.id
|
||||
|
||||
let r = await f('/api/v1/summit-log', {
|
||||
method: 'PUT',
|
||||
@@ -102,7 +106,12 @@ export async function summit_logs_create(summitLog: SummitLog, f: (url: RequestI
|
||||
}
|
||||
|
||||
export async function summit_logs_update(oldSummitLog: SummitLog, newSummitLog: SummitLog) {
|
||||
newSummitLog.author = pb.authStore.record!.id
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
newSummitLog.author = user.id
|
||||
|
||||
let r = await fetch('/api/v1/summit-log/' + newSummitLog.id, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { SummitLog } from "$lib/models/summit_log";
|
||||
import type { Tag } from "$lib/models/tag";
|
||||
import { defaultTrailSearchAttributes, Trail, type TrailFilter, type TrailFilterValues, type TrailSearchResult } from "$lib/models/trail";
|
||||
import type { Waypoint } from "$lib/models/waypoint";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { deepEqual } from "$lib/util/deep_util";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import * as M from "maplibre-gl";
|
||||
import type { Hits } from "meilisearch";
|
||||
import { type ListResult, type RecordModel } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { type AuthRecord, type ListResult, type RecordModel } from "pocketbase";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store";
|
||||
import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { tags_create } from "./tag_store";
|
||||
import type { Tag } from "$lib/models/tag";
|
||||
import { currentUser } from "./user_store";
|
||||
import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store";
|
||||
|
||||
let trails: Trail[] = []
|
||||
export const trail: Writable<Trail> = writable(new Trail(""));
|
||||
@@ -57,7 +57,9 @@ export async function trails_recommend(size: number, f: (url: RequestInfo | URL,
|
||||
}
|
||||
|
||||
export async function trails_search_filter(filter: TrailFilter, page: number = 1, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
let filterText: string = buildFilterText(filter, true);
|
||||
const user = get(currentUser)
|
||||
|
||||
let filterText: string = buildFilterText(user, filter, true);
|
||||
|
||||
let r = await f("/api/v1/search/trails", {
|
||||
method: "POST",
|
||||
@@ -91,11 +93,12 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
|
||||
}
|
||||
|
||||
export async function trails_search_bounding_box(northEast: M.LngLat, southWest: M.LngLat, filter?: TrailFilter, page: number = 1, includePolyline: boolean = true) {
|
||||
const user = get(currentUser)
|
||||
|
||||
let filterText: string = "";
|
||||
|
||||
if (filter) {
|
||||
filterText = buildFilterText(filter, false);
|
||||
filterText = buildFilterText(user, filter, false);
|
||||
}
|
||||
|
||||
let r = await fetch("/api/v1/search/trails", {
|
||||
@@ -173,6 +176,10 @@ export async function trails_show(id: string, loadGPX?: boolean, f: (url: Reques
|
||||
}
|
||||
|
||||
export async function trails_create(trail: Trail, photos: File[], gpx: File | Blob | null, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
for (const waypoint of trail.expand?.waypoints ?? []) {
|
||||
const model = await waypoints_create({
|
||||
@@ -194,7 +201,7 @@ export async function trails_create(trail: Trail, photos: File[], gpx: File | Bl
|
||||
}
|
||||
}
|
||||
|
||||
trail.author = pb.authStore.record!.id
|
||||
trail.author = user.id
|
||||
|
||||
let r = await f(`/api/v1/trail?` + new URLSearchParams({
|
||||
expand: "category,waypoints,summit_logs,trail_share_via_trail,tags",
|
||||
@@ -500,7 +507,7 @@ async function searchResultToTrailList(hits: Hits<TrailSearchResult>): Promise<T
|
||||
return trails
|
||||
}
|
||||
|
||||
function buildFilterText(filter: TrailFilter, includeGeo: boolean): string {
|
||||
function buildFilterText(user: AuthRecord, filter: TrailFilter, includeGeo: boolean): string {
|
||||
let filterText: string = "";
|
||||
|
||||
filterText += `distance >= ${Math.floor(filter.distanceMin)} AND elevation_gain >= ${Math.floor(filter.elevationGainMin)} AND elevation_loss >= ${Math.floor(filter.elevationLossMin)}`
|
||||
@@ -530,17 +537,17 @@ function buildFilterText(filter: TrailFilter, includeGeo: boolean): string {
|
||||
if (filter.public !== undefined) {
|
||||
filterText += `(public = ${filter.public}`
|
||||
|
||||
if (!filter.author?.length || filter.author == pb.authStore.record?.id) {
|
||||
filterText += ` OR author = ${pb.authStore.record?.id}`
|
||||
if (!filter.author?.length || filter.author == user?.id) {
|
||||
filterText += ` OR author = ${user?.id}`
|
||||
}
|
||||
filterText += ")"
|
||||
}
|
||||
|
||||
if (filter.shared !== undefined) {
|
||||
if (filter.shared === true) {
|
||||
filterText += ` OR shares = ${pb.authStore.record?.id}`
|
||||
filterText += ` OR shares = ${user?.id}`
|
||||
} else {
|
||||
filterText += ` AND NOT shares = ${pb.authStore.record?.id}`
|
||||
filterText += ` AND NOT shares = ${user?.id}`
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { User, UserAnonymous } from "$lib/models/user";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { getPb } from "$lib/pocketbase";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { type AuthMethodsList } from "pocketbase";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
|
||||
export const currentUser: Writable<User | null> = writable<User | null>()
|
||||
|
||||
@@ -24,8 +24,10 @@ export async function users_create(user: User) {
|
||||
}
|
||||
|
||||
export async function users_search(q: string, includeSelf: boolean = true) {
|
||||
const user = get(currentUser)
|
||||
|
||||
let r = await fetch('/api/v1/user/anonymous?' + new URLSearchParams({
|
||||
"filter": `username~"${q}"${includeSelf ? '' : `&&id!="${pb.authStore.record?.id}"`}`,
|
||||
"filter": `username~"${q}"${includeSelf ? '' : `&&id!="${user?.id}"`}`,
|
||||
}), {
|
||||
method: 'GET',
|
||||
})
|
||||
@@ -69,6 +71,8 @@ export async function users_auth_methods(f: (url: RequestInfo | URL, config?: Re
|
||||
|
||||
|
||||
export async function login(user: User) {
|
||||
const pb = getPb();
|
||||
|
||||
const r = await fetch('/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(user),
|
||||
@@ -83,6 +87,8 @@ export async function login(user: User) {
|
||||
}
|
||||
|
||||
export async function oauth_login(data: { name: string, code: string, codeVerifier: string }) {
|
||||
const pb = getPb();
|
||||
|
||||
const r = await fetch('/api/v1/auth/oauth', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data)
|
||||
@@ -98,6 +104,8 @@ export async function oauth_login(data: { name: string, code: string, codeVerifi
|
||||
|
||||
|
||||
export async function logout() {
|
||||
const pb = getPb();
|
||||
|
||||
pb.authStore.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { Waypoint } from "$lib/models/waypoint";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { APIError } from "$lib/util/api_util";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { currentUser } from "./user_store";
|
||||
|
||||
export const waypoint: Writable<Waypoint> = writable(new Waypoint(0, 0));
|
||||
|
||||
export async function waypoints_create(waypoint: Waypoint, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
|
||||
waypoint.author = pb.authStore.record!.id
|
||||
waypoint.author = user.id
|
||||
|
||||
let r = await f('/api/v1/waypoint', {
|
||||
method: 'PUT',
|
||||
@@ -45,7 +49,11 @@ export async function waypoints_create(waypoint: Waypoint, f: (url: RequestInfo
|
||||
}
|
||||
|
||||
export async function waypoints_update(oldWaypoint: Waypoint, newWaypoint: Waypoint) {
|
||||
newWaypoint.author = pb.authStore.record!.id
|
||||
const user = get(currentUser)
|
||||
if (!user) {
|
||||
throw Error("Unauthenticated")
|
||||
}
|
||||
newWaypoint.author = user.id
|
||||
|
||||
let r = await fetch('/api/v1/waypoint/' + newWaypoint.id, {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user