-
Notifications
You must be signed in to change notification settings - Fork 8
Move passwords from tmc #1503
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: master
Are you sure you want to change the base?
Move passwords from tmc #1503
Changes from all commits
5db8f45
7120691
ba1e424
23d2578
e261563
ed671f1
3396bc9
facc79a
0c5f2d5
2e7bfc8
a4e2f4c
1ec83cd
47e770d
08ee405
83a218b
f9c95df
aa3ad19
2ab91f8
33f858b
b602c56
04a5bf9
7c84a09
e2b5c9a
8d647bd
5e4a6f3
1a683e0
c4d5d1a
10f0fa6
e5cfdc4
486e919
1631329
7aa3a9b
95034eb
4b2ad4e
2c62428
2806570
ba0a090
e26b541
47d9001
5e75d21
4f664a2
f7f6136
690357b
17bd8b7
1210b07
c153fb7
b64c495
3a95ec0
df88247
2771ba7
ead3008
9f7c00e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiVersion: apps/v1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kind: Deployment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: email-deliver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: email-deliver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deploymentType: with-init-container | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| needs-db: "true" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spec: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| replicas: 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selector: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| matchLabels: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: email-deliver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| template: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| annotations: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| linkerd.io/inject: enabled | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app: email-deliver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spec: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| containers: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: email-deliver | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: headless-lms | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| command: ["bin/run", "email-deliver"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Pin image (avoid mutable tags) and define imagePullPolicy. Using an untagged image resolves to “latest” and is mutable. Pin to a version or digest to prevent supply-chain drift; set an explicit pull policy. - image: headless-lms
+ image: ghcr.io/your-org/headless-lms@sha256:<digest>
+ imagePullPolicy: IfNotPresentIf you must stay on a mutable tag temporarily: - image: headless-lms
+ image: your-registry/headless-lms:latest
+ imagePullPolicy: Always📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resources: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requests: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memory: 100Mi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu: 20m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limits: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memory: 300Mi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu: 200m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| envFrom: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - secretRef: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: headless-lms-secrets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Maijjay marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initContainers: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+21
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harden container: drop privileges and disallow privilege escalation. Static analysis flags CKV_K8S_20 and CKV_K8S_23 are valid here. The workload has no declared securityContext, so it will likely run as root and with allowPrivilegeEscalation by default. Add a restrictive securityContext to the app container. Apply: - name: email-deliver
image: headless-lms
command: ["bin/run", "email-deliver"]
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop: ["ALL"]
+ seccompProfile:
+ type: RuntimeDefault
resources:📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: headless-lms-wait-for-db | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: headless-lms | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| command: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - bash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - "-c" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo Waiting for postgres to be available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timeout 120 ./wait-for-db.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ./wait-for-db-migrations.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resources: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requests: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memory: 100Mi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu: 20m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limits: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memory: 300Mi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu: 200m | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| envFrom: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - secretRef: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: headless-lms-secrets | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harden initContainer as well. Same principle of least privilege should apply to the init container, otherwise the Pod still violates the checks. - name: headless-lms-wait-for-db
image: headless-lms
command:
- bash
- "-c"
- |
echo Waiting for postgres to be available
timeout 120 ./wait-for-db.sh
./wait-for-db-migrations.sh
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop: ["ALL"]
+ seccompProfile:
+ type: RuntimeDefault
resources:📝 Committable suggestion
Suggested change
🧰 Tools🪛 Checkov (3.2.334)[MEDIUM] 1-54: Containers should not run with allowPrivilegeEscalation (CKV_K8S_20) [MEDIUM] 1-54: Minimize the admission of root containers (CKV_K8S_23) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DROP TABLE user_passwords; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| CREATE TABLE user_passwords ( | ||
| user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, | ||
| password_hash TEXT NOT NULL, | ||
| created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| deleted_at TIMESTAMP WITH TIME ZONE | ||
| ); | ||
| CREATE TRIGGER set_timestamp BEFORE | ||
| UPDATE ON user_passwords FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); | ||
|
|
||
| COMMENT ON TABLE user_passwords IS 'This table is used to keep a record of the users password'; | ||
| COMMENT ON COLUMN user_passwords.user_id IS 'References the unique identifier of the user.'; | ||
| COMMENT ON COLUMN user_passwords.password_hash IS 'Hashed password of the user'; | ||
| ; | ||
| COMMENT ON COLUMN user_passwords.created_at IS 'Timestamp of when the record was created.'; | ||
| COMMENT ON COLUMN user_passwords.updated_at IS 'Timestamp when the record was last updated. The field is updated automatically by the set_timestamp trigger.'; | ||
| COMMENT ON COLUMN user_passwords.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| DROP TABLE password_reset_tokens; | ||
|
|
||
| DELETE FROM email_deliveries | ||
| WHERE email_template_id IN ( | ||
| SELECT id | ||
| FROM email_templates | ||
| WHERE course_instance_id IS NULL | ||
| ); | ||
|
|
||
| DELETE FROM email_templates | ||
| WHERE course_instance_id IS NULL; | ||
|
|
||
| ALTER TABLE email_templates | ||
| ALTER COLUMN course_instance_id | ||
| SET NOT NULL; | ||
|
|
||
| ALTER TABLE email_templates DROP COLUMN IF EXISTS language; | ||
|
|
||
| DROP INDEX IF EXISTS unique_email_templates_name_language_general; | ||
|
|
||
|
|
||
| COMMENT ON TABLE email_templates IS 'An email template table, which contains the email subject and content written in the Gutenberg Editor. Supports adding exercise points/completions threshold templates for course instances.'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| CREATE TABLE password_reset_tokens ( | ||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
| token UUID NOT NULL UNIQUE, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Store only hashed reset tokens; avoid plaintext UUID tokens. Saving raw reset tokens (even UUID v4) is a security risk if the DB is leaked. Best practice is to hash the token server-side, store only the hash, and perform constant‑time comparisons on lookup. This prevents immediate account takeover from DB dumps and limits blast radius. Suggested schema change (requires coordinated Rust changes to compute and query by hash): -CREATE TABLE password_reset_tokens (
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
- token UUID NOT NULL UNIQUE,
+CREATE TABLE password_reset_tokens (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ token_hash BYTEA NOT NULL,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() + INTERVAL '1 hour',
used_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);Additional indices and constraints to add in this or a follow-up migration: -- Uniqueness is optional but helps guard against accidental duplicates
CREATE UNIQUE INDEX IF NOT EXISTS idx_password_reset_tokens_token_hash
ON password_reset_tokens (token_hash);
-- Enforce at most one active token per user (see concurrency note below)
CREATE UNIQUE INDEX IF NOT EXISTS idx_password_reset_tokens_one_active_per_user
ON password_reset_tokens (user_id)
WHERE used_at IS NULL AND deleted_at IS NULL;I can provide the Rust-side changes (issue token, hash with SHA-256, store hash, and compare in constant time) if you want to move forward. |
||
| user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
| expires_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() + INTERVAL '1 hour', | ||
| used_at TIMESTAMP WITH TIME ZONE, | ||
| created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| deleted_at TIMESTAMP WITH TIME ZONE | ||
| ); | ||
Maijjay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| CREATE TRIGGER set_timestamp BEFORE | ||
| UPDATE ON password_reset_tokens FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); | ||
|
|
||
| CREATE UNIQUE INDEX IF NOT EXISTS unique_active_password_reset_tokens_user ON password_reset_tokens(user_id) | ||
| WHERE deleted_at IS NULL | ||
| AND used_at IS NULL; | ||
|
|
||
|
|
||
| COMMENT ON TABLE password_reset_tokens IS 'Stores one-time password reset tokens with expiration and usage tracking.'; | ||
| COMMENT ON COLUMN password_reset_tokens.id IS 'A unique identifier for the resetting user password'; | ||
| COMMENT ON COLUMN password_reset_tokens.token IS 'Token sent to user for resetting their password.'; | ||
| COMMENT ON COLUMN password_reset_tokens.user_id IS 'References the user the token belongs to.'; | ||
| COMMENT ON COLUMN password_reset_tokens.expires_at IS 'Time after which the token becomes invalid.'; | ||
| COMMENT ON COLUMN password_reset_tokens.used_at IS 'Time when the token was used. Null if unused.'; | ||
| COMMENT ON COLUMN password_reset_tokens.created_at IS 'Time when the token was created.'; | ||
| COMMENT ON COLUMN password_reset_tokens.updated_at IS 'Time when the token was last updated. Automatically set by trigger.'; | ||
| COMMENT ON COLUMN password_reset_tokens.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.'; | ||
|
|
||
| ALTER TABLE email_templates | ||
| ALTER COLUMN course_instance_id DROP NOT NULL; | ||
|
|
||
| ALTER TABLE email_templates | ||
| ADD COLUMN language VARCHAR(255); | ||
|
|
||
Maijjay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| CREATE UNIQUE INDEX IF NOT EXISTS unique_email_templates_name_language_general ON email_templates(name, language) | ||
| WHERE course_instance_id IS NULL; | ||
|
|
||
| COMMENT ON COLUMN email_templates.course_instance_id IS 'If not null the template is considered course instance specific. If null, the template is considered general.'; | ||
|
|
||
| COMMENT ON COLUMN email_templates.language IS 'Language code for the template, e.g. fi, en, sv. If null, language is not specified'; | ||
|
|
||
| COMMENT ON TABLE email_templates IS 'An email template table, which contains the email subject and content written in the Gutenberg Editor. Template is general if course_instance_id is NULL, or specific to a course instance if course_instance_id is set. Supports adding exercise points/completions threshold templates for course instances.'; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| DROP TABLE user_email_codes; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| CREATE TABLE user_email_codes ( | ||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||
| code VARCHAR(16) NOT NULL, | ||
| user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
| expires_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() + INTERVAL '1 hour', | ||
| used_at TIMESTAMP WITH TIME ZONE, | ||
| created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), | ||
| deleted_at TIMESTAMP WITH TIME ZONE | ||
| ); | ||
|
|
||
| CREATE TRIGGER set_timestamp BEFORE | ||
| UPDATE ON user_email_codes FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); | ||
|
|
||
| CREATE UNIQUE INDEX IF NOT EXISTS unique_active_user_email_codes_user ON user_email_codes(user_id, code) | ||
| WHERE deleted_at IS NULL | ||
| AND used_at IS NULL; | ||
|
|
||
| COMMENT ON TABLE user_email_codes IS 'Stores single-use codes for actions like user account deletion verification.'; | ||
| COMMENT ON COLUMN user_email_codes.id IS 'A unique identifier for this code record'; | ||
| COMMENT ON COLUMN user_email_codes.code IS 'The single-use code sent to the user.'; | ||
| COMMENT ON COLUMN user_email_codes.user_id IS 'References the user the code belongs to.'; | ||
| COMMENT ON COLUMN user_email_codes.expires_at IS 'Time after which the code becomes invalid.'; | ||
| COMMENT ON COLUMN user_email_codes.used_at IS 'Time when the code was used. Null if unused.'; | ||
| COMMENT ON COLUMN user_email_codes.created_at IS 'Time when the code was created.'; | ||
| COMMENT ON COLUMN user_email_codes.updated_at IS 'Time when the code was last updated. Automatically set by trigger.'; | ||
| COMMENT ON COLUMN user_email_codes.deleted_at IS 'Timestamp when the record was deleted. If null, the record is not deleted.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Set Pod-level security context (runAsNonRoot/fsgroup).
To avoid UID drift and ensure non-root across all containers, define a pod-level securityContext. Choose a UID/GID your image supports.
📝 Committable suggestion
🤖 Prompt for AI Agents