Skip to content

Commit fb1fc75

Browse files
Merge branch 'master' into upload-support
Signed-off-by: Lukasz <108612299+LukasMalyszko@users.noreply.github.com>
2 parents 62261a9 + a81b2a7 commit fb1fc75

110 files changed

Lines changed: 16270 additions & 5221 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/devcontainer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"forwardPorts": [3000, 5432],
1717
"postCreateCommand": "apt-get update && apt-get install -y --no-install-recommends build-essential libpq-dev libyaml-dev nodejs npm && bin/setup",
1818
"remoteEnv": {
19-
"DATABASE_URL": "postgres://postgres:postgres@db:5432/pwpush_development"
19+
"DATABASE_URL": "postgres://postgres:postgres@db:5432/pwpush_development",
20+
"PWP__REQUIRE_MFA": "false"
2021
}
2122
}

.do/deploy.template.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ spec:
9999
scope: RUN_TIME
100100
type: GENERAL
101101
value: "false"
102+
- key: PWP__REQUIRE_MFA
103+
scope: RUN_TIME
104+
type: GENERAL
105+
value: "false"
102106
- key: PWP__ENABLE_USER_ACCOUNT_EMAILS
103107
scope: RUN_TIME
104108
type: GENERAL

Gemfile.lock

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ GEM
9696
ansi (1.6.0)
9797
ast (2.4.3)
9898
aws-eventstream (1.4.0)
99-
aws-partitions (1.1237.0)
99+
aws-partitions (1.1238.0)
100100
aws-sdk-core (3.244.0)
101101
aws-eventstream (~> 1, >= 1.3.0)
102102
aws-partitions (~> 1, >= 1.992.0)
@@ -386,7 +386,7 @@ GEM
386386
mail_form (1.11.0)
387387
actionpack (>= 7.0)
388388
activemodel (>= 7.0)
389-
mailbin (1.1.0)
389+
mailbin (1.1.1)
390390
importmap-rails
391391
rails (>= 7.1.0)
392392
turbo-rails
@@ -415,7 +415,7 @@ GEM
415415
stimulus-rails
416416
turbo-rails
417417
msgpack (1.8.0)
418-
multi_json (1.19.1)
418+
multi_json (1.20.1)
419419
mysql2 (0.5.7)
420420
bigdecimal
421421
net-http (0.9.1)
@@ -452,7 +452,7 @@ GEM
452452
racc (~> 1.4)
453453
nokogiri (1.19.2-x86_64-linux-musl)
454454
racc (~> 1.4)
455-
oj (3.16.16)
455+
oj (3.16.17)
456456
bigdecimal (>= 3.0)
457457
ostruct (>= 0.2)
458458
orm_adapter (0.5.0)
@@ -471,7 +471,7 @@ GEM
471471
overmind (2.5.1-x86-freebsd)
472472
overmind (2.5.1-x86-linux)
473473
overmind (2.5.1-x86_64-freebsd)
474-
pagy (43.5.0)
474+
pagy (43.5.1)
475475
json
476476
uri
477477
yaml
@@ -650,7 +650,7 @@ GEM
650650
sass-embedded (1.99.0-x86_64-linux-musl)
651651
google-protobuf (~> 4.31)
652652
securerandom (0.4.1)
653-
selenium-webdriver (4.41.0)
653+
selenium-webdriver (4.43.0)
654654
base64 (~> 0.2)
655655
logger (~> 1.4)
656656
rexml (~> 3.2, >= 3.2.5)
@@ -870,7 +870,7 @@ CHECKSUMS
870870
apipie-rails (1.6.0)
871871
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
872872
aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
873-
aws-partitions (1.1237.0) sha256=9b82f529b69ad83a8e4c5e123038924ed5e8f59bd6064a293ef20efc63364841
873+
aws-partitions (1.1238.0) sha256=fa3d1bdea6d7e7619e8cee22ebce8a569d2119296d3ec8c5f9b9b7c81fb0602c
874874
aws-sdk-core (3.244.0) sha256=3e458c078b0c5bdee95bc370c3a483374b3224cf730c1f9f0faf849a5d9a18ea
875875
aws-sdk-kms (1.123.0) sha256=d405f37e82f8fa32045ca8980be266c0b45b37aaf2012afe0254321a1e811f20
876876
aws-sdk-s3 (1.219.0) sha256=6a755d7377978525758b3c29185ca6a10128ce2b07555ca37c4549de10c2f1c7
@@ -981,7 +981,7 @@ CHECKSUMS
981981
madmin (2.3.2) sha256=6961cbaeed82634240c7c9888a49b181834bf9b85a9282caebf0ee7f368df73c
982982
mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941
983983
mail_form (1.11.0) sha256=54aaaf11b69e7e5bd4be220dfc80f2469bb809d6ed1f893b8d940f0b67232c5a
984-
mailbin (1.1.0) sha256=a5a3556d9a9a7b6168dea5389c36856cf0cd4abb35d74d25cea0a2c58fab8660
984+
mailbin (1.1.1) sha256=c68e45ba050d4406fd1ae23df87160fac71f311b35b5348c4020a34be53f0f95
985985
marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee
986986
matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b
987987
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
@@ -992,7 +992,7 @@ CHECKSUMS
992992
minitest-reporters (1.8.0) sha256=8ce5280fb73ad3178ae525454df169b6f28c1b38b1d088ea91815d3a370ba384
993993
mission_control-jobs (1.1.0) sha256=b13da9cde3344fec7c744b79d2a34c3ecd454f45d4d593d4c968ba762315e84c
994994
msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732
995-
multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7
995+
multi_json (1.20.1) sha256=2f3934e805cc45ef91b551a1f89d0e9191abd06a5e04a2ef09a6a036c452ca6d
996996
mysql2 (0.5.7) sha256=ba09ede515a0ae8a7192040a1b778c0fb0f025fa5877e9be895cd325fa5e9d7b
997997
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
998998
net-imap (0.6.3) sha256=9bab75f876596d09ee7bf911a291da478e0cd6badc54dfb82874855ccc82f2ad
@@ -1011,7 +1011,7 @@ CHECKSUMS
10111011
nokogiri (1.19.2-arm64-darwin) sha256=58d8ea2e31a967b843b70487a44c14c8ba1866daa1b9da9be9dbdf1b43dee205
10121012
nokogiri (1.19.2-x86_64-linux-gnu) sha256=fa8feca882b73e871a9845f3817a72e9734c8e974bdc4fbad6e4bc6e8076b94f
10131013
nokogiri (1.19.2-x86_64-linux-musl) sha256=93128448e61a9383a30baef041bf1f5817e22f297a1d400521e90294445069a8
1014-
oj (3.16.16) sha256=3635b36128991796434f55da8decc0de236a323535adcb36fc04e6d0253c013d
1014+
oj (3.16.17) sha256=a6688f666143632a1ef11a8d80c8d631b1112733c7da698ffafa4a22a8488244
10151015
orm_adapter (0.5.0) sha256=aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9
10161016
os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f
10171017
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
@@ -1025,7 +1025,7 @@ CHECKSUMS
10251025
overmind (2.5.1-x86-freebsd) sha256=7fe2bade9d4b3274e1a5b759489bb8a1f3182d842bbb3cc0f92f85b73a8fa5b6
10261026
overmind (2.5.1-x86-linux) sha256=0ba519b106f6473500543fa945d7acd3ef8485a3f8a9c4838d9f0f1869fe5176
10271027
overmind (2.5.1-x86_64-freebsd) sha256=37e63e97534e03c612a066d30c7fc9ada5e062333702f74c782078ed122544c9
1028-
pagy (43.5.0) sha256=58885d5f659e8db5b92cf35eeba674113e4e7bda12649b603c2d6908402570a4
1028+
pagy (43.5.1) sha256=ca5aaa6d65d21eee67a48fe8801d022d07ee72afbc5bea6a9e21b13a27b7c0b9
10291029
parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970
10301030
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
10311031
pg (1.6.3) sha256=1388d0563e13d2758c1089e35e973a3249e955c659592d10e5b77c468f628a99
@@ -1098,7 +1098,7 @@ CHECKSUMS
10981098
sass-embedded (1.99.0-x86_64-linux-gnu) sha256=a4e2ae5e9951815cb8b3ab408cee8b800852491988a57de735f18d259c70d7c4
10991099
sass-embedded (1.99.0-x86_64-linux-musl) sha256=94be72a6f856c610e67b12303dc76a58412e64b24ce97bdf4912199b8145c8dd
11001100
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
1101-
selenium-webdriver (4.41.0) sha256=cdc1173cd55cf186022cea83156cc2d0bec06d337e039b02ad25d94e41bedd22
1101+
selenium-webdriver (4.43.0) sha256=a634377b964b701c6ac0a009ce3a08fa34ec1e1e7fe9a6d57e3088d14529a65c
11021102
signet (0.21.0) sha256=d617e9fbf24928280d39dcfefba9a0372d1c38187ffffd0a9283957a10a8cd5b
11031103
singleton (0.3.0) sha256=83ea1bca5f4aa34d00305ab842a7862ea5a8a11c73d362cb52379d94e9615778
11041104
smart_properties (1.17.0) sha256=f9323f8122e932341756ddec8e0ac9ec6e238408a7661508be99439ca6d6384b

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Use the [hosted service](https://pwpush.com) or run your own instance with Docke
4545

4646
- **Encrypted at rest** — Sensitive data is stored encrypted and deleted when expired.
4747
- **Expiry controls** — Limit by number of views and/or time; links can require a passphrase.
48-
- **Two-factor authentication** — TOTP (authenticator apps) for user accounts, with optional backup codes.
48+
- **Two-factor authentication (MFA)** — TOTP (authenticator apps) with backup codes; admins can require MFA instance-wide with `PWP__REQUIRE_MFA=true`.
4949
- **Audit logging** — Track what was shared and who viewed it (with optional logins).
5050
- **Unbranded delivery page** — No logos, superfluous text or unrelated links to confuse push recipients.
5151

@@ -59,7 +59,8 @@ Use the [hosted service](https://pwpush.com) or run your own instance with Docke
5959

6060
### Integrations & API
6161

62-
- **JSON API** — Integrate with scripts, `curl`, `wget`, or third-party tools.
62+
- **JSON API v2** — Modern `/api/v2` endpoints for create/retrieve/audit/active/expired workflows.
63+
- **Legacy API compatibility** — Existing `/p`, `/f`, `/r` API routes (v1 style) remain available for backwards compatibility.
6364
- **CLI** — Automate distribution with [CLI tools](https://docs.pwpush.com/docs/3rd-party-tools/) and scripts.
6465
- **31 languages** — UI and secret-URL pages in 31 languages (courtesy of [Translation.io](https://translation.io/?utm_source=pwpush)).
6566

@@ -139,7 +140,17 @@ Production image build: [containers/docker/Dockerfile](containers/docker/Dockerf
139140

140141
### Use the API, CLI, or integrations
141142

142-
See [3rd party tools & integrations](https://docs.pwpush.com/docs/3rd-party-tools/) for API usage, CLIs, and integrations.
143+
For API usage, CLI tools, and integrations:
144+
145+
- API v2 docs: [docs.pwpush.com/docs/api-v2](https://docs.pwpush.com/docs/api-v2/)
146+
- OSS API endpoint reference in-app: `/api/v2/version` and `/api/v2/pushes`
147+
- 3rd-party tools and CLI integrations: [docs.pwpush.com/docs/3rd-party-tools](https://docs.pwpush.com/docs/3rd-party-tools/)
148+
149+
Quick API v2 smoke test:
150+
151+
```bash
152+
curl -s https://YOUR_HOST/api/v2/version
153+
```
143154

144155
---
145156

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.2.2
1+
2.4.2

app.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@
9292
"description": "Hide Log In; sign_in returns 404. Use with allow_anonymous true for anonymous-only. Do not combine with allow_anonymous false.",
9393
"value": "false"
9494
},
95+
"PWP__REQUIRE_MFA": {
96+
"description": "Require TOTP MFA for all signed-in users. Users without MFA are redirected to setup.",
97+
"value": "false"
98+
},
9599
"PWP__ENABLE_USER_ACCOUNT_EMAILS": {
96100
"description": "Devise mail (confirm/reset). Requires SMTP; set PWP__MAIL__SMTP_* when true.",
97101
"value": "false"

app/controllers/api/base_controller.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ def require_api_authentication
1111
if (user = user_from_token)
1212
sign_in user, store: false
1313

14-
elsif params["controller"] == "api/v1/version"
15-
# Version endpoint is public
14+
elsif %w[api/v1/version api/v2/version].include?(params["controller"])
15+
# Version endpoints are public
1616
nil
1717

1818
elsif request.headers.key?("Authorization") || request.headers.key?("X-User-Token")
1919
# The user is trying to authenticate with a bad token
2020
head :unauthorized
2121

22+
elsif !Settings.allow_anonymous
23+
# When anonymous access is disabled, API endpoints require authentication.
24+
head :unauthorized
25+
26+
elsif params["controller"] == "api/v2/pushes"
27+
if %w[audit active expired].include?(params["action"])
28+
# These v2 endpoints require a valid token
29+
head :unauthorized
30+
end
31+
2232
elsif request.path.start_with?("/p")
2333
if %w[audit active expired].include?(params["action"])
2434
# These paths require a valid token

app/controllers/api/v1/pushes_controller.rb

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,15 @@ def show
147147
}
148148
EOS
149149
def create
150-
# Require authentication if allow_anonymous is false
151-
# See config/settings.yml
152-
authenticate_user! unless Settings.allow_anonymous
150+
permitted_params = push_params
153151

154-
@push = Push.new(push_params)
152+
# Require authentication if anonymous creation is disabled or
153+
# when creating file pushes / uploading attachments.
154+
authenticate_user! if requires_authentication_for_create?(permitted_params)
155155

156-
if !push_params[:kind].present?
156+
@push = Push.new(permitted_params)
157+
158+
if !permitted_params[:kind].present?
157159
# These are used to determine the default kind based on the request path
158160
# for old push records. Their paths are generated based on their kind.
159161
# And, QR code pushes are created by using `/p/` path.
@@ -162,7 +164,7 @@ def create
162164
"file"
163165
elsif request.path.include?("/r.json")
164166
"url"
165-
elsif request.path.include?("/p.json") && push_params.key?(:files)
167+
elsif request.path.include?("/p.json") && permitted_params.key?(:files)
166168
"file"
167169
else
168170
"text"
@@ -171,8 +173,8 @@ def create
171173

172174
@push.user = current_user if user_signed_in?
173175

174-
assign_deletable_by_viewer(@push, push_params)
175-
assign_retrieval_step(@push, push_params)
176+
assign_deletable_by_viewer(@push, permitted_params)
177+
assign_retrieval_step(@push, permitted_params)
176178

177179
if @push.save
178180
log_creation(@push)
@@ -417,6 +419,18 @@ def expired
417419

418420
private
419421

422+
def requires_authentication_for_create?(permitted_params)
423+
return true unless Settings.allow_anonymous
424+
return true if request.path.start_with?("/f")
425+
426+
# Keep auth semantics aligned with API surface:
427+
# - v1 /p.json treats files key presence as file intent
428+
# - v2 /api/v2/pushes should do the same for auth gating
429+
((request.path.include?("/p.json") || params["controller"] == "api/v2/pushes") &&
430+
permitted_params.key?(:files)) ||
431+
permitted_params[:kind] == "file"
432+
end
433+
420434
# validate_page_parameter
421435
#
422436
# Validates and sanitizes the page parameter for pagination
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
class Api::V2::PushesController < Api::V1::PushesController
4+
before_action :force_json_format
5+
6+
private
7+
8+
def force_json_format
9+
request.format = :json
10+
end
11+
12+
def push_params
13+
permitted = params.require(:push).permit(:name, :kind, :expire_after_days, :expire_after_views,
14+
:deletable_by_viewer, :retrieval_step, :payload, :note, :passphrase, files: [])
15+
16+
# For v2 requests, file uploads imply a file push unless kind is explicit.
17+
if permitted[:kind].blank? && permitted[:files].present?
18+
permitted[:kind] = "file"
19+
end
20+
21+
permitted
22+
rescue => e
23+
Rails.logger.error("Error in push_params: #{e.message}")
24+
raise e
25+
end
26+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
class Api::V2::VersionController < Api::BaseController
4+
def show
5+
render json: {
6+
application_version: Version.current.to_s,
7+
api_version: "2.0",
8+
edition: "oss"
9+
}
10+
end
11+
end

0 commit comments

Comments
 (0)