webdav fix

This commit is contained in:
2025-10-31 20:10:42 +01:00
parent 175387f0d3
commit e9731ec691
9 changed files with 110 additions and 8 deletions
@@ -0,0 +1,5 @@
DROP POLICY IF EXISTS tenant_webdav_token_policy ON tenant.webdav_tokens;
ALTER TABLE tenant.webdav_tokens NO FORCE ROW LEVEL SECURITY;
ALTER TABLE tenant.webdav_tokens DISABLE ROW LEVEL SECURITY;
DROP FUNCTION IF EXISTS shared.current_webdav_token_prefix();
@@ -0,0 +1,15 @@
CREATE OR REPLACE FUNCTION shared.current_webdav_token_prefix() RETURNS text AS $$
SELECT NULLIF(current_setting('papercrate.webdav_token_prefix', true), '')
$$ LANGUAGE SQL STABLE;
ALTER TABLE tenant.webdav_tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE tenant.webdav_tokens FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_webdav_token_policy ON tenant.webdav_tokens
USING (
tenant_id = shared.current_tenant_id()
OR (
shared.current_webdav_token_prefix() IS NOT NULL
AND token_prefix = shared.current_webdav_token_prefix()
)
)
WITH CHECK (tenant_id = shared.current_tenant_id());
+6 -1
View File
@@ -13,6 +13,7 @@ use crate::{
models::{NewWebdavToken, WebdavToken},
schema::webdav_tokens,
state::PgPooledConnection,
tenants::{apply_webdav_token_prefix, clear_webdav_token_prefix},
};
const TOKEN_PREFIX_LENGTH: usize = 12;
@@ -84,6 +85,7 @@ pub fn find_active_token_by_secret(
}
let prefix = &secret[..TOKEN_PREFIX_LENGTH];
apply_webdav_token_prefix(conn, prefix)?;
let mut query = webdav_tokens::table
.filter(webdav_tokens::user_id.eq(user_id))
.filter(webdav_tokens::token_prefix.eq(prefix))
@@ -101,7 +103,10 @@ pub fn find_active_token_by_secret(
query = query.filter(webdav_tokens::tenant_id.eq(tenant_id));
}
let candidates = query.load::<WebdavToken>(conn)?;
let load_result = query.load::<WebdavToken>(conn);
let clear_result = clear_webdav_token_prefix(conn);
clear_result?;
let candidates = load_result?;
for token in candidates {
if verify_token_secret(secret, &token.token_hash)? {
+17 -1
View File
@@ -148,7 +148,8 @@ pub fn clear_tenant_context(conn: &mut PgConnection) -> AppResult<()> {
"SELECT \
set_config('papercrate.tenant_id', '', false), \
set_config('papercrate.user_id', '', false), \
set_config('papercrate.refresh_token_hash', '', false)"
set_config('papercrate.refresh_token_hash', '', false), \
set_config('papercrate.webdav_token_prefix', '', false)"
)
.execute(conn)
.map(|_| ())
@@ -177,6 +178,21 @@ pub fn clear_refresh_token_hash(conn: &mut PgConnection) -> AppResult<()> {
.map_err(AppError::from)
}
pub fn apply_webdav_token_prefix(conn: &mut PgConnection, prefix: &str) -> AppResult<()> {
diesel::sql_query("SELECT set_config('papercrate.webdav_token_prefix', $1, false)")
.bind::<Text, _>(prefix)
.execute(conn)
.map(|_| ())
.map_err(AppError::from)
}
pub fn clear_webdav_token_prefix(conn: &mut PgConnection) -> AppResult<()> {
diesel::sql_query("SELECT set_config('papercrate.webdav_token_prefix', '', false)")
.execute(conn)
.map(|_| ())
.map_err(AppError::from)
}
fn normalize_storage_root(raw: Option<&str>, tenant_id: Uuid) -> String {
match raw.map(str::trim) {
Some(root) if !root.is_empty() => {
+13
View File
@@ -57,6 +57,19 @@ impl JobHandler for ProvisionTenantJob {
}
};
drop(conn);
let mut conn = match state.db_for_tenant(tenant.id) {
Ok(conn) => conn,
Err(err) => {
warn!(job_id = %job.id, error = ?err, "failed to scope connection for tenant provisioning");
return JobExecution::Retry {
delay: std::time::Duration::from_secs(30),
error: "tenant connection unavailable".into(),
};
}
};
if tenant.status == TenantStatus::Active {
warn!(
job_id = %job.id,
+4 -2
View File
@@ -3,8 +3,10 @@
{{- $config := dict "databaseUrl" .Values.backend.env.databaseUrl -}}
{{- if not (index $config "databaseUrl") -}}
{{- if .Values.postgres.enabled -}}
{{- $dbUser := required "postgres.auth.username is required when postgres.enabled" .Values.postgres.auth.username -}}
{{- $dbPass := required "postgres.auth.password is required when postgres.enabled" .Values.postgres.auth.password -}}
{{- $appUser := default .Values.postgres.auth.username .Values.postgres.auth.appUsername -}}
{{- $appPass := default .Values.postgres.auth.password .Values.postgres.auth.appPassword -}}
{{- $dbUser := required "postgres auth appUsername/password (or username/password) must be provided when postgres.enabled" $appUser -}}
{{- $dbPass := required "postgres auth appUsername/password (or username/password) must be provided when postgres.enabled" $appPass -}}
{{- $dbName := default "papercrate" .Values.backend.env.databaseName -}}
{{- $dbHost := printf "%s-postgres" (include "papercrate.fullname" .) -}}
{{- $_ := set $config "databaseUrl" (printf "postgres://%s:%s@%s:5432/%s" $dbUser $dbPass $dbHost $dbName) -}}
@@ -17,6 +17,44 @@ data:
-c "CREATE DATABASE \"{{ $name }}\"";
fi
{{- end }}
{{- $appRole := default "" .Values.postgres.auth.appRole }}
{{- $appUser := default "" .Values.postgres.auth.appUsername }}
{{- $appPassword := default "" .Values.postgres.auth.appPassword }}
APP_ROLE="{{ $appRole }}"
APP_USER="{{ $appUser }}"
APP_PASSWORD="{{ $appPassword }}"
APP_DATABASE="{{ default "papercrate" .Values.backend.env.databaseName }}"
if [ -n "$APP_ROLE" ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<-EOSQL
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '$APP_ROLE') THEN
CREATE ROLE "$APP_ROLE" NOLOGIN;
END IF;
END
$$;
EOSQL
fi
if [ -n "$APP_USER" ] && [ -n "$APP_PASSWORD" ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<-EOSQL
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '$APP_USER') THEN
CREATE ROLE "$APP_USER" LOGIN PASSWORD '$APP_PASSWORD';
ELSE
ALTER ROLE "$APP_USER" WITH LOGIN PASSWORD '$APP_PASSWORD';
END IF;
IF '$APP_ROLE' <> '' THEN
GRANT "$APP_ROLE" TO "$APP_USER";
END IF;
END
$$;
GRANT CONNECT ON DATABASE "$APP_DATABASE" TO "$APP_USER";
GRANT USAGE ON SCHEMA public TO "$APP_USER";
ALTER ROLE "$APP_USER" SET search_path = 'tenant, shared, public';
ALTER ROLE "$APP_USER" INHERIT;
EOSQL
fi
if [ -n "$PGDATA" ] && [ -f "$PGDATA/postgresql.conf" ]; then
if grep -q "^max_connections" "$PGDATA/postgresql.conf"; then
sed -i "s/^max_connections.*/max_connections = {{ .Values.postgres.maxConnections | default 200 }}/" "$PGDATA/postgresql.conf"
@@ -35,10 +35,7 @@ spec:
{{- fail "quickwit.env.storageUri must be set, or provide global.s3.bucket" -}}
{{- end -}}
{{- if and (not (index $qw "metastoreUri")) .Values.postgres.enabled -}}
{{- $dbUser := required "postgres.auth.username is required when postgres.enabled" .Values.postgres.auth.username -}}
{{- $dbPass := required "postgres.auth.password is required when postgres.enabled" .Values.postgres.auth.password -}}
{{- $dbHost := printf "%s-postgres" (include "papercrate.fullname" .) -}}
{{- $_ := set $qw "metastoreUri" (printf "postgres://%s:%s@%s:5432/quickwit" $dbUser $dbPass $dbHost) -}}
{{- $_ := fail "quickwit.env.metastoreSecret must be provided when using chart-managed Postgres" -}}
{{- end -}}
{{- if not (index $qw "metastoreUri") -}}
{{- fail "quickwit.env.metastoreUri must be set when not using bundled Postgres" -}}
@@ -55,8 +52,16 @@ spec:
env:
- name: QW_STORAGE_URI
value: {{ index $qw "storageUri" | quote }}
{{- if .Values.quickwit.env.metastoreSecret }}
- name: QW_METASTORE_URI
valueFrom:
secretKeyRef:
name: {{ .Values.quickwit.env.metastoreSecret }}
key: metastoreUri
{{- else }}
- name: QW_METASTORE_URI
value: {{ index $qw "metastoreUri" | quote }}
{{- end }}
{{- with .Values.global.s3.endpoint }}
- name: AWS_ENDPOINT_URL
value: {{ . | quote }}
+3
View File
@@ -108,6 +108,9 @@ postgres:
auth:
username: papercrate
password: papercrate_dev
appRole: papercrate_app
appUsername: papercrate_app_login
appPassword: papercrate_app
databases:
papercrate: {}
quickwit: {}