Skip to content

Commit

Permalink
jira cloud pat
Browse files Browse the repository at this point in the history
  • Loading branch information
nk-coding committed Jul 23, 2024
1 parent ef1aaaa commit e29ef3a
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 7 deletions.
7 changes: 1 addition & 6 deletions backend/src/strategies/github-token/github-token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class GithubTokenStrategyService extends Strategy {
* - graphql-url: The URL of the github graphql endpoint.
* If imsTemplatedFieldsFilter not given, defaults to "https://api.github.com/graphql"
*
* @param instanceConfig The instance config for a github strategy instance to check
* @param instanceConfig The instance config for a github-token strategy instance to check
* @returns The extended config (with default parameters for the global github) if check successful
*/
protected override checkAndExtendInstanceConfig(instanceConfig: object): object {
Expand Down Expand Up @@ -112,11 +112,6 @@ export class GithubTokenStrategyService extends Strategy {
override getCensoredInstanceConfig(instance: StrategyInstance): object {
return {
imsTemplatedFieldsFilter: instance.instanceConfig["imsTemplatedFieldsFilter"],
authorizationUrl: instance.instanceConfig["authorizationUrl"],
tokenUrl: instance.instanceConfig["tokenUrl"],
userProfileUrl: instance.instanceConfig["userProfileUrl"],
clientId: instance.instanceConfig["clientId"],
clientSecret: "**********",
};
}

Expand Down
203 changes: 203 additions & 0 deletions backend/src/strategies/jira-token-cloud/jira-token-cloud.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { PerformAuthResult, Strategy, StrategyUpdateAction, StrategyVariable } from "../Strategy";
import { OAuthAuthorizeServerState } from "src/api-oauth/OAuthAuthorizeServerState";
import { StrategyInstance } from "src/model/postgres/StrategyInstance.entity";
import { AuthStateServerData } from "../AuthResult";
import { Schema } from "jtd";
import { StrategiesService } from "src/model/services/strategies.service";
import { StrategyInstanceService } from "src/model/services/strategy-instance.service";
import { UserLoginData } from "src/model/postgres/UserLoginData.entity";
import { UserLoginDataService } from "src/model/services/user-login-data.service";

@Injectable()
export class JiraTokenCloudStrategyService extends Strategy {
constructor(
strategiesService: StrategiesService,
strategyInstanceService: StrategyInstanceService,
private readonly loginDataService: UserLoginDataService,
) {
super("jira-token-cloud", strategyInstanceService, strategiesService, false, true, false, false, false);
}

override get instanceConfigSchema(): Record<string, Schema> {
return {
imsTemplatedFieldsFilter: {
properties: {
"root-url": { type: "string" },
},
},
};
}

override get acceptsVariables(): StrategyVariable[] {
return [
{
name: "email",
displayName: "Email",
type: "string",
},
{
name: "token",
displayName: "API token",
type: "password",
},
];
}

override get updateActions(): StrategyUpdateAction[] {
return [
{
name: "update-token",
displayName: "Update API token",
variables: [
{
name: "token",
displayName: "API token",
type: "password",
},
{
name: "email",
displayName: "Email (if changed)",
type: "string",
nullable: true,
},
],
},
];
}

/**
* Chechs the given config is valid for a jira
*
* Needed parameters
* - imsTemplatedFieldsFilter containing:
* - root-url: The URL of the jira root endpoint, must be provided.
*
* @param instanceConfig The instance config for a jira-token-cloud strategy instance to check
* @returns The extended config if check successful
*/
protected override checkAndExtendInstanceConfig(instanceConfig: object): object {
const resultingConfig = instanceConfig;

if (resultingConfig["imsTemplatedFieldsFilter"]) {
const rootUrl = resultingConfig["imsTemplatedFieldsFilter"]["root-url"];
if (!rootUrl) {
throw new Error("At least Jira URL must be given in imsTemplatedFieldsFilter");
}
} else {
throw new Error("At least imsTemplatedFieldsFilter must be given");
}

return super.checkAndExtendInstanceConfig(instanceConfig);
}

override async getSyncDataForLoginData(
loginData: UserLoginData,
): Promise<{ token: string | null; [key: string]: any }> {
return { token: loginData.data["apiToken"] ?? null, type: "PAT" };
}

override getImsUserTemplatedValuesForLoginData(loginData: UserLoginData): object {
return {
jira_id: loginData.data["jira_id"],
username: loginData.data["username"],
displayName: loginData.data["displayName"],
email: loginData.data["email"],
};
}

override getLoginDataDataForImsUserTemplatedFields(imsUser: object): object | Promise<object> {
return {
jira_id: imsUser["jira_id"],
};
}

override async getLoginDataDescription(loginData: UserLoginData): Promise<string> {
return loginData.data?.email;
}

override getCensoredInstanceConfig(instance: StrategyInstance): object {
return {
imsTemplatedFieldsFilter: instance.instanceConfig["imsTemplatedFieldsFilter"],
};
}

private async getUserData(
token: string,
email: string,
strategyInstance: StrategyInstance,
): Promise<{
jira_id: string;
username: string;
displayName: string;
email: string;
} | null> {
const response = await fetch(
new URL("/rest/api/2/myself", strategyInstance.instanceConfig["imsTemplatedFieldsFilter"]["root-url"]),
{
method: "GET",
headers: {
Authorization: `Basic ${btoa(`${email}:${token}`)}`,
Accept: "application/json",
},
},
);

if (!response.ok) {
return null;
}

const userData = await response.json();

return {
jira_id: userData.accountId,
username: "",
displayName: userData.displayName,
email,
};
}

override async performAuth(
strategyInstance: StrategyInstance,
state: (AuthStateServerData & OAuthAuthorizeServerState) | undefined,
req: any,
res: any,
): Promise<PerformAuthResult> {
const token = req.query["token"];
const email = req.query["email"];

const userLoginData = await this.getUserData(token, email, strategyInstance);
if (userLoginData == null) {
return { result: null, returnedState: {}, info: { message: "Token invalid" } };
}

return {
result: {
dataActiveLogin: {},
dataUserLoginData: userLoginData,
mayRegister: true,
},
returnedState: {},
info: {},
};
}

override async handleAction(loginData: UserLoginData, name: string, data: Record<string, any>): Promise<void> {
if (name === "update-token") {
const apiToken = data["token"];
const email = data["email"] || loginData.data["email"];
const userLoginData = await this.getUserData(apiToken, email, await loginData.strategyInstance);
if (userLoginData == null) {
throw new HttpException("Token invalid", HttpStatus.BAD_REQUEST);
}
if (loginData.data["jira_id"] !== userLoginData.jira_id) {
throw new HttpException("Token does not match the user", HttpStatus.BAD_REQUEST);
}
loginData.data["apiToken"] = apiToken;
loginData.data["email"] = email;
this.loginDataService.save(loginData);
} else {
throw new HttpException("Unknown action", HttpStatus.BAD_REQUEST);
}
}
}
6 changes: 5 additions & 1 deletion backend/src/strategies/jira/jira.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class JiraStrategyService extends StrategyUsingPassport {
} else {
this.loggerJira.log("Refreshed token valid");
firstLogin = await this.activeLoginService.save(firstLogin);
return { token: firstLogin?.data["accessToken"] ?? null, cloudIds: cloudIds };
return { token: firstLogin?.data["accessToken"] ?? null, cloudIds: cloudIds, type: "OAUTH" };
}
} else {
this.loggerJira.log("Non valid cloud IDs, and no refresh token token");
Expand Down Expand Up @@ -271,6 +271,10 @@ export class JiraStrategyService extends StrategyUsingPassport {
return new passportJira(config, this.passportUserCallback.bind(this, strategyInstance));
}

override async getLoginDataDescription(loginData: UserLoginData): Promise<string> {
return loginData.data?.email;
}

override getCensoredInstanceConfig(instance: StrategyInstance): object {
return {
imsTemplatedFieldsFilter: instance.instanceConfig["imsTemplatedFieldsFilter"],
Expand Down
2 changes: 2 additions & 0 deletions backend/src/strategies/strategies.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BackendServicesModule } from "src/backend-services/backend-services.mod
import { GithubStrategyService } from "./github/github.service";
import { JiraStrategyService } from "./jira/jira.service";
import { GithubTokenStrategyService } from "./github-token/github-token.service";
import { JiraTokenCloudStrategyService } from "./jira-token-cloud/jira-token-cloud.service";

@Module({
imports: [
Expand Down Expand Up @@ -36,6 +37,7 @@ import { GithubTokenStrategyService } from "./github-token/github-token.service"
GithubStrategyService,
JiraStrategyService,
GithubTokenStrategyService,
JiraTokenCloudStrategyService,
{ provide: "StateJwtService", useExisting: JwtService },
StrategiesMiddleware,
],
Expand Down

0 comments on commit e29ef3a

Please sign in to comment.