perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Align PLC identity empty responses

alice 2b1dee8c 10be6e03

+74 -9
+1
docs/TEST_AUDIT.md
··· 79 79 - App-password sessions follow the official runtime more closely than the older local assumptions did: access-token scopes use the `com.atproto.appPass` / `com.atproto.appPassPrivileged` names, standard app-password sessions may list app passwords, privileged-only `getServiceAuth` failures report `InvalidRequest`, and revoked refresh tokens on `refreshSession` fail with `400 ExpiredToken`. 80 80 - `com.atproto.server.requestPasswordReset` and `com.atproto.server.deleteAccount` now follow the reference form-token flow, with focused regression coverage for missing-account and bearerless deletion semantics. 81 81 - Core no-output account lifecycle procedures now match the official runtime’s empty-body `200` surface instead of returning local JSON `{}` objects: `deleteSession`, `revokeAppPassword`, `deactivateAccount`, `activateAccount`, `requestPasswordReset`, `resetPassword`, `requestEmailConfirmation`, `confirmEmail`, `updateEmail`, `requestAccountDelete`, and `deleteAccount`. 82 + - PLC identity success procedures now also match the official runtime’s empty-body `200` surface: `com.atproto.identity.requestPlcOperationSignature`, `com.atproto.identity.updateHandle`, and `com.atproto.identity.submitPlcOperation`. 82 83 - Password-bearing account endpoints need the same bounded-length behavior as the official runtime: `createAccount` rejects passwords longer than 256 characters, `createSession` rejects passwords longer than 512 characters with the reset hint, and `resetPassword` / `deleteAccount` reject overlong password inputs with `Invalid password length.` 83 84 - The executable reference harness now also pins those password-boundary semantics directly, including the official `AuthenticationRequired` error shape for overlong `createSession` requests and case-insensitive `requestPasswordReset` email lookup. 84 85 - `com.atproto.server.createAccount` with an explicit `did` must behave like an authenticated migration flow: require auth from that same DID, keep the existing DID document, and start the new account deactivated until activation catches the DID document up to the new PDS.
+8 -3
lib/ATProto/PDS/API/Misc.pm
··· 81 81 subject => 'PLC update requested', 82 82 content => sub ($token) { "Use token $token->{token} to authorize your PLC operation." }, 83 83 ); 84 - return {}; 84 + return _render_empty_success($c); 85 85 }); 86 86 87 87 $registry->register('com.atproto.identity.signPlcOperation', sub ($c, $endpoint) { ··· 154 154 my $did_doc = refresh_plc_did_doc($c->app->settings, $account->{did}); 155 155 $account = $c->store->update_account($account->{did}, did_doc => $did_doc); 156 156 _append_identity_event($c, $account); 157 - return {}; 157 + return _render_empty_success($c); 158 158 }); 159 159 160 160 $registry->register('com.atproto.identity.updateHandle', sub ($c, $endpoint) { ··· 192 192 } 193 193 my $updated = $c->store->update_account($account->{did}, %changes); 194 194 _append_identity_event($c, $updated); 195 - return {}; 195 + return _render_empty_success($c); 196 196 }); 197 197 198 198 $registry->register('com.atproto.lexicon.resolveLexicon', sub ($c, $endpoint) { ··· 362 362 xrpc_error(400, 'InvalidToken', 'Bad token scope') 363 363 unless (($claims->{scope} // TOKEN_AUD_ACCESS) eq TOKEN_AUD_ACCESS); 364 364 return 1; 365 + } 366 + 367 + sub _render_empty_success ($c) { 368 + $c->render(data => q()); 369 + return; 365 370 } 366 371 367 372 sub _valid_plc_operation ($operation) {
+56
script/differential-validate
··· 577 577 confirm => 'confirm_email', 578 578 update => 'update_email', 579 579 delete => 'delete_account', 580 + plc => 'plc_operation', 580 581 }, 581 582 }, 582 583 perlsky => { ··· 591 592 confirm => 'email_confirm', 592 593 update => 'email_update', 593 594 delete => 'account_delete', 595 + plc => 'plc_operation', 594 596 }, 595 597 }, 596 598 ); ··· 1129 1131 auth_header($server{$name}{access}), 1130 1132 ); 1131 1133 check($res->is_success, "$name requestPlcOperationSignature succeeds"); 1134 + next unless $res->is_success; 1135 + $server{$name}{plc_signature_request} = { 1136 + status => $res->code // 0, 1137 + empty_body => (($res->body // q()) eq q()) ? 1 : 0, 1138 + }; 1132 1139 } 1133 1140 1141 + check( 1142 + same_hash($server{reference}{plc_signature_request}, $server{perlsky}{plc_signature_request}), 1143 + 'requestPlcOperationSignature matches the official reference PDS empty-body semantics', 1144 + ); 1145 + 1134 1146 note('Comparing PLC handle updates'); 1135 1147 for my $name (sort keys %server) { 1136 1148 my $new_handle = $name eq 'reference' ? 'alice-renamed-ref.test' : 'alice-renamed-perl.test'; ··· 1142 1154 ); 1143 1155 check($res->is_success, "$name updateHandle succeeds for PLC accounts"); 1144 1156 next unless $res->is_success; 1157 + $server{$name}{plc_handle_update} = { 1158 + status => $res->code // 0, 1159 + empty_body => (($res->body // q()) eq q()) ? 1 : 0, 1160 + }; 1145 1161 $server{$name}{renamed_handle} = $new_handle; 1146 1162 1147 1163 my $handle_res = get_form( ··· 1154 1170 if $handle_res->is_success; 1155 1171 } 1156 1172 1173 + check( 1174 + same_hash($server{reference}{plc_handle_update}, $server{perlsky}{plc_handle_update}), 1175 + 'updateHandle matches the official reference PDS empty-body semantics', 1176 + ); 1177 + 1157 1178 note('Comparing PLC token requirements'); 1158 1179 for my $name (sort keys %server) { 1159 1180 my $res = post_json( ··· 1169 1190 check( 1170 1191 same_hash($server{reference}{missing_plc_token_error}, $server{perlsky}{missing_plc_token_error}), 1171 1192 'signPlcOperation matches the official reference PDS token requirement semantics', 1193 + ); 1194 + 1195 + note('Comparing successful PLC submit semantics'); 1196 + for my $name (sort keys %server) { 1197 + my $plc_token = latest_email_token_record($server{$name}, 'plc', $server{$name}{did}); 1198 + check($plc_token && length($plc_token->{token} // q()), "$name requestPlcOperationSignature issues a PLC token"); 1199 + next unless $plc_token && length($plc_token->{token} // q()); 1200 + 1201 + my $sign_res = post_json( 1202 + $server{$name}{origin}, 1203 + 'com.atproto.identity.signPlcOperation', 1204 + { token => $plc_token->{token} }, 1205 + auth_header($server{$name}{access}), 1206 + ); 1207 + check($sign_res->is_success, "$name signPlcOperation succeeds with the issued PLC token"); 1208 + next unless $sign_res->is_success; 1209 + 1210 + my $submit_res = post_json( 1211 + $server{$name}{origin}, 1212 + 'com.atproto.identity.submitPlcOperation', 1213 + { operation => (($sign_res->json || {})->{operation} || {}) }, 1214 + auth_header($server{$name}{access}), 1215 + ); 1216 + check($submit_res->is_success, "$name submitPlcOperation succeeds with a signed PLC op"); 1217 + next unless $submit_res->is_success; 1218 + 1219 + $server{$name}{plc_submit_success} = { 1220 + status => $submit_res->code // 0, 1221 + empty_body => (($submit_res->body // q()) eq q()) ? 1 : 0, 1222 + }; 1223 + } 1224 + 1225 + check( 1226 + same_hash($server{reference}{plc_submit_success}, $server{perlsky}{plc_submit_success}), 1227 + 'submitPlcOperation matches the official reference PDS empty-body success semantics', 1172 1228 ); 1173 1229 1174 1230 note('Comparing PLC submitPlcOperation validation');
+1 -1
t/external-handle-update.t
··· 80 80 } => json => { 81 81 handle => 'alice.external', 82 82 })->status_is(200) 83 - ->json_is({}); 83 + ->content_is(q()); 84 84 85 85 $t->get_ok('/xrpc/com.atproto.identity.resolveHandle?handle=alice.external') 86 86 ->status_is(200)
+2 -2
t/oauth-permissions.t
··· 239 239 'POST', 240 240 $config->{base_url} . '/xrpc/com.atproto.identity.updateHandle', 241 241 ) => json => { handle => 'alice-renamed' })->status_is(200) 242 - ->json_is({}); 242 + ->content_is(q()); 243 243 244 244 $t->post_ok('/xrpc/com.atproto.identity.requestPlcOperationSignature' => _oauth_headers( 245 245 $identity_handle->{access_token}, ··· 254 254 'POST', 255 255 $config->{base_url} . '/xrpc/com.atproto.identity.requestPlcOperationSignature', 256 256 ) => json => {})->status_is(200) 257 - ->json_is({}); 257 + ->content_is(q()); 258 258 259 259 my $plc_token = $app->store->latest_action_token( 260 260 did => $did,
+6 -3
t/plc-identity.t
··· 168 168 Authorization => "Bearer $access", 169 169 } => json => { 170 170 handle => 'alice-renamed.test', 171 - })->status_is(200); 171 + })->status_is(200) 172 + ->content_is(q()); 172 173 173 174 $t->post_ok('/xrpc/com.atproto.identity.updateHandle' => { 174 175 Authorization => "Bearer $app_password_access", ··· 191 192 192 193 $t->post_ok('/xrpc/com.atproto.identity.requestPlcOperationSignature' => { 193 194 Authorization => "Bearer $access", 194 - })->status_is(200); 195 + })->status_is(200) 196 + ->content_is(q()); 195 197 196 198 my $token = $app->store->latest_action_token( 197 199 did => $did, ··· 266 268 Authorization => "Bearer $access", 267 269 } => json => { 268 270 operation => $signed, 269 - })->status_is(200); 271 + })->status_is(200) 272 + ->content_is(q()); 270 273 271 274 $t->get_ok('/xrpc/com.atproto.identity.resolveHandle' => form => { 272 275 handle => 'alice-renamed.test',