4
4
* See License.AGPL.txt in the project root for license information.
5
5
*/
6
6
7
+ import * as crypto from "crypto" ;
7
8
import { IDEFrontendDashboardService } from "@gitpod/gitpod-protocol/lib/frontend-dashboard-service" ;
8
9
import { RemoteTrackMessage } from "@gitpod/gitpod-protocol/lib/analytics" ;
9
10
import { Emitter } from "@gitpod/gitpod-protocol/lib/util/event" ;
10
11
import { workspaceUrl , serverUrl } from "./urls" ;
11
12
12
13
export class FrontendDashboardServiceClient implements IDEFrontendDashboardService . IClient {
13
- public latestStatus ! : IDEFrontendDashboardService . Status ;
14
+ public latestInfo ! : IDEFrontendDashboardService . Info ;
15
+ private credentialsToken ?: Uint8Array ;
14
16
15
- private readonly onDidChangeEmitter = new Emitter < IDEFrontendDashboardService . Status > ( ) ;
16
- readonly onStatusUpdate = this . onDidChangeEmitter . event ;
17
+ private readonly onDidChangeEmitter = new Emitter < IDEFrontendDashboardService . Info > ( ) ;
18
+ readonly onInfoUpdate = this . onDidChangeEmitter . event ;
17
19
18
20
private readonly onOpenBrowserIDEEmitter = new Emitter < void > ( ) ;
19
21
readonly onOpenBrowserIDE = this . onOpenBrowserIDEEmitter . event ;
@@ -28,11 +30,16 @@ export class FrontendDashboardServiceClient implements IDEFrontendDashboardServi
28
30
if ( event . origin !== serverUrl . url . origin ) {
29
31
return ;
30
32
}
31
- if ( IDEFrontendDashboardService . isStatusUpdateEventData ( event . data ) ) {
33
+ if ( IDEFrontendDashboardService . isInfoUpdateEventData ( event . data ) ) {
32
34
this . version = event . data . version ;
33
- this . latestStatus = event . data . status ;
35
+ this . latestInfo = event . data . info ;
36
+ if ( event . data . info . credentialsToken ?. length > 0 ) {
37
+ this . credentialsToken = Uint8Array . from ( atob ( event . data . info . credentialsToken ) , ( c ) =>
38
+ c . charCodeAt ( 0 ) ,
39
+ ) ;
40
+ }
34
41
this . resolveInit ( ) ;
35
- this . onDidChangeEmitter . fire ( this . latestStatus ) ;
42
+ this . onDidChangeEmitter . fire ( this . latestInfo ) ;
36
43
}
37
44
if ( IDEFrontendDashboardService . isRelocateEventData ( event . data ) ) {
38
45
window . location . href = event . data . url ;
@@ -46,6 +53,50 @@ export class FrontendDashboardServiceClient implements IDEFrontendDashboardServi
46
53
return this . initPromise ;
47
54
}
48
55
56
+ decrypt ( str : string ) : string {
57
+ if ( ! this . credentialsToken ) {
58
+ throw new Error ( "no credentials token available" ) ;
59
+ }
60
+ const obj = JSON . parse ( str ) ;
61
+ if ( ! isSerializedEncryptedData ( obj ) ) {
62
+ throw new Error ( "incorrect encrypted data" ) ;
63
+ }
64
+ const data = {
65
+ ...obj ,
66
+ iv : Buffer . from ( obj . iv , "base64" ) ,
67
+ tag : Buffer . from ( obj . tag , "base64" ) ,
68
+ } ;
69
+ const decipher = crypto . createDecipheriv ( "aes-256-gcm" , this . credentialsToken , data . iv ) ;
70
+ decipher . setAuthTag ( data . tag ) ;
71
+ const decrypted = decipher . update ( data . encrypted , "hex" , "utf8" ) ;
72
+ return decrypted + decipher . final ( "utf8" ) ;
73
+ }
74
+
75
+ encrypt ( content : string ) : string {
76
+ if ( ! this . credentialsToken ) {
77
+ throw new Error ( "no credentials token available" ) ;
78
+ }
79
+ const iv = crypto . randomBytes ( 12 ) ;
80
+ const cipher = crypto . createCipheriv ( "aes-256-gcm" , this . credentialsToken , iv ) ;
81
+ let encrypted = cipher . update ( content , "utf8" , "hex" ) ;
82
+ encrypted += cipher . final ( "hex" ) ;
83
+ const tag = cipher . getAuthTag ( ) ;
84
+ return JSON . stringify ( {
85
+ iv : iv . toString ( "base64" ) ,
86
+ tag : tag . toString ( "base64" ) ,
87
+ encrypted,
88
+ } ) ;
89
+ }
90
+
91
+ isEncryptedData ( content : string ) : boolean {
92
+ try {
93
+ const obj = JSON . parse ( content ) ;
94
+ return isSerializedEncryptedData ( obj ) ;
95
+ } catch ( e ) {
96
+ return false ;
97
+ }
98
+ }
99
+
49
100
trackEvent ( msg : RemoteTrackMessage ) : void {
50
101
const debugWorkspace = workspaceUrl . debugWorkspace ;
51
102
msg . properties = { ...msg . properties , debugWorkspace } ;
@@ -78,3 +129,13 @@ export class FrontendDashboardServiceClient implements IDEFrontendDashboardServi
78
129
) ;
79
130
}
80
131
}
132
+
133
+ function isSerializedEncryptedData ( obj : any ) : obj is { iv : string ; encrypted : string ; tag : string } {
134
+ return (
135
+ obj != null &&
136
+ typeof obj === "object" &&
137
+ typeof obj . iv === "string" &&
138
+ typeof obj . encrypted === "string" &&
139
+ typeof obj . tag === "string"
140
+ ) ;
141
+ }
0 commit comments