-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[server] add OAuth2 server endpoints (behind a feature flag)
to manage client application access to users Gitpod workspaces
- Loading branch information
Showing
32 changed files
with
957 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* Copyright (c) 2021 Gitpod GmbH. All rights reserved. | ||
* Licensed under the GNU Affero General Public License (AGPL). | ||
* See License-AGPL.txt in the project root for license information. | ||
*/ | ||
|
||
import gitpodIcon from './icons/gitpod.svg'; | ||
import { getSafeURLRedirect } from "./provider-utils"; | ||
|
||
export default function OAuthClientApproval() { | ||
const params = new URLSearchParams(window.location.search); | ||
const clientName = params.get("clientName") || ""; | ||
let redirectToParam = params.get("redirectTo") || undefined; | ||
if (redirectToParam) { | ||
redirectToParam = decodeURIComponent(redirectToParam); | ||
} | ||
const redirectTo = getSafeURLRedirect(redirectToParam) || "/"; | ||
const updateClientApproval = async (isApproved: boolean) => { | ||
if (redirectTo === "/") { | ||
window.location.replace(redirectTo); | ||
} | ||
window.location.replace(`${redirectTo}&approved=${isApproved ? 'yes' : 'no'}`); | ||
} | ||
|
||
return (<div id="oauth-container" className="z-50 flex w-screen h-screen"> | ||
<div id="oauth-section" className="flex-grow flex w-full"> | ||
<div id="oauth-section-column" className="flex-grow max-w-2xl flex flex-col h-100 mx-auto"> | ||
<div className="flex-grow h-100 flex flex-row items-center justify-center" > | ||
<div className="rounded-xl px-10 py-10 mx-auto"> | ||
<div className="mx-auto pb-8"> | ||
<img src={gitpodIcon} className="h-16 mx-auto" /> | ||
</div> | ||
<div className="mx-auto text-center pb-8 space-y-2"> | ||
<h1 className="text-3xl">Authorize {clientName}</h1> | ||
<h4>You are about to authorize {clientName} to access your Gitpod account including data for all workspaces.</h4> | ||
</div> | ||
<div className="flex justify-center mt-6"> | ||
<button className="secondary" onClick={() => updateClientApproval(false)}>Cancel</button> | ||
<button key={"button-yes"} className="ml-2" onClick={() => updateClientApproval(true)}> | ||
Authorize | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div>); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
## Gitpod-db | ||
|
||
Contains all the database related functionality, implemented using [typeorm](https://typeorm.io/). | ||
|
||
### Adding a new table | ||
1. Create a [migration](./src/typeorm/migration/README.md) - use the [baseline](./src/typeorm/migration/1592203031938-Baseline.ts) as an exemplar | ||
1. Create a new entity that implements the requisite interface or extend an existing entity as required - see [db-user.ts](./src/typeorm/entity/db-user.ts) | ||
1. If it is a new table, create the matching injectable ORM implementation and interface (if required) - see [user-db-impl.ts](./src/typeorm/user-db-impl.ts) and [user-db.ts](./src/user-db.ts). Otherwise extend the existing interface and implementation as required. | ||
1. Add the injectable implementation to the [DB container module](./src/container-module.ts), binding the interface and implementation as appropriate, otherwise it will not be instantiated correctly e.g. | ||
``` | ||
bind(TypeORMUserDBImpl).toSelf().inSingletonScope(); | ||
bind(UserDB).toService(TypeORMUserDBImpl); | ||
``` | ||
1. Add the new ORM as an injected component where required e.g. in [user-controller.ts](./src/user/user-controller.ts) | ||
``` | ||
@inject(UserDB) protected readonly userDb: UserDB; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
components/gitpod-db/src/typeorm/auth-code-repository-db.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/** | ||
* Copyright (c) 2021 Gitpod GmbH. All rights reserved. | ||
* Licensed under the GNU Affero General Public License (AGPL). | ||
* See License-AGPL.txt in the project root for license information. | ||
*/ | ||
|
||
import { DateInterval, OAuthAuthCode, OAuthAuthCodeRepository, OAuthClient, OAuthScope, OAuthUser } from "@jmondi/oauth2-server"; | ||
import * as crypto from 'crypto'; | ||
import { inject, injectable } from "inversify"; | ||
import { EntityManager, Repository } from "typeorm"; | ||
import { DBOAuthAuthCodeEntry } from './entity/db-oauth-auth-code'; | ||
import { TypeORM } from './typeorm'; | ||
|
||
const expiryInFuture = new DateInterval("5m"); | ||
|
||
@injectable() | ||
export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository { | ||
|
||
@inject(TypeORM) | ||
private readonly typeORM: TypeORM; | ||
|
||
protected async getEntityManager(): Promise<EntityManager> { | ||
return (await this.typeORM.getConnection()).manager; | ||
} | ||
|
||
async getOauthAuthCodeRepo(): Promise<Repository<DBOAuthAuthCodeEntry>> { | ||
return (await this.getEntityManager()).getRepository<DBOAuthAuthCodeEntry>(DBOAuthAuthCodeEntry); | ||
} | ||
|
||
public async getByIdentifier(authCodeCode: string): Promise<OAuthAuthCode> { | ||
const authCodeRepo = await this.getOauthAuthCodeRepo(); | ||
let authCodes = await authCodeRepo.find({ code: authCodeCode }); | ||
authCodes = authCodes.filter(te => (new Date(te.expiresAt)).getTime() > Date.now()); | ||
const authCode = authCodes.length > 0 ? authCodes[0] : undefined; | ||
if (!authCode) { | ||
throw new Error(`authentication code not found`); | ||
} | ||
return authCode; | ||
} | ||
public issueAuthCode(client: OAuthClient, user: OAuthUser | undefined, scopes: OAuthScope[]): OAuthAuthCode { | ||
const code = crypto.randomBytes(30).toString('hex'); | ||
// NOTE: caller (@jmondi/oauth2-server) is responsible for adding the remaining items, PKCE params, redirect URL, etc | ||
return { | ||
code: code, | ||
user, | ||
client, | ||
expiresAt: expiryInFuture.getEndDate(), | ||
scopes: scopes, | ||
}; | ||
} | ||
public async persist(authCode: OAuthAuthCode): Promise<void> { | ||
const authCodeRepo = await this.getOauthAuthCodeRepo(); | ||
authCodeRepo.save(authCode); | ||
} | ||
public async isRevoked(authCodeCode: string): Promise<boolean> { | ||
const authCode = await this.getByIdentifier(authCodeCode); | ||
return Date.now() > authCode.expiresAt.getTime(); | ||
} | ||
public async revoke(authCodeCode: string): Promise<void> { | ||
const authCode = await this.getByIdentifier(authCodeCode); | ||
if (authCode) { | ||
// Set date to earliest timestamp that MySQL allows | ||
authCode.expiresAt = new Date(1000); | ||
return this.persist(authCode); | ||
} | ||
} | ||
} |
Oops, something went wrong.