Skip to content

add invitation - #863

Open
nananankona wants to merge 12 commits into
nextcloud:masterfrom
nananankona:master
Open

nananankona wants to merge 12 commits into
nextcloud:masterfrom
nananankona:master

Conversation

@nananankona

Copy link
Copy Markdown

Allow-list with email domains (including wildcard) or exact email addresses to register with
Invitation codes and invitation links: restrict who can register, limit the number of uses per link, set a storage quota for invited users and optionally skip the email verification step

@tcitworld

Copy link
Copy Markdown
Member

Thanks! Will review when I get the time.

Do you mind rebasing so that the CI passes ? You may drop the css/js changes to make it easier, they can be recompiled here. Ideally, please remove the translation files changes as well, those are usually handled through Transifex.

@tcitworld
tcitworld self-requested a review September 1, 2026 21:03
…limits- Add invitation links (/apps/registration/invite/{code}) with optional email/domain restriction, storage quota, maximum number of uses and expiry date; the usage counter is incremented and the quota applied when an account is created- Add allowed email addresses setting (in addition to allowed domains)- Add option to require an invitation code for all registrations- Add invitation management UI to the admin settings

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
… bypass- Add skip_email_verification option to invitations: registrations via such an invitation skip the email verification step and go straight to the account creation form- Show the invitation code and shareable link in a dialog after creating an invitation, each with a copy button; add a Details button to the invitation list for the same dialog- Add Japanese translations (l10n/ja) for the invitation strings

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
…time-typed expires/created_at fields can be returned as DateTimeobjects from the entity, which JSON-encode as objects and break thefrontend formatting (TypeError: b.replace is not a function). Normalizedates to Y-m-d H:i:s strings in the controller and harden isExpired andformatDate against non-string values.

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
…skip_email_verification flag can return null from the entity when thecolumn is missing or unset, which broke the bool return type ofinvitationSkipsVerification() with a TypeError on invite links. Defaultthe flag to false in the entity constructor and compare with === trueinstead of relying on the raw value.

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
The backtick-quoted uses + 1 expression breaks on PostgreSQL and
Oracle, which do not accept backticks as identifier quotes. Use the
unquoted column reference instead (uses is not a reserved word).

Also guard domainMatches() against email addresses without an @ sign,
which previously raised an undefined key warning and a TypeError in the
wildcard branch instead of a clean RegistrationException.

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Nextcloud forbids BOOLEAN columns with NOT NULL (see
MigrationService::ensureOracleConstraints and nextcloud/server#45576).
Follow the pattern used by nextcloud/mail and declare the invitation
flags as nullable booleans instead.

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Nextcloud requires index names to be 30 characters or shorter
(see MigrationService::ensureOracleConstraints).

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
With the default primary key name the table name (minus prefix) must
be shorter than 23 chars. registration_invitation is exactly 23 chars,
so set an explicit name instead (see
MigrationService::ensureOracleConstraints).

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
Use IQueryBuilder::getColumnName() so the identifier is quoted per
platform (Oracle folds unquoted identifiers to upper case, MySQL does
not accept backticks on other platforms). Fixes ORA-00904 on Oracle.

Signed-off-by: nananankona <185404318+nananankona@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@tcitworld tcitworld left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please bump the app version so that the migrations run.

$invitation = $this->invitationService->getById($invitationId);
$this->invitationService->validate($invitation, $registration->getEmail());
} catch (DoesNotExistException $e) {
// The invitation was deleted in the meantime, continue without it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really go on with the account creation if the invitation has been deleted in the meantime?

$invitation->setCode($code);
$invitation->setEmail($email !== null && $email !== '' ? strtolower($email) : null);
$invitation->setDomain($domain !== null && $domain !== '' ? strtolower($domain) : null);
$invitation->setQuota($quota !== null && $quota !== '' ? $quota : null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quota should be validated first here with OCP\Util::computerFileSize before being saved

$invitation->setQuota($quota !== null && $quota !== '' ? $quota : null);
$invitation->setMaxUses($maxUses);
$invitation->setUses(0);
$invitation->setExpires($expires);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Date should be validated as well

}

#[PublicPage]
#[NoCSRFRequired]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add AnonRateLimit and BruteForceProtection attributes to avoid attackers guessing simple invite codes.

Comment on lines +46 to +50
try {
$this->invitationMapper->findByCode($code);
throw new RegistrationException($this->l10n->t('An invitation with this code already exists.'));
} catch (DoesNotExistException $e) {
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing a read then a write, simply catch the UniqueConstraintViolationException on insertion and throw RegistrationException then.

Comment thread src/AdminSettings.vue
Comment on lines +56 to +62
<NcTextField
v-model="allowedEmails"
:label="emailListLabel"
:labelVisible="true"
:disabled="loading"
placeholder="user1@example.com;user2@example.com"
@update:modelValue="debounceSavingSlow" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what's the use case for this: if you have specific e-mail adresses to allow, why not just create accounts for them?

@tcitworld

Copy link
Copy Markdown
Member

@nananankona Are you affiliated with https://github.com/LukasTechhelpSupport2907/invite-registration ? If not, did you test it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants