@@ -3,15 +3,26 @@ import { nanoid } from 'nanoid';
3
3
import got from 'got' ;
4
4
5
5
import gotInstance from './common/request' ;
6
- import { AuthFailResult , AuthSuccessResult , CodingResponse , UserResponse } from './typings/respResult' ;
6
+ import { AuthFailResult , AuthSuccessResult , CodingResponse } from './typings/respResult' ;
7
7
import { PromiseAdapter , promiseFromEvent , parseQuery , parseCloneUrl } from './common/utils' ;
8
8
import { GitService } from './common/gitService' ;
9
- import { RepoInfo , SessionData } from './typings/commonTypes' ;
9
+ import { RepoInfo , SessionData , TokenType } from './typings/commonTypes' ;
10
+ import { keychain } from './common/keychain' ;
11
+ import Logger from './common/logger' ;
10
12
11
13
const AUTH_SERVER = `https://x5p7m.csb.app` ;
12
14
const ClientId = `ff768664c96d04235b1cc4af1e3b37a8` ;
13
15
const ClientSecret = `d29ebb32cab8b5f0a643b5da7dcad8d1469312c7` ;
14
16
17
+ export const ScopeList = [
18
+ `user` ,
19
+ `user:email` ,
20
+ `project` ,
21
+ `project:depot` ,
22
+ ] ;
23
+ const SCOPES = ScopeList . join ( `,` ) ;
24
+ const NETWORK_ERROR = 'network error' ;
25
+
15
26
class UriEventHandler extends vscode . EventEmitter < vscode . Uri > implements vscode . UriHandler {
16
27
public handleUri ( uri : vscode . Uri ) {
17
28
this . fire ( uri ) ;
@@ -25,25 +36,67 @@ const onDidManuallyProvideToken = new vscode.EventEmitter<string>();
25
36
export class CodingServer {
26
37
private _pendingStates = new Map < string , string [ ] > ( ) ;
27
38
private _codeExchangePromises = new Map < string , Promise < AuthSuccessResult > > ( ) ;
28
- private _session : SessionData = { } as SessionData ;
29
- private _repo : RepoInfo = {
30
- team : `` ,
31
- project : `` ,
32
- repo : `` ,
33
- } ;
39
+ private _repo : RepoInfo | undefined ;
34
40
private _loggedIn : boolean = false ;
41
+ private _session : SessionData | null = null ;
35
42
36
- constructor ( session ?: SessionData | null , repo ?: RepoInfo | null ) {
37
- if ( session ) {
38
- this . _session = session ;
39
- this . _loggedIn = true ;
40
- }
43
+ constructor ( repo ?: RepoInfo | null ) {
41
44
if ( repo ) {
42
45
this . _repo = repo ;
43
46
}
44
47
}
45
48
46
- public async login ( team : string , scopes : string ) {
49
+ public async initialize ( context : vscode . ExtensionContext ) : Promise < void > {
50
+ try {
51
+ this . _session = await this . _readSessions ( ) ;
52
+ } catch ( e ) {
53
+ // Ignore, network request failed
54
+ }
55
+ }
56
+
57
+ private async _readSessions ( ) : Promise < SessionData | null > {
58
+ const [ accessToken , refreshToken ] = await Promise . all ( [
59
+ keychain . getToken ( TokenType . AccessToken ) ,
60
+ keychain . getToken ( TokenType . RefreshToken ) ,
61
+ ] ) ;
62
+ if ( accessToken && refreshToken ) {
63
+ try {
64
+ const session = await this . getSessionData ( accessToken as TokenType . AccessToken , refreshToken as TokenType . RefreshToken ) ;
65
+ return session ;
66
+ } catch ( e ) {
67
+ if ( e === NETWORK_ERROR ) {
68
+ return null ;
69
+ }
70
+
71
+ Logger . error ( `Error reading sessions: ${ e } ` ) ;
72
+ await keychain . deleteToken ( TokenType . AccessToken ) ;
73
+ }
74
+ }
75
+
76
+ return null ;
77
+ }
78
+
79
+ public async getSessionData ( accessToken : TokenType . AccessToken , refreshToken : TokenType . RefreshToken ) : Promise < SessionData > {
80
+ try {
81
+ const result = await this . getUserInfo ( this . _repo ?. team || `` , accessToken ) ;
82
+ const { data : userInfo } = result ;
83
+ const ret : SessionData = {
84
+ id : nanoid ( ) ,
85
+ user : userInfo ,
86
+ accessToken,
87
+ refreshToken,
88
+ } ;
89
+
90
+ vscode . window . showInformationMessage ( `USER ${ userInfo . name } @ TEAM ${ userInfo . team } ` ) ;
91
+ return ret ;
92
+ } catch ( err ) {
93
+ console . error ( err ) ;
94
+ throw new Error ( err ) ;
95
+ }
96
+ }
97
+
98
+
99
+ public async startOAuth ( team : string , scopes : string ) {
47
100
const state = nanoid ( ) ;
48
101
const { name, publisher } = require ( '../package.json' ) ;
49
102
const callbackUri = await vscode . env . asExternalUri ( vscode . Uri . parse ( `${ vscode . env . uriScheme } ://${ publisher } .${ name } /on-did-authenticate` ) ) ;
@@ -69,16 +122,35 @@ export class CodingServer {
69
122
} ) ;
70
123
}
71
124
125
+ public async login ( team : string , scopes : string = SCOPES ) : Promise < SessionData | null > {
126
+ const { access_token : accessToken , refresh_token : refreshToken } = await this . startOAuth ( team , scopes ) ;
127
+ if ( accessToken && refreshToken ) {
128
+ try {
129
+ const session = await this . getSessionData ( accessToken , refreshToken ) ;
130
+ this . _session = session ;
131
+ await Promise . all ( [
132
+ keychain . setToken ( accessToken , TokenType . AccessToken ) ,
133
+ keychain . setToken ( refreshToken , TokenType . RefreshToken ) ,
134
+ ] ) ;
135
+ return session ;
136
+ } catch ( e ) {
137
+ throw new Error ( e ) ;
138
+ }
139
+ }
140
+
141
+ return null ;
142
+ }
143
+
72
144
private _exchangeCodeForToken : ( team : string , scopes : string ) => PromiseAdapter < vscode . Uri , AuthSuccessResult > = ( team , scopes ) => async ( uri , resolve , reject ) => {
73
145
const query = parseQuery ( uri ) ;
74
146
const { code } = query ;
75
147
76
- const acceptedStates = this . _pendingStates . get ( scopes ) || [ ] ;
77
- if ( ! acceptedStates . includes ( query . state ) ) {
78
- console . error ( `Received mismatched state` ) ;
79
- reject ( { } ) ;
80
- return ;
81
- }
148
+ // const acceptedStates = this._pendingStates.get(scopes) || [];
149
+ // if (!acceptedStates.includes(query.state)) {
150
+ // console.error(`Received mismatched state`);
151
+ // reject({});
152
+ // return;
153
+ // }
82
154
83
155
try {
84
156
const result = await got . post (
@@ -104,7 +176,7 @@ export class CodingServer {
104
176
}
105
177
} ;
106
178
107
- public async getUserInfo ( team : string , token : string = this . _session ?. accessToken ) {
179
+ public async getUserInfo ( team : string , token : string = this . _session ?. accessToken || `` ) {
108
180
try {
109
181
const result : CodingResponse = await gotInstance . get ( `https://${ team } .coding.net/api/current_user` , {
110
182
searchParams : {
@@ -131,9 +203,9 @@ export class CodingServer {
131
203
}
132
204
133
205
public async getMRList (
134
- team : string = this . _repo . team ,
135
- project : string = this . _repo . project ,
136
- repo : string = this . _repo . repo ,
206
+ team : string | undefined = this . _repo ? .team ,
207
+ project : string | undefined = this . _repo ? .project ,
208
+ repo : string | undefined = this . _repo ? .repo ,
137
209
) {
138
210
try {
139
211
const result = await got . get ( `https://${ team } .coding.net/api/user/${ team } /project/${ project } /depot/${ repo } /git/merges/query` , {
@@ -143,7 +215,7 @@ export class CodingServer {
143
215
page : 1 ,
144
216
PageSize : 20 ,
145
217
sortDirection : `DESC` ,
146
- access_token : this . _session . accessToken ,
218
+ access_token : this . _session ? .accessToken ,
147
219
} ,
148
220
} ) . json ( ) ;
149
221
@@ -160,5 +232,18 @@ export class CodingServer {
160
232
get session ( ) {
161
233
return this . _session ;
162
234
}
235
+
236
+ public async logout ( ) {
237
+ try {
238
+ await Promise . all ( [
239
+ keychain . deleteToken ( TokenType . AccessToken ) ,
240
+ keychain . deleteToken ( TokenType . RefreshToken ) ,
241
+ ] ) ;
242
+ this . _session = null ;
243
+ return true ;
244
+ } catch ( e ) {
245
+ throw Error ( e ) ;
246
+ }
247
+ }
163
248
}
164
249
0 commit comments