mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding GristConnect login system
Summary: New login system to allow simple SSO flow that is based on Discourse description that is available at: https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045 Test Plan: New core test. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3418
This commit is contained in:
@@ -47,6 +47,9 @@ export class User extends BaseEntity {
|
||||
@Column({name: 'options', type: nativeValues.jsonEntityType, nullable: true})
|
||||
public options: UserOptions | null;
|
||||
|
||||
@Column({name: 'connect_id', type: String, nullable: true})
|
||||
public connectId: string | null;
|
||||
|
||||
/**
|
||||
* Get user's email. Returns undefined if logins has not been joined, or no login
|
||||
* is available
|
||||
|
||||
@@ -476,6 +476,60 @@ export class HomeDBManager extends EventEmitter {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that user with external id exists and updates its profile and email if necessary.
|
||||
*
|
||||
* @param profile External profile
|
||||
*/
|
||||
public async ensureExternalUser(profile: UserProfile) {
|
||||
await this._connection.transaction(async manager => {
|
||||
// First find user by the connectId from the profile
|
||||
const existing = await manager.findOne(User, { connectId: profile.connectId}, {relations: ["logins"]});
|
||||
|
||||
// If a user does not exist, create it with data from the external profile.
|
||||
if (!existing) {
|
||||
const newUser = await this.getUserByLoginWithRetry(profile.email, {
|
||||
profile,
|
||||
manager
|
||||
});
|
||||
if (!newUser) {
|
||||
throw new ApiError("Unable to create user", 500);
|
||||
}
|
||||
// No need to survey this user.
|
||||
newUser.isFirstTimeUser = false;
|
||||
await newUser.save();
|
||||
} else {
|
||||
// Else update profile and login information from external profile.
|
||||
let updated = false;
|
||||
let login: Login = existing.logins[0]!;
|
||||
const properEmail = normalizeEmail(profile.email);
|
||||
|
||||
if (properEmail !== existing.loginEmail) {
|
||||
login = login ?? new Login();
|
||||
login.email = properEmail;
|
||||
login.displayEmail = profile.email;
|
||||
existing.logins.splice(0, 1, login);
|
||||
login.user = existing;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (profile?.name && profile?.name !== existing.name) {
|
||||
existing.name = profile.name;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (profile?.picture && profile?.picture !== existing.picture) {
|
||||
existing.picture = profile.picture;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
await manager.save([existing, login]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async updateUser(userId: number, props: UserProfileChange): Promise<void> {
|
||||
let isWelcomed: boolean = false;
|
||||
let user: User|undefined;
|
||||
@@ -601,6 +655,12 @@ export class HomeDBManager extends EventEmitter {
|
||||
login.displayEmail = profile.email;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (profile?.connectId && profile?.connectId !== user.connectId) {
|
||||
user.connectId = profile.connectId;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (!login.displayEmail) {
|
||||
// Save some kind of display email if we don't have anything at all for it yet.
|
||||
// This could be coming from how someone wrote it in a UserManager dialog, for
|
||||
|
||||
22
app/gen-server/migration/1652277549983-UserConnectId.ts
Normal file
22
app/gen-server/migration/1652277549983-UserConnectId.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {MigrationInterface, QueryRunner, TableColumn, TableIndex} from "typeorm";
|
||||
|
||||
export class UserConnectId1652277549983 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.addColumn("users", new TableColumn({
|
||||
name: "connect_id",
|
||||
type: 'varchar',
|
||||
isNullable: true,
|
||||
isUnique: true,
|
||||
}));
|
||||
await queryRunner.createIndex("users", new TableIndex({
|
||||
name: "users_connect_id",
|
||||
columnNames: ["connect_id"],
|
||||
isUnique: true
|
||||
}));
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.dropIndex("users", "users_connect_id");
|
||||
await queryRunner.dropColumn("users", "connect_id");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user