Repository navigation
feat: record terms acceptance from the app, and meet Apple's rules for user-generated content #1042
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
Changes from all commits
7d21e83
e770927
2f40a89
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,18 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Actions; | ||
|
|
||
| use App\Models\User; | ||
|
|
||
| final readonly class UpdateUserTermsAcceptance | ||
| { | ||
| /** | ||
| * Records that they accepted the terms and privacy policy as they stand now. | ||
| */ | ||
| public function handle(User $user): void | ||
| { | ||
| $user->update(['terms_accepted_at' => now()]); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Http\Controllers\Api; | ||
|
|
||
| use App\Actions\UpdateUserTermsAcceptance; | ||
| use App\Http\Requests\UpdateApiUserTermsRequest; | ||
| use App\Http\Resources\CurrentUserResource; | ||
| use App\Models\User; | ||
| use App\Queries\NotificationGroupsQuery; | ||
| use Illuminate\Container\Attributes\CurrentUser; | ||
|
|
||
| final readonly class UserTermsController | ||
| { | ||
| /** | ||
| * Records that the person ticked the box for the terms and privacy policy in the app, and answers as GET /api/v1/me does. | ||
| */ | ||
| public function update(UpdateApiUserTermsRequest $request, #[CurrentUser] User $user, UpdateUserTermsAcceptance $action, NotificationGroupsQuery $notifications): CurrentUserResource | ||
| { | ||
| $action->handle($user); | ||
|
|
||
| return CurrentUserResource::make($user->loadCounts())->additional([ | ||
| 'unread_notifications_count' => $notifications->unreadCount($user), | ||
| ]); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Http\Requests; | ||
|
|
||
| use Illuminate\Foundation\Http\FormRequest; | ||
| use Illuminate\Validation\Rule; | ||
|
|
||
| /** | ||
| * The app says which version of the terms the person was shown, so an old build can't accept a version it never | ||
| * showed them. | ||
| */ | ||
| final class UpdateApiUserTermsRequest extends FormRequest | ||
| { | ||
| public function authorize(): bool | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * @return array<string, list<mixed>> | ||
| */ | ||
| public function rules(): array | ||
| { | ||
| return [ | ||
| 'version' => ['required', 'string', Rule::in([config('legal.terms_version')])], | ||
| ]; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| return [ | ||
|
|
||
| /* | ||
| |-------------------------------------------------------------------------- | ||
| | Terms version | ||
| |-------------------------------------------------------------------------- | ||
| | | ||
| | The version of the terms and privacy policy people accept: the moment it took effect, as an ISO 8601 UTC timestamp. | ||
| | Anyone who accepted at or after it accepted this version. Set a new version to the moment it ships, never earlier, | ||
| | and keep its date in step with resources/js/lib/legal.ts. Raising it asks everyone to accept again in the app. | ||
| | | ||
| */ | ||
|
|
||
| 'terms_version' => '2026-10-25T00:00:00Z', | ||
|
|
||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| use Illuminate\Database\Migrations\Migration; | ||
| use Illuminate\Database\Schema\Blueprint; | ||
| use Illuminate\Support\Facades\Schema; | ||
|
|
||
| return new class extends Migration | ||
| { | ||
| /** | ||
| * When someone accepted the terms and privacy policy. The version they accepted is the one in force at that moment, | ||
| * so it is not stored. Everyone already here is left empty: the apps ask them to accept the first time they open them. | ||
| */ | ||
| public function up(): void | ||
| { | ||
| Schema::table('users', function (Blueprint $table): void { | ||
| $table->timestamp('terms_accepted_at')->nullable(); | ||
|
Contributor
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. Add a reverse migration for If an operator runs Proposed change
|
||
| }); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,10 +55,10 @@ export default function Show() { | |
| <DocumentSection id="eligibility" title="Who can use it"> | ||
| <p> | ||
| You must be at least 16 years old, sign in with your own | ||
| account, and be allowed to use the service under the | ||
| laws that apply to you, including sanctions and export | ||
| laws. If you use it for an organization, you accept | ||
| these terms on its behalf. | ||
| GitHub or Apple account, and be allowed to use the | ||
| service under the laws that apply to you, including | ||
| sanctions and export laws. If you use it for an | ||
| organization, you accept these terms on its behalf. | ||
| </p> | ||
| </DocumentSection> | ||
|
|
||
|
|
@@ -152,9 +152,15 @@ export default function Show() { | |
| ]} | ||
| /> | ||
| <p> | ||
| To report content you believe is illegal or infringes | ||
| your rights, write to <Mail />. We decide how to handle | ||
| reports. | ||
| There is no place on the service for objectionable | ||
| content or abusive users, and we have zero tolerance for | ||
| them. Report a commit with Report in its menu, and use | ||
| Mute and Block on a profile to stop seeing someone. To | ||
| report content you believe is illegal or infringes your | ||
| rights, or anything else, write to <Mail />. We review | ||
| reports and act on content that breaks these rules | ||
| within 24 hours, by removing it and restricting or | ||
| banning the account that posted it. | ||
| </p> | ||
| </DocumentSection> | ||
|
|
||
|
|
@@ -170,11 +176,11 @@ export default function Show() { | |
| </p> | ||
| <p> | ||
| If you break these terms, we may stop you from posting | ||
| for a while (a timeout) or ban you. A ban applies to | ||
| your GitHub account, so you can't sign up again with it, | ||
| even after deleting your account. We keep a record of | ||
| timeouts and bans, and the GitHub account they apply to, | ||
| after your account is deleted. | ||
| for a while (a timeout) or ban you. A ban applies to the | ||
| GitHub or Apple account you signed in with, so you can't | ||
| sign up again with it, even after deleting your account. | ||
| We keep a record of timeouts and bans, and the account | ||
| they apply to, after your account is deleted. | ||
|
Comment on lines
+179
to
+183
Contributor
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. Align the Apple ban record with the privacy policy. These terms say you keep the account associated with an Apple ban after account deletion. The privacy policy at
|
||
| </p> | ||
| </DocumentSection> | ||
|
|
||
|
|
@@ -197,6 +203,25 @@ export default function Show() { | |
| </p> | ||
| </DocumentSection> | ||
|
|
||
| <DocumentSection id="apple" title="Apple"> | ||
| <p> | ||
| If you use the iOS app, these terms are between you and | ||
| us only, not Apple. Apple is not responsible for the app | ||
| or its content, and has no obligation to provide | ||
| maintenance or support for it. If the app fails to | ||
| conform to a warranty, you may tell Apple, and Apple | ||
| will refund the purchase price, if any; Apple has no | ||
| other warranty obligation for the app. We, not Apple, | ||
| answer any claim about the app, including product | ||
| liability, legal or regulatory compliance, and consumer | ||
| protection. Apple and its subsidiaries are third-party | ||
| beneficiaries of these terms and may enforce them | ||
| against you once you accept them. You confirm that you | ||
|
Comment on lines
+217
to
+219
Contributor
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. Preserve Apple’s third-party beneficiary rights in the general clause. This section gives Apple and its subsidiaries the right to enforce these terms. The general clause at lines 287-288 says the terms give rights to nobody other than the user and the service. Add an explicit Apple exception to that clause so the agreement does not contradict the new beneficiary provision. Apple’s minimum terms require the beneficiary provision when these terms serve as a custom app EULA. (developer.apple.com)
|
||
| are not in a country under a U.S. government embargo or | ||
| listed as a restricted party. | ||
| </p> | ||
| </DocumentSection> | ||
|
|
||
| <DocumentSection id="disclaimer" title="No warranty"> | ||
| <p className="font-medium uppercase"> | ||
| The service is provided "as is" and "as available", | ||
|
|
||
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.
🔎 Supported by static analysis
Script executed:
Repository: dev-null-sh/dev-null.sh
Length of output: 4078
Script executed:
Repository: dev-null-sh/dev-null.sh
Length of output: 5609
Script executed:
Repository: dev-null-sh/dev-null.sh
Length of output: 23611
Script executed:
Repository: dev-null-sh/dev-null.sh
Length of output: 6271
Script executed:
Repository: dev-null-sh/dev-null.sh
Length of output: 1532
Reject pre-effective terms acceptance.
Before
2026-10-23T00:00:00Z, both API requests accept the configured version and writenow()asterms_accepted_at. The response then reportsterms_accepted: false, and it remains false after the effective date because the stored timestamp is earlier thanlegal.terms_version.Add an effective-date validation to both request paths and return 422 before recording acceptance or creating the device.