Skip to content

Commit a5432b0

Browse files
committed
Fix csrf token issue on invite accept dialog url.
Generate proper invite accept dialog path. Set inviteAcceptDialog in OCMDiscoveryListener. Rename db migration. Signed-off-by: antoonp <antoon@redblom.com>
1 parent 19c3e79 commit a5432b0

File tree

4 files changed

+34
-34
lines changed

4 files changed

+34
-34
lines changed

lib/Controller/FederatedInvitesController.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,14 @@ public function deleteInvite(string $token): JSONResponse {
141141
}
142142

143143
/**
144-
* Sets the token and provider states which triggers display of the invite accept dialog.
144+
* Results in displaying the invite accept dialog upon following the invite link.
145145
*
146146
* @param string $token
147147
* @param string $providerDomain
148148
* @return TemplateResponse
149149
*/
150150
#[NoAdminRequired]
151+
#[NoCSRFRequired]
151152
public function inviteAcceptDialog(string $token = '', string $providerDomain = ''): TemplateResponse {
152153
$this->initialStateService->provideInitialState(Application::APP_ID, 'inviteToken', $token);
153154
$this->initialStateService->provideInitialState(Application::APP_ID, 'inviteProvider', $providerDomain);
@@ -157,14 +158,14 @@ public function inviteAcceptDialog(string $token = '', string $providerDomain =
157158
}
158159

159160
/**
160-
* Creates an invitation to exchange contact info for the user with the specified uid.
161+
* Creates an invitation to exchange contact info with the remote user.
161162
*
162-
* @param string $emailAddress the recipient email address to send the invitation to
163-
* @param string $message the optional message to send with the invitation
164-
* @return JSONResponse with data signature ['token' | 'message'] - the token of the invitation or an error message in case of error
163+
* @param string $emailAddress the recipient's (remote user's) email address to send the invitation to.
164+
* @param string $message optional message to send with the invitation.
165+
* @return JSONResponse with data signature ['invite' | 'message'] - the invite url or an error message in case of error.
165166
*/
166167
#[NoAdminRequired]
167-
public function createInvite(string $email, string $message): JSONResponse {
168+
public function createInvite(string $email, ?string $message): JSONResponse {
168169
if (!isset($email)) {
169170
return new JSONResponse(['message' => 'Recipient email is required'], Http::STATUS_BAD_REQUEST);
170171
}
@@ -382,7 +383,7 @@ public function discover(string $base): DataResponse {
382383
} elseif (empty($dialog)) {
383384
// We can not check and see, because we have to be logged in here
384385
// so we will just risk it.
385-
$dialog = $base . WayfProvider::INVITE_ACCEPT_DIALOG;
386+
$dialog = $base . $this->wayfProvider->getInviteAcceptDialogPath();
386387
$absolute = preg_match('#^https?://#i', $dialog) ? $dialog : $base . $dialog;
387388
return new DataResponse([
388389
'base' => $base,

lib/Listener/OcmDiscoveryListener.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,35 @@
88
*/
99
namespace OCA\Contacts\Listener;
1010

11+
use OC\Core\AppInfo\ConfigLexicon;
1112
use OCP\EventDispatcher\Event;
1213
use OCP\EventDispatcher\IEventListener;
14+
use OCP\IAppConfig;
15+
use OCP\IURLGenerator;
1316
use OCP\OCM\Events\LocalOCMDiscoveryEvent;
1417

1518
/** @template-implements IEventListener<LocalOCMDiscoveryEvent> */
1619
class OcmDiscoveryListener implements IEventListener {
1720

18-
public function __construct() {}
21+
public function __construct(
22+
private IAppConfig $appConfig,
23+
private IURLGenerator $urlGenerator,
24+
) {}
1925

2026
/**
21-
* This handler will register the capability: invite-accepted
27+
* This handler will register the capability invite-accepted
28+
* and set the invite accept dialog url.
2229
*
2330
* @param Event $event an event of type LocalOCMDiscoveryEvent
2431
* @return void
2532
*/
26-
public function handle(Event $event): void {
27-
if($event instanceof LocalOCMDiscoveryEvent) {
33+
public function handle(Event $event): void {
34+
if ($event instanceof LocalOCMDiscoveryEvent) {
2835
$event->addCapability('invite-accepted');
36+
$inviteAcceptDialog = $this->appConfig->getValueString('core', ConfigLexicon::OCM_INVITE_ACCEPT_DIALOG);
37+
if ($inviteAcceptDialog !== '') {
38+
$event->getProvider()->setInviteAcceptDialog($this->urlGenerator->linkToRouteAbsolute($inviteAcceptDialog));
39+
}
2940
}
3041
}
31-
3242
}

lib/Migration/Version1016Date202502262004.php renamed to lib/Migration/Version8004Date20260130131217.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use OCP\Migration\SimpleMigrationStep;
1818

1919
#[CreateTable(table: 'federated_invites', columns: ['id', 'user_id', 'recipient_provider', 'recipient_user_id', 'recipient_name', 'recipient_email', 'token', 'accepted', 'created_at', 'expired_at', 'accepted_at'], description: 'Supporting the OCM Invitation Flow feature')]
20-
class Version1016Date202502262004 extends SimpleMigrationStep {
20+
class Version8004Date20260130131217 extends SimpleMigrationStep {
2121
/**
2222
* @param IOutput $output
2323
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`

lib/WayfProvider.php

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ class WayfProvider {
2222
// The default wayf page route.
2323
public const WAYF_ROUTE = '/wayf';
2424
public const DISCOVERY_ROUTE = '/discover';
25-
public const INVITE_ACCEPT_DIALOG = '/index.php/apps/contacts' . FederatedInvitesService::OCM_INVITE_ACCEPT_DIALOG_ROUTE;
2625

2726
public function __construct(
2827
private IAppConfig $appConfig,
2928
private IClientService $httpClient,
30-
private FederatedInvitesService $federatedInvitesService,
3129
private LoggerInterface $logger,
3230
private IOCMDiscoveryService $discovery,
3331
private IURLGenerator $urlGenerator,
@@ -73,7 +71,8 @@ public function getMeshProviders(): array {
7371
}
7472
if ($inviteAcceptDialog === '') {
7573
// We fall back on Nextcloud default path
76-
$inviteAcceptDialog = $prov['url'] . WayfProvider::INVITE_ACCEPT_DIALOG;
74+
$inviteAcceptDialogPath = self::getInviteAcceptDialogPath();
75+
$inviteAcceptDialog = rtrim($prov['url'], '/') . $inviteAcceptDialogPath;
7776
}
7877
$federations[$fed][] = [
7978
'provider' => $disc->getProvider(),
@@ -116,27 +115,17 @@ public function getMeshProvidersFromCache(): array {
116115
* @return string|null the WAYF login page endpoint or null if it could not be created
117116
*/
118117
public function getWayfEndpoint(): string|null {
119-
$appRootUrl = $this->getAppRootUrl();
120-
if(empty($appRootUrl)) {
121-
$this->logger->error("Unable to create WAYF endpoint", ['app' => Application::APP_ID]);
122-
return null;
123-
}
124-
$appRootUrl = trim($appRootUrl, '/');
125-
$wayfEndpoint = 'https://' . $this->federatedInvitesService->getProviderFQDN() . "/$appRootUrl/" . Application::APP_ID . WayfProvider::WAYF_ROUTE;
126-
return $this->appConfig->getValueString(Application::APP_ID, 'wayf_endpoint', $wayfEndpoint);
118+
// default wayf endpoint
119+
$defaultWayfEndpoint = $this->urlGenerator->linkToRouteAbsolute(Application::APP_ID . '.federated_invites.wayf');
120+
return $this->appConfig->getValueString(Application::APP_ID, 'wayf_endpoint', $defaultWayfEndpoint);
127121
}
128122

129123
/**
130-
* Returns this app root url. Currently either '/apps' or '/custom_apps'.
131-
* @return string|null the app root url or null if the app root url could not be determined
124+
* Returns the path of the invite accept dialog route.
125+
*
126+
* @return string
132127
*/
133-
private function getAppRootUrl(): string|null {
134-
foreach(\OC::$APPSROOTS as $appRoot) {
135-
if(str_starts_with(__DIR__, $appRoot['path'])) {
136-
return $appRoot['url'];
137-
}
138-
}
139-
$this->logger->error("Could not determine app root url", ['app' => Application::APP_ID]);
140-
return null;
128+
public function getInviteAcceptDialogPath(): string {
129+
return $this->urlGenerator->linkToRoute(Application::APP_ID . '.federated_invites.invite_accept_dialog');
141130
}
142131
}

0 commit comments

Comments
 (0)