import 'package:clock/clock.dart'; import 'package:halkyon_base_server/base.dart'; import 'package:halkyon_oidc_server/testing.dart'; import 'package:serverpod/serverpod.dart'; import 'package:serverpod_auth_idp_server/core.dart'; import '../test_tools/serverpod_test_tools.dart'; /// The app the tests' host is. const testAppName = 'Halkyon Test'; /// The id of the app the tests' host serves. const testApp = 'test'; /// Where the tests' host says its project publishes its source. const testUpstreamSourceCodeUrl = 'https://code.example.org/halkyon_test'; /// The oldest app build the tests' host works with. const testMinimumAppBuild = 7; /// Sets the module up on the pod [sessionBuilder]'s sessions run on, as a /// host does before its pod starts, for [app], with [deletePersonData] as /// its deletions, [profilePageNote] at the foot of profile pages and /// [privacyPagePoints] on the privacy page, [files] as its file store, and /// authentication exactly as hosts run it /// (`auth_config.dart`). Not on `Serverpod.instance`: that is the pod made /// last, and a test file with several `withServerpod` groups has a pod each. void useBase( TestSessionBuilder sessionBuilder, { String app = testApp, List deletePersonData = const [], ProfilePageNote? profilePageNote, List privacyPagePoints = const [], AccountsMode accounts = const HoldsRegister(), FileStore? files, SpaceGrants? spaces, }) { final pod = sessionBuilder.build().serverpod; HalkyonBase.configure( pod, HalkyonBase( app: app, appName: testAppName, upstreamSourceCodeUrl: testUpstreamSourceCodeUrl, minimumAppBuild: testMinimumAppBuild, deletePersonData: deletePersonData, profilePageNote: profilePageNote, privacyPagePoints: privacyPagePoints, accounts: accounts, files: files, spaces: spaces, ), ); // No password sign-in unless a test gives it keys, and no SimpleLogin // unless a test gives it an app, whatever the test run's passwords and // environment hold. usePasswordKeys(pod, null); useOfferedWays(pod, null); useSimpleLoginApp(pod, null); useAgents(pod, null); AuthServices.set( tokenManagerBuilders: tokenManagerBuilders, identityProviderBuilders: identityProviderBuilders(pod), ); } /// SimpleLogin on the pod [sessionBuilder]'s sessions run on (ADR 0020), the /// instance's app answered by [fake] (none for null), and authentication set /// up anew as hosts run it. void useSimpleLogin(TestSessionBuilder sessionBuilder, FakeProvider? fake) { final pod = sessionBuilder.build().serverpod; useSimpleLoginApp( pod, fake == null ? null : ( clientId: fake.clientId, clientSecret: fake.clientSecret, callbackUrl: fake.callbackUrl, ), client: fake?.client, ); AuthServices.set( tokenManagerBuilders: tokenManagerBuilders, identityProviderBuilders: identityProviderBuilders(pod), ); } /// Signs [person] in on a new device and returns the new session's id. Future signInNewDevice( Session session, UuidValue person, { Set scopes = const {}, String method = 'test', }) async { Future> sessionIds() async => { for (final serverSideSession in await ServerSideSession.db.find( session, where: (t) => t.authUserId.equals(person), )) serverSideSession.id!, }; final before = await sessionIds(); await AuthServices.instance.tokenManager.createToken( session, authUserId: person, method: method, scopes: scopes, ); return (await sessionIds()).difference(before).single; } /// A person as codes by mail left them before passwords (ADR 0017): /// identified, with [address] as their only way in, and no password; with /// [handle] where given (ADR 0020, D5), their profile private. Future newPersonWithAddress( Session session, String address, { String? handle, }) async { final person = (await AuthServices.instance.authUsers.create( session, scopes: {identified}, )).id; // INSERT INTO "email_identity" ("authUserId", "addressHash", "createdAt") // VALUES ($1, $2, $3) await EmailIdentity.db.insertRow( session, EmailIdentity( authUserId: person, addressHash: addressHash(session.serverpod, address), createdAt: clock.now(), ), ); if (handle != null) { // INSERT INTO "public_profile" (…) VALUES (…) await PublicProfile.db.insertRow( session, PublicProfile( authUserId: person, handle: handle, handleKey: handle.toLowerCase(), displayName: '', bio: '', links: const [], verifiedLinks: const [], updatedAt: clock.now(), ), ); } return person; } /// An anonymous account, as apps made them on their first start until /// ADR 0020, D25, left from before. Future newAnonymousPerson( Session session, { DateTime? createdAt, }) async { final person = (await AuthServices.instance.authUsers.create(session)).id; await AnonymousAccount.db.insertRow( session, AnonymousAccount(authUserId: person, createdAt: createdAt), ); return person; }