SSO for GitHub Enterprise self hosted (#3352)

* SSO for GitHub Enterprise self hosted

* changes

* test cases

* fixes

* label fix

* fixes

* readme changes
This commit is contained in:
Midhun G S 2022-07-29 17:03:42 +05:30 committed by GitHub
parent 0055773ad0
commit b710d7b02e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 556 additions and 49 deletions

View file

@ -53,5 +53,6 @@ ENABLE_MULTIPLAYER_EDITING=true
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=
SSO_ACCEPTED_DOMAINS=
SSO_DISABLE_SIGNUPS=

View file

@ -49,6 +49,7 @@ SSO_RESTRICTED_DOMAIN=
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=
#TELEMETRY
DEPLOYMENT_PLATFORM=docker

View file

@ -195,6 +195,7 @@ Configurations for instance level SSO. Valid only if `DISABLE_MULTI_WORKSPACE` i
| SSO_GOOGLE_OAUTH2_CLIENT_ID | Google OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_ID | GitHub OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_SECRET | GitHub OAuth client secret |
| SSO_GIT_OAUTH2_HOST | GitHub OAuth host name if GitHub is self hosted |
| SSO_ACCEPTED_DOMAINS | comma separated email domains that supports SSO authentication |
| SSO_DISABLE_SIGNUPS | Disable user sign up if authenticated user does not exist |

View file

@ -56,3 +56,18 @@ Go to [GitHub Developer settings](https://github.com/settings/developers) and na
Lastly, enter `Client Id` and `Client Secret` in GitHub manage SSO page and save.
The GitHub sign-in button will now be available in your ToolJet login screen if you have not enabled Multi-Workspace.
:::info
Should configure `Host Name` if you are using GitHub Enterprise self hosted. Host name should be a URL and should not ends with `/`, example: `https://github.tooljet.com`
:::
## Multi-Workspace
If you have enabled Multi-Workspace you can configure GitHub SSO as mentioned above, for setting default SSO for the instance use environment variable.
| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
| SSO_GIT_OAUTH2_CLIENT_ID | GitHub OAuth client id |
| SSO_GIT_OAUTH2_CLIENT_SECRET | GitHub OAuth client secret |
| SSO_GIT_OAUTH2_HOST | GitHub OAuth host name if GitHub is self hosted |
Redirect URL should be `<host>/sso/git`

View file

@ -83,3 +83,12 @@ Set the `Redirect URL` generated at manage SSO `Google` page under Authorised re
Lastly, set the `client id` in google manage SSO page. This value will be available from your [Google cloud console credentials page](https://console.cloud.google.com/apis/credentials)
The Google sign-in button will now be available in your ToolJet login screen, if you are not enabled Multi-Workspace.
## Multi-Workspace
If you have enabled Multi-Workspace you can configure Google SSO as mentioned above, for setting default SSO for the instance use environment variable.
| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
| SSO_GOOGLE_OAUTH2_CLIENT_ID | Google OAuth client id |
Redirect URL should be `<host>/sso/google`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -4,7 +4,7 @@ import { buildURLWithQuery } from '@/_helpers/utils';
export default function GitSSOLoginButton({ configs, text }) {
const gitLogin = (e) => {
e.preventDefault();
window.location.href = buildURLWithQuery('https://github.com/login/oauth/authorize', {
window.location.href = buildURLWithQuery(`${configs.host_name || 'https://github.com'}/login/oauth/authorize`, {
client_id: configs?.client_id,
scope: 'user:email',
});

View file

@ -72,6 +72,7 @@ class LoginPage extends React.Component {
enabled: !!window.public_config?.SSO_GIT_OAUTH2_CLIENT_ID,
configs: {
client_id: window.public_config?.SSO_GIT_OAUTH2_CLIENT_ID,
host_name: window.public_config?.SSO_GIT_OAUTH2_HOST,
},
},
form: {

View file

@ -6,6 +6,7 @@ import { copyToClipboard } from '@/_helpers/appUtils';
export function Git({ settings, updateData }) {
const [enabled, setEnabled] = useState(settings?.enabled || false);
const [clientId, setClientId] = useState(settings?.configs?.client_id || '');
const [hostName, setHostName] = useState(settings?.configs?.host_name || '');
const [clientSecret, setClientSecret] = useState(settings?.configs?.client_secret || '');
const [isSaving, setSaving] = useState(false);
const [configId, setConfigId] = useState(settings?.id);
@ -13,6 +14,7 @@ export function Git({ settings, updateData }) {
const reset = () => {
setClientId(settings?.configs?.client_id || '');
setClientSecret(settings?.configs?.client_secret || '');
setHostName(settings?.configs?.host_name || '');
};
const copyFunction = (input) => {
@ -21,11 +23,14 @@ export function Git({ settings, updateData }) {
};
const saveSettings = () => {
setSaving(true);
organizationService.editOrganizationConfigs({ type: 'git', configs: { clientId, clientSecret } }).then(
organizationService.editOrganizationConfigs({ type: 'git', configs: { clientId, clientSecret, hostName } }).then(
(data) => {
setSaving(false);
data.id && setConfigId(data.id);
updateData('git', { id: data.id, configs: { client_id: clientId, client_secret: clientSecret } });
updateData('git', {
id: data.id,
configs: { client_id: clientId, client_secret: clientSecret, host_name: hostName },
});
toast.success('updated SSO configurations', {
position: 'top-center',
});
@ -86,6 +91,24 @@ export function Git({ settings, updateData }) {
</div>
<div className="card-body">
<form noValidate>
<div className="form-group mb-3">
<label className="form-label" data-cy="host-name-label">
Host Name
</label>
<div>
<input
type="text"
className="form-control"
placeholder="Enter Host Name"
value={hostName}
onChange={(e) => setHostName(e.target.value)}
data-cy="host-name-input"
/>
</div>
<div className="help-text mt-2">
<div data-cy="general-settings-help-text">Required if GitHub is self hosted</div>
</div>
</div>
<div className="form-group mb-3">
<label className="form-label" data-cy="client-id-label">
Client Id

View file

@ -1,17 +1,22 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import got from 'got';
import UserResponse from './models/user_response';
@Injectable()
export class GitOAuthService {
constructor(private readonly configService: ConfigService) {}
private readonly authUrl = 'https://github.com/login/oauth/access_token';
private readonly getUserUrl = 'https://api.github.com/user';
private readonly getUserEmailUrl = 'https://api.github.com/user/emails';
private readonly authUrl = '/login/oauth/access_token';
async #getUserDetails({ access_token }: AuthResponse): Promise<UserResponse> {
const response: any = await got(this.getUserUrl, {
#getAuthUrl(hostName) {
return `${hostName || 'https://github.com'}${this.authUrl}`;
}
#getUserUrl(hostName) {
return `${hostName ? `${hostName}/api/v3` : 'https://github.com'}/user`;
}
#getUserEmailUrl(hostName) {
return `${hostName ? `${hostName}/api/v3` : 'https://github.com'}/user/emails`;
}
async #getUserDetails({ access_token }: AuthResponse, hostName: string): Promise<UserResponse> {
const response: any = await got(this.#getUserUrl(hostName), {
method: 'get',
headers: { Accept: 'application/json', Authorization: `token ${access_token}` },
}).json();
@ -24,14 +29,14 @@ export class GitOAuthService {
if (!email) {
// email visibility not set to public
email = await this.#getEmailId(access_token);
email = await this.#getEmailId(access_token, hostName);
}
return { userSSOId: access_token, firstName, lastName, email, sso: 'git' };
}
async #getEmailId(access_token: string) {
const response: any = await got(this.getUserEmailUrl, {
async #getEmailId(access_token: string, hostName: string) {
const response: any = await got(this.#getUserEmailUrl(hostName), {
method: 'get',
headers: { Accept: 'application/json', Authorization: `token ${access_token}` },
}).json();
@ -40,13 +45,13 @@ export class GitOAuthService {
}
async signIn(code: string, configs: any): Promise<any> {
const response: any = await got(this.authUrl, {
const response: any = await got(this.#getAuthUrl(configs.hostName), {
method: 'post',
headers: { Accept: 'application/json' },
json: { client_id: configs.clientId, client_secret: configs.clientSecret, code },
}).json();
return await this.#getUserDetails(response);
return await this.#getUserDetails(response, configs.hostName);
}
}

View file

@ -128,6 +128,7 @@ export class OauthService {
configs: {
clientId: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_ID'),
clientSecret: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_SECRET'),
hostName: this.configService.get<string>('SSO_GIT_OAUTH2_HOST'),
},
};
default:

View file

@ -15,6 +15,7 @@ type Google = {
type Git = {
clientId: string;
clientSecret: string;
hostName?: string;
};
@Entity({ name: 'sso_configs' })
export class SSOConfigs {

View file

@ -26,6 +26,7 @@ export class AppConfigService {
'DISABLE_MULTI_WORKSPACE',
'SSO_GOOGLE_OAUTH2_CLIENT_ID',
'SSO_GIT_OAUTH2_CLIENT_ID',
'SSO_GIT_OAUTH2_HOST',
'SSO_DISABLE_SIGNUPS',
];
}

View file

@ -250,6 +250,12 @@ export class OrganizationsService {
enabled: true,
configs: {
clientId: this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_ID'),
clientSecret: await this.encryptionService.encryptColumnValue(
'ssoConfigs',
'clientSecret',
this.configService.get<string>('SSO_GIT_OAUTH2_CLIENT_SECRET')
),
hostName: this.configService.get<string>('SSO_GIT_OAUTH2_HOST'),
},
});
}

View file

@ -858,6 +858,174 @@ describe('oauth controller', () => {
organization,
} = response.body;
expect(email).toEqual('anotherUser1@tooljet.io');
expect(first_name).toEqual('SSO');
expect(last_name).toEqual('userExist');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('Common login - should return login info when the user exist and hostname exist in configs', async () => {
jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
switch (key) {
case 'SSO_GOOGLE_OAUTH2_CLIENT_ID':
return 'google-client-id';
case 'SSO_GIT_OAUTH2_CLIENT_ID':
return 'git-client-id';
case 'SSO_GIT_OAUTH2_CLIENT_SECRET':
return 'git-secret';
case 'SSO_GIT_OAUTH2_HOST':
return 'https://github.host.com';
default:
return process.env[key];
}
});
const { orgUser } = await createUser(app, {
firstName: 'SSO',
lastName: 'userExist',
email: 'anotherUser1@tooljet.io',
groups: ['all_users'],
organization: current_organization,
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: 'SSO userExist',
email: 'anotherUser1@tooljet.io',
};
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
const response = await request(app.getHttpServer()).post('/api/oauth/sign-in/common/git').send({ token });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const {
email,
first_name,
last_name,
admin,
group_permissions,
app_group_permissions,
organization_id,
organization,
} = response.body;
expect(email).toEqual('anotherUser1@tooljet.io');
expect(first_name).toEqual('SSO');
expect(last_name).toEqual('userExist');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('Workspace login - should return login info when the user exist and hostname exist in configs', async () => {
jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
switch (key) {
case 'SSO_GOOGLE_OAUTH2_CLIENT_ID':
return 'google-client-id';
case 'SSO_GIT_OAUTH2_CLIENT_ID':
return 'git-client-id';
case 'SSO_GIT_OAUTH2_CLIENT_SECRET':
return 'git-secret';
case 'SSO_GIT_OAUTH2_HOST':
return 'https://github.host.com';
default:
return process.env[key];
}
});
const { orgUser } = await createUser(app, {
firstName: 'SSO',
lastName: 'userExist',
email: 'anotherUser1@tooljet.io',
groups: ['all_users'],
organization: current_organization,
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: 'SSO userExist',
email: 'anotherUser1@tooljet.io',
};
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
const response = await request(app.getHttpServer())
.post('/api/oauth/sign-in/common/git')
.send({ token, organizationId: current_organization.id });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const {
email,
first_name,
last_name,
admin,
group_permissions,
app_group_permissions,
organization_id,
organization,
} = response.body;
expect(email).toEqual('anotherUser1@tooljet.io');
expect(first_name).toEqual('SSO');
expect(last_name).toEqual('userExist');

View file

@ -583,27 +583,164 @@ describe('oauth controller', () => {
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(
[
'id',
'organization_id',
'group',
'app_create',
'app_delete',
'updated_at',
'created_at',
'folder_create',
'org_environment_variable_create',
'org_environment_variable_update',
'org_environment_variable_delete',
'folder_delete',
'folder_update',
].sort()
);
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('should return login info when the user exist and hostname exist in configs', async () => {
await ssoConfigsRepository.update(sso_configs.id, {
configs: { clientId: 'some-client-id', hostName: 'https://github.host.com' },
});
const { orgUser } = await createUser(app, {
firstName: 'SSO',
lastName: 'userExist',
email: 'anotherUser1@tooljet.io',
groups: ['all_users'],
organization: current_organization,
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: 'SSO userExist',
email: 'anotherUser1@tooljet.io',
};
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
const response = await request(app.getHttpServer())
.post('/api/oauth/sign-in/' + sso_configs.id)
.send({ token });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const {
email,
first_name,
last_name,
admin,
group_permissions,
app_group_permissions,
organization_id,
organization,
} = response.body;
expect(email).toEqual('anotherUser1@tooljet.io');
expect(first_name).toEqual('SSO');
expect(last_name).toEqual('userExist');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('should return login info when the user does not exist and email id not available and sign up is enabled, host name configured', async () => {
await ssoConfigsRepository.update(sso_configs.id, {
configs: { clientId: 'some-client-id', hostName: 'https://github.host.com' },
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: '',
email: '',
};
},
};
});
const gitGetUserEmailResponse = jest.fn();
gitGetUserEmailResponse.mockImplementation(() => {
return {
json: () => {
return [
{
email: 'ssoUserGit@tooljet.io',
primary: true,
verified: true,
},
{
email: 'ssoUserGit2@tooljet.io',
primary: false,
verified: true,
},
];
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
mockedGot.mockImplementationOnce(gitGetUserEmailResponse);
const response = await request(app.getHttpServer())
.post('/api/oauth/sign-in/' + sso_configs.id)
.send({ token });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(gitGetUserEmailResponse).toBeCalledWith(
'https://github.host.com/api/v3/user/emails',
expect.anything()
);
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const { email, first_name, admin, group_permissions, app_group_permissions, organization_id, organization } =
response.body;
expect(email).toEqual('ssoUserGit@tooljet.io');
expect(first_name).toEqual('ssoUserGit');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
});
});
});
@ -1122,27 +1259,164 @@ describe('oauth controller', () => {
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(
[
'id',
'organization_id',
'group',
'app_create',
'app_delete',
'updated_at',
'created_at',
'folder_create',
'org_environment_variable_create',
'org_environment_variable_update',
'org_environment_variable_delete',
'folder_delete',
'folder_update',
].sort()
);
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('should return login info when the user exist and hostname exist in configs', async () => {
await ssoConfigsRepository.update(sso_configs.id, {
configs: { clientId: 'some-client-id', hostName: 'https://github.host.com' },
});
const { orgUser } = await createUser(app, {
firstName: 'SSO',
lastName: 'userExist',
email: 'anotherUser1@tooljet.io',
groups: ['all_users'],
organization: current_organization,
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: 'SSO userExist',
email: 'anotherUser1@tooljet.io',
};
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
const response = await request(app.getHttpServer())
.post('/api/oauth/sign-in/' + sso_configs.id)
.send({ token });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const {
email,
first_name,
last_name,
admin,
group_permissions,
app_group_permissions,
organization_id,
organization,
} = response.body;
expect(email).toEqual('anotherUser1@tooljet.io');
expect(first_name).toEqual('SSO');
expect(last_name).toEqual('userExist');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
await orgUser.reload();
expect(orgUser.status).toEqual('active');
});
it('should return login info when the user does not exist and email id not available and sign up is enabled, host name configured', async () => {
await ssoConfigsRepository.update(sso_configs.id, {
configs: { clientId: 'some-client-id', hostName: 'https://github.host.com' },
});
const gitAuthResponse = jest.fn();
gitAuthResponse.mockImplementation(() => {
return {
json: () => {
return {
access_token: 'some-access-token',
scope: 'scope',
token_type: 'bearer',
};
},
};
});
const gitGetUserResponse = jest.fn();
gitGetUserResponse.mockImplementation(() => {
return {
json: () => {
return {
name: '',
email: '',
};
},
};
});
const gitGetUserEmailResponse = jest.fn();
gitGetUserEmailResponse.mockImplementation(() => {
return {
json: () => {
return [
{
email: 'ssoUserGit@tooljet.io',
primary: true,
verified: true,
},
{
email: 'ssoUserGit2@tooljet.io',
primary: false,
verified: true,
},
];
},
};
});
mockedGot.mockImplementationOnce(gitAuthResponse);
mockedGot.mockImplementationOnce(gitGetUserResponse);
mockedGot.mockImplementationOnce(gitGetUserEmailResponse);
const response = await request(app.getHttpServer())
.post('/api/oauth/sign-in/' + sso_configs.id)
.send({ token });
expect(response.statusCode).toBe(201);
expect(gitAuthResponse).toBeCalledWith('https://github.host.com/login/oauth/access_token', expect.anything());
expect(gitGetUserResponse).toBeCalledWith('https://github.host.com/api/v3/user', expect.anything());
expect(gitGetUserEmailResponse).toBeCalledWith(
'https://github.host.com/api/v3/user/emails',
expect.anything()
);
expect(Object.keys(response.body).sort()).toEqual(authResponseKeys);
const { email, first_name, admin, group_permissions, app_group_permissions, organization_id, organization } =
response.body;
expect(email).toEqual('ssoUserGit@tooljet.io');
expect(first_name).toEqual('ssoUserGit');
expect(admin).toBeFalsy();
expect(organization_id).toBe(current_organization.id);
expect(organization).toBe(current_organization.name);
expect(group_permissions).toHaveLength(1);
expect(group_permissions[0].group).toEqual('all_users');
expect(Object.keys(group_permissions[0]).sort()).toEqual(groupPermissionsKeys);
expect(app_group_permissions).toHaveLength(0);
});
});
});
});