mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-26 14:26:06 +00:00
feat(cloud): complete secure invitation experience
This commit is contained in:
@@ -86,3 +86,20 @@ async def test_environment_mapping_enables_provider_without_leaking_secret(monke
|
||||
'https://env.langbot.example/invitations/accept#token=lbi_secret'
|
||||
)
|
||||
assert service.capability() == {'enabled': True, 'provider': 'smtp'}
|
||||
|
||||
|
||||
async def test_cloud_invitation_email_has_branded_html_plain_fallback_and_expiry_copy():
|
||||
service = InvitationDeliveryService(_app({}))
|
||||
link = 'https://cloud.langbot.app/invitations/accept#token=lbi_secret&next=<unsafe>'
|
||||
|
||||
text = service._plain_text('Research & Development', link)
|
||||
html = service._html('Research & Development', link)
|
||||
|
||||
assert 'LangBot Cloud' in text
|
||||
assert 'Research & Development' in text
|
||||
assert '7 days' in text
|
||||
assert link in text
|
||||
assert 'Accept invitation' in html
|
||||
assert 'Research & Development' in html
|
||||
assert 'expires in 7 days' in html
|
||||
assert 'lbi_secret&next=<unsafe>' in html
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import uuid
|
||||
from contextlib import asynccontextmanager
|
||||
from types import SimpleNamespace
|
||||
@@ -19,6 +20,7 @@ from langbot.pkg.entity.persistence.workspace import (
|
||||
)
|
||||
from langbot.pkg.workspace.collaboration import (
|
||||
InvitationEmailMismatchError,
|
||||
InvitationExpiredError,
|
||||
InvitationRoleError,
|
||||
InvitationUsedError,
|
||||
LastOwnerError,
|
||||
@@ -159,6 +161,23 @@ async def test_invitation_rejects_owner_role_and_email_mismatch(collaboration_co
|
||||
await service.accept_invitation(created.token, wrong_account.uuid)
|
||||
|
||||
|
||||
async def test_expired_invitation_is_inspectable_before_periodic_cleanup_deletes_it(collaboration_context):
|
||||
service, _, session_factory, _, workspace, owner_membership = collaboration_context
|
||||
created = await service.create_invitation(
|
||||
workspace.uuid, owner_membership, 'expired@example.com', 'viewer'
|
||||
)
|
||||
async with session_factory.begin() as session:
|
||||
invitation = await session.get(WorkspaceInvitation, created.invitation.uuid)
|
||||
invitation.expires_at = service._utcnow() - datetime.timedelta(minutes=1)
|
||||
|
||||
with pytest.raises(InvitationExpiredError):
|
||||
await service.inspect_invitation(created.token)
|
||||
|
||||
assert await service.cleanup_expired_invitations(retention=datetime.timedelta(0)) == 1
|
||||
async with session_factory() as session:
|
||||
assert await session.get(WorkspaceInvitation, created.invitation.uuid) is None
|
||||
|
||||
|
||||
async def test_last_owner_cannot_be_demoted(collaboration_context):
|
||||
service, _, session_factory, _, workspace, owner_membership = collaboration_context
|
||||
created = await service.create_invitation(
|
||||
|
||||
Reference in New Issue
Block a user