@passlock/node 0.9.30 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -21
- package/README.template.md +11 -20
- package/dist/index.d.ts +4 -88
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -80
- package/dist/index.js.map +1 -1
- package/dist/principal/effect.d.ts +53 -0
- package/dist/principal/effect.d.ts.map +1 -0
- package/dist/principal/effect.js +78 -0
- package/dist/principal/effect.js.map +1 -0
- package/dist/principal/index.d.ts +42 -0
- package/dist/principal/index.d.ts.map +1 -0
- package/dist/principal/index.js +60 -0
- package/dist/principal/index.js.map +1 -0
- package/dist/shared.d.ts +39 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +21 -0
- package/dist/shared.js.map +1 -0
- package/dist/user/effect.d.ts +18 -0
- package/dist/user/effect.d.ts.map +1 -0
- package/dist/user/effect.js +27 -0
- package/dist/user/effect.js.map +1 -0
- package/dist/user/index.d.ts +18 -0
- package/dist/user/index.d.ts.map +1 -0
- package/dist/user/index.js +36 -0
- package/dist/user/index.js.map +1 -0
- package/package.json +59 -52
- package/LICENSE +0 -21
- package/dist/config/config.d.ts +0 -9
- package/dist/config/config.js +0 -4
- package/dist/config/config.js.map +0 -1
- package/dist/principal/principal.d.ts +0 -27
- package/dist/principal/principal.fixture.d.ts +0 -15
- package/dist/principal/principal.fixture.js +0 -67
- package/dist/principal/principal.fixture.js.map +0 -1
- package/dist/principal/principal.js +0 -88
- package/dist/principal/principal.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -2
- package/dist/version.js.map +0 -1
- package/src/config/config.ts +0 -10
- package/src/index.ts +0 -159
- package/src/principal/principal.fixture.ts +0 -107
- package/src/principal/principal.test.ts +0 -113
- package/src/principal/principal.ts +0 -160
- package/src/version.ts +0 -1
package/src/index.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { Effect as E, Layer as L, Runtime, Scope, pipe } from 'effect'
|
|
2
|
-
|
|
3
|
-
import { ErrorCode } from '@passlock/shared/dist/error/error.js'
|
|
4
|
-
import type {
|
|
5
|
-
Forbidden,
|
|
6
|
-
InternalServerError,
|
|
7
|
-
NotFound,
|
|
8
|
-
Unauthorized,
|
|
9
|
-
} from '@passlock/shared/dist/error/error.js'
|
|
10
|
-
|
|
11
|
-
import { Config } from './config/config.js'
|
|
12
|
-
import {
|
|
13
|
-
type PrincipalRequest,
|
|
14
|
-
PrincipalService,
|
|
15
|
-
PrincipalServiceLive,
|
|
16
|
-
StreamResponseLive,
|
|
17
|
-
} from './principal/principal.js'
|
|
18
|
-
|
|
19
|
-
export type { PrincipalRequest } from './principal/principal.js'
|
|
20
|
-
|
|
21
|
-
export { ErrorCode } from '@passlock/shared/dist/error/error.js'
|
|
22
|
-
|
|
23
|
-
export class PasslockError extends Error {
|
|
24
|
-
readonly _tag = 'PasslockError'
|
|
25
|
-
readonly code: ErrorCode
|
|
26
|
-
|
|
27
|
-
constructor(message: string, code: ErrorCode) {
|
|
28
|
-
super(message)
|
|
29
|
-
this.code = code
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static readonly isError = (error: unknown): error is PasslockError => {
|
|
33
|
-
return (
|
|
34
|
-
typeof error === 'object' &&
|
|
35
|
-
error !== null &&
|
|
36
|
-
'_tag' in error &&
|
|
37
|
-
error['_tag'] === 'PasslockError'
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
type PasslockErrors = NotFound | Unauthorized | Forbidden | InternalServerError
|
|
43
|
-
|
|
44
|
-
const hasMessage = (defect: unknown): defect is { message: string } => {
|
|
45
|
-
return (
|
|
46
|
-
typeof defect === 'object' &&
|
|
47
|
-
defect !== null &&
|
|
48
|
-
'message' in defect &&
|
|
49
|
-
typeof defect['message'] === 'string'
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const transformErrors = <A, R>(
|
|
54
|
-
effect: E.Effect<A, PasslockErrors, R>,
|
|
55
|
-
): E.Effect<A | PasslockError, never, R> => {
|
|
56
|
-
const withErrorHandling = E.catchTags(effect, {
|
|
57
|
-
NotFound: e => E.succeed(new PasslockError(e.message, ErrorCode.NotFound)),
|
|
58
|
-
Unauthorized: e => E.succeed(new PasslockError(e.message, ErrorCode.Unauthorized)),
|
|
59
|
-
Forbidden: e => E.succeed(new PasslockError(e.message, ErrorCode.Forbidden)),
|
|
60
|
-
InternalServerError: e =>
|
|
61
|
-
E.succeed(new PasslockError(e.message, ErrorCode.InternalServerError)),
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const sandboxed = E.sandbox(withErrorHandling)
|
|
65
|
-
|
|
66
|
-
const withSandboxing = E.catchTags(sandboxed, {
|
|
67
|
-
Die: ({ defect }) => {
|
|
68
|
-
return hasMessage(defect)
|
|
69
|
-
? E.succeed(new PasslockError(defect.message, ErrorCode.InternalServerError))
|
|
70
|
-
: E.succeed(new PasslockError('Sorry, something went wrong', ErrorCode.InternalServerError))
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
Interrupt: () => {
|
|
74
|
-
console.error('Interrupt')
|
|
75
|
-
|
|
76
|
-
return E.succeed(new PasslockError('Operation aborted', ErrorCode.InternalBrowserError))
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
Sequential: errors => {
|
|
80
|
-
console.error(errors)
|
|
81
|
-
|
|
82
|
-
return E.succeed(
|
|
83
|
-
new PasslockError('Sorry, something went wrong', ErrorCode.InternalServerError),
|
|
84
|
-
)
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
Parallel: errors => {
|
|
88
|
-
console.error(errors)
|
|
89
|
-
|
|
90
|
-
return E.succeed(
|
|
91
|
-
new PasslockError('Sorry, something went wrong', ErrorCode.InternalServerError),
|
|
92
|
-
)
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
return E.unsandbox(withSandboxing)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
type Requirements = PrincipalService
|
|
100
|
-
|
|
101
|
-
export class PasslockUnsafe {
|
|
102
|
-
private readonly runtime: Runtime.Runtime<Requirements>
|
|
103
|
-
|
|
104
|
-
constructor(config: { tenancyId: string; apiKey: string; endpoint?: string }) {
|
|
105
|
-
const configLive = L.succeed(Config, Config.of(config))
|
|
106
|
-
const allLayers = pipe(
|
|
107
|
-
PrincipalServiceLive,
|
|
108
|
-
L.provide(configLive),
|
|
109
|
-
L.provide(StreamResponseLive),
|
|
110
|
-
)
|
|
111
|
-
const scope = E.runSync(Scope.make())
|
|
112
|
-
this.runtime = E.runSync(L.toRuntime(allLayers).pipe(Scope.extend(scope)))
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private readonly runPromise = <A, R extends Requirements>(
|
|
116
|
-
effect: E.Effect<A, PasslockErrors, R>,
|
|
117
|
-
) => {
|
|
118
|
-
return pipe(
|
|
119
|
-
transformErrors(effect),
|
|
120
|
-
E.flatMap(result => (PasslockError.isError(result) ? E.fail(result) : E.succeed(result))),
|
|
121
|
-
effect => Runtime.runPromise(this.runtime)(effect),
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
fetchPrincipal = (request: PrincipalRequest) =>
|
|
126
|
-
pipe(
|
|
127
|
-
PrincipalService,
|
|
128
|
-
E.flatMap(service => service.fetchPrincipal(request)),
|
|
129
|
-
effect => this.runPromise(effect),
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export class Passlock {
|
|
134
|
-
private readonly runtime: Runtime.Runtime<Requirements>
|
|
135
|
-
|
|
136
|
-
constructor(config: { tenancyId: string; apiKey: string; endpoint?: string }) {
|
|
137
|
-
const configLive = L.succeed(Config, Config.of(config))
|
|
138
|
-
const allLayers = pipe(
|
|
139
|
-
PrincipalServiceLive,
|
|
140
|
-
L.provide(configLive),
|
|
141
|
-
L.provide(StreamResponseLive),
|
|
142
|
-
)
|
|
143
|
-
const scope = E.runSync(Scope.make())
|
|
144
|
-
this.runtime = E.runSync(L.toRuntime(allLayers).pipe(Scope.extend(scope)))
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private readonly runPromise = <A, R extends Requirements>(
|
|
148
|
-
effect: E.Effect<A, PasslockErrors, R>,
|
|
149
|
-
) => {
|
|
150
|
-
return pipe(transformErrors(effect), effect => Runtime.runPromise(this.runtime)(effect))
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
fetchPrincipal = (request: PrincipalRequest) =>
|
|
154
|
-
pipe(
|
|
155
|
-
PrincipalService,
|
|
156
|
-
E.flatMap(service => service.fetchPrincipal(request)),
|
|
157
|
-
effect => this.runPromise(effect),
|
|
158
|
-
)
|
|
159
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import * as S from '@effect/schema/Schema'
|
|
2
|
-
import { Context, Effect as E, Layer as L, LogLevel, Logger, Ref, Stream, pipe } from 'effect'
|
|
3
|
-
import type { RequestOptions } from 'https'
|
|
4
|
-
|
|
5
|
-
import { Principal } from '@passlock/shared/dist/schema/principal.js'
|
|
6
|
-
|
|
7
|
-
import { Config } from '../config/config.js'
|
|
8
|
-
import {
|
|
9
|
-
type PrincipalService,
|
|
10
|
-
PrincipalServiceLive,
|
|
11
|
-
StreamResponse,
|
|
12
|
-
buildError,
|
|
13
|
-
} from './principal.js'
|
|
14
|
-
|
|
15
|
-
export const principal: Principal = {
|
|
16
|
-
jti: 'token',
|
|
17
|
-
token: 'token',
|
|
18
|
-
sub: 'user-1',
|
|
19
|
-
iss: 'idp.passlock.dev',
|
|
20
|
-
aud: 'tenancy_id',
|
|
21
|
-
// must be at least 1 second
|
|
22
|
-
// as it's truncated to seconds
|
|
23
|
-
iat: new Date(60 * 1000),
|
|
24
|
-
nbf: new Date(120 * 100),
|
|
25
|
-
exp: new Date(180 * 1000),
|
|
26
|
-
email: 'john.doe@gmail.com',
|
|
27
|
-
givenName: 'john',
|
|
28
|
-
familyName: 'doe',
|
|
29
|
-
emailVerified: false,
|
|
30
|
-
authType: 'passkey',
|
|
31
|
-
authId: 'auth-1',
|
|
32
|
-
userVerified: true,
|
|
33
|
-
// legacy
|
|
34
|
-
user: {
|
|
35
|
-
id: 'user-1',
|
|
36
|
-
givenName: 'john',
|
|
37
|
-
familyName: 'doe',
|
|
38
|
-
email: 'john.doe@gmail.com',
|
|
39
|
-
emailVerified: false,
|
|
40
|
-
},
|
|
41
|
-
authStatement: {
|
|
42
|
-
authType: 'passkey',
|
|
43
|
-
userVerified: false,
|
|
44
|
-
authTimestamp: new Date(0),
|
|
45
|
-
},
|
|
46
|
-
expireAt: new Date(0),
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const tenancyId = 'tenancyId'
|
|
50
|
-
export const apiKey = 'apiKey'
|
|
51
|
-
|
|
52
|
-
export const configTest = L.succeed(Config, Config.of({ tenancyId, apiKey }))
|
|
53
|
-
|
|
54
|
-
export class State extends Context.Tag('State')<State, Ref.Ref<RequestOptions | undefined>>() {}
|
|
55
|
-
|
|
56
|
-
export const buildEffect = <A, E>(
|
|
57
|
-
assertions: E.Effect<A, E, PrincipalService | State>,
|
|
58
|
-
): E.Effect<void, E> => {
|
|
59
|
-
const streamResponseTest = L.effect(
|
|
60
|
-
StreamResponse,
|
|
61
|
-
E.gen(function* (_) {
|
|
62
|
-
const ref = yield* _(State)
|
|
63
|
-
const res = S.encodeSync(Principal)(principal)
|
|
64
|
-
const buff = Buffer.from(JSON.stringify(res))
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
streamResponse: options =>
|
|
68
|
-
pipe(Stream.fromEffect(Ref.set(ref, options)), Stream.zipRight(Stream.make(buff))),
|
|
69
|
-
}
|
|
70
|
-
}),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
const service = pipe(PrincipalServiceLive, L.provide(streamResponseTest), L.provide(configTest))
|
|
74
|
-
|
|
75
|
-
const args = L.effect(State, Ref.make<RequestOptions | undefined>(undefined))
|
|
76
|
-
|
|
77
|
-
const effect = pipe(
|
|
78
|
-
E.provide(assertions, service),
|
|
79
|
-
E.provide(args),
|
|
80
|
-
Logger.withMinimumLogLevel(LogLevel.None),
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return effect
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const buildErrorEffect =
|
|
87
|
-
(statusCode: number) =>
|
|
88
|
-
<A>(assertions: E.Effect<void, A, PrincipalService>): E.Effect<void, A> => {
|
|
89
|
-
const streamResponseTest = L.succeed(
|
|
90
|
-
StreamResponse,
|
|
91
|
-
StreamResponse.of({
|
|
92
|
-
streamResponse: () => Stream.fail(buildError({ statusCode })),
|
|
93
|
-
}),
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
const service = pipe(PrincipalServiceLive, L.provide(streamResponseTest), L.provide(configTest))
|
|
97
|
-
|
|
98
|
-
const args = L.effect(State, Ref.make<RequestOptions | undefined>(undefined))
|
|
99
|
-
|
|
100
|
-
const effect = pipe(
|
|
101
|
-
E.provide(assertions, service),
|
|
102
|
-
E.provide(args),
|
|
103
|
-
Logger.withMinimumLogLevel(LogLevel.None),
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
return effect
|
|
107
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { Effect as E, Effect, Ref } from 'effect'
|
|
2
|
-
import { describe, expect, test } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
Forbidden,
|
|
6
|
-
InternalServerError,
|
|
7
|
-
NotFound,
|
|
8
|
-
Unauthorized,
|
|
9
|
-
} from '@passlock/shared/dist/error/error.js'
|
|
10
|
-
|
|
11
|
-
import * as Fixture from './principal.fixture.js'
|
|
12
|
-
import { PrincipalService } from './principal.js'
|
|
13
|
-
|
|
14
|
-
describe('fetchPrincipal should', () => {
|
|
15
|
-
test('return a valid principal', () => {
|
|
16
|
-
const assertions = E.gen(function* (_) {
|
|
17
|
-
const service = yield* _(PrincipalService)
|
|
18
|
-
const result = yield* _(service.fetchPrincipal({ token: 'token' }))
|
|
19
|
-
|
|
20
|
-
expect(result).toEqual(Fixture.principal)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const effect = Fixture.buildEffect(assertions)
|
|
24
|
-
|
|
25
|
-
return E.runPromise(effect)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
test('call the correct url', () => {
|
|
29
|
-
const assertions = E.gen(function* (_) {
|
|
30
|
-
const service = yield* _(PrincipalService)
|
|
31
|
-
yield* _(service.fetchPrincipal({ token: 'myToken' }))
|
|
32
|
-
|
|
33
|
-
const state = yield* _(Fixture.State)
|
|
34
|
-
const args = yield* _(Ref.get(state))
|
|
35
|
-
|
|
36
|
-
expect(args?.hostname).toEqual('api.passlock.dev')
|
|
37
|
-
expect(args?.method).toEqual('GET')
|
|
38
|
-
expect(args?.path).toEqual(`/${Fixture.tenancyId}/token/myToken`)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
const effect = Fixture.buildEffect(assertions)
|
|
42
|
-
|
|
43
|
-
return E.runPromise(effect)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test('pass the api key as a header', () => {
|
|
47
|
-
const assertions = E.gen(function* (_) {
|
|
48
|
-
const service = yield* _(PrincipalService)
|
|
49
|
-
yield* _(service.fetchPrincipal({ token: 'myToken' }))
|
|
50
|
-
|
|
51
|
-
const state = yield* _(Fixture.State)
|
|
52
|
-
const args = yield* _(Ref.get(state))
|
|
53
|
-
|
|
54
|
-
expect(args?.headers?.['Authorization']).toEqual(`Bearer ${Fixture.apiKey}`)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const effect = Fixture.buildEffect(assertions)
|
|
58
|
-
|
|
59
|
-
return E.runPromise(effect)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('propagate a 401 error', () => {
|
|
63
|
-
const assertions = E.gen(function* (_) {
|
|
64
|
-
const service = yield* _(PrincipalService)
|
|
65
|
-
const result = service.fetchPrincipal({ token: 'myToken' })
|
|
66
|
-
const error = yield* _(Effect.flip(result))
|
|
67
|
-
expect(error).toBeInstanceOf(Unauthorized)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const effect = Fixture.buildErrorEffect(401)(assertions)
|
|
71
|
-
|
|
72
|
-
return E.runPromise(effect)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('propagate a 403 error', () => {
|
|
76
|
-
const assertions = E.gen(function* (_) {
|
|
77
|
-
const service = yield* _(PrincipalService)
|
|
78
|
-
const result = service.fetchPrincipal({ token: 'myToken' })
|
|
79
|
-
const error = yield* _(Effect.flip(result))
|
|
80
|
-
expect(error).toBeInstanceOf(Forbidden)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
const effect = Fixture.buildErrorEffect(403)(assertions)
|
|
84
|
-
|
|
85
|
-
return E.runPromise(effect)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test('propagate a 404 error', () => {
|
|
89
|
-
const assertions = E.gen(function* (_) {
|
|
90
|
-
const service = yield* _(PrincipalService)
|
|
91
|
-
const result = service.fetchPrincipal({ token: 'myToken' })
|
|
92
|
-
const error = yield* _(Effect.flip(result))
|
|
93
|
-
expect(error).toBeInstanceOf(NotFound)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const effect = Fixture.buildErrorEffect(404)(assertions)
|
|
97
|
-
|
|
98
|
-
return E.runPromise(effect)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
test('propagate a 500 error', () => {
|
|
102
|
-
const assertions = E.gen(function* (_) {
|
|
103
|
-
const service = yield* _(PrincipalService)
|
|
104
|
-
const result = service.fetchPrincipal({ token: 'myToken' })
|
|
105
|
-
const error = yield* _(Effect.flip(result))
|
|
106
|
-
expect(error).toBeInstanceOf(InternalServerError)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const effect = Fixture.buildErrorEffect(500)(assertions)
|
|
110
|
-
|
|
111
|
-
return E.runPromise(effect)
|
|
112
|
-
})
|
|
113
|
-
})
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import * as https from 'https'
|
|
2
|
-
import type { StreamEmit } from 'effect'
|
|
3
|
-
import { Chunk, Console, Context, Effect as E, Layer, Option, Stream, flow, pipe } from 'effect'
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
Forbidden,
|
|
7
|
-
InternalServerError,
|
|
8
|
-
NotFound,
|
|
9
|
-
Unauthorized,
|
|
10
|
-
} from '@passlock/shared/dist/error/error.js'
|
|
11
|
-
import { Principal } from '@passlock/shared/dist/schema/principal.js'
|
|
12
|
-
import { createParser } from '@passlock/shared/dist/schema/utils.js'
|
|
13
|
-
|
|
14
|
-
import { Config } from '../config/config.js'
|
|
15
|
-
import { PASSLOCK_CLIENT_VERSION } from '../version.js'
|
|
16
|
-
|
|
17
|
-
/* Dependencies */
|
|
18
|
-
|
|
19
|
-
export class StreamResponse extends Context.Tag('@services/StreamResponse')<
|
|
20
|
-
StreamResponse,
|
|
21
|
-
{
|
|
22
|
-
streamResponse: (options: https.RequestOptions) => Stream.Stream<Buffer, PrincipalErrors>
|
|
23
|
-
}
|
|
24
|
-
>() {}
|
|
25
|
-
|
|
26
|
-
/* Service */
|
|
27
|
-
|
|
28
|
-
const parsePrincipal = createParser(Principal)
|
|
29
|
-
|
|
30
|
-
export type PrincipalErrors = NotFound | Unauthorized | Forbidden | InternalServerError
|
|
31
|
-
export type PrincipalRequest = { token: string }
|
|
32
|
-
|
|
33
|
-
export class PrincipalService extends Context.Tag('@services/PrincipalService')<
|
|
34
|
-
PrincipalService,
|
|
35
|
-
{
|
|
36
|
-
fetchPrincipal: (request: PrincipalRequest) => E.Effect<Principal, PrincipalErrors>
|
|
37
|
-
}
|
|
38
|
-
>() {}
|
|
39
|
-
|
|
40
|
-
/* Effects */
|
|
41
|
-
|
|
42
|
-
const buildHostname = (endpoint: string | undefined) => {
|
|
43
|
-
return new URL(endpoint || 'https://api.passlock.dev').hostname
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const buildOptions = (token: string) =>
|
|
47
|
-
pipe(
|
|
48
|
-
Config,
|
|
49
|
-
E.map(({ endpoint, tenancyId, apiKey }) => ({
|
|
50
|
-
hostname: buildHostname(endpoint),
|
|
51
|
-
port: 443,
|
|
52
|
-
path: `/${tenancyId}/token/${token}`,
|
|
53
|
-
method: 'GET',
|
|
54
|
-
headers: {
|
|
55
|
-
'Accept': 'application/json',
|
|
56
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
57
|
-
'X-PASSLOCK-CLIENT-VERSION': PASSLOCK_CLIENT_VERSION,
|
|
58
|
-
},
|
|
59
|
-
})),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
export const buildError = (res: {
|
|
63
|
-
statusCode?: number | undefined
|
|
64
|
-
statusMessage?: string | undefined
|
|
65
|
-
}) => {
|
|
66
|
-
if (res.statusCode === 404) return new NotFound({ message: 'Invalid token' })
|
|
67
|
-
if (res.statusCode === 401) return new Unauthorized({ message: 'Unauthorized' })
|
|
68
|
-
if (res.statusCode === 403) return new Forbidden({ message: 'Forbidden' })
|
|
69
|
-
|
|
70
|
-
if (res.statusCode && res.statusMessage)
|
|
71
|
-
return new InternalServerError({ message: `${String(res.statusCode)} - ${res.statusMessage}` })
|
|
72
|
-
|
|
73
|
-
if (res.statusCode) return new InternalServerError({ message: String(res.statusCode) })
|
|
74
|
-
|
|
75
|
-
if (res.statusMessage) return new InternalServerError({ message: res.statusMessage })
|
|
76
|
-
|
|
77
|
-
return new InternalServerError({ message: 'Received non 200 response' })
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const fail = (error: PrincipalErrors) => E.fail(Option.some(error))
|
|
81
|
-
const succeed = (data: Buffer) => E.succeed(Chunk.of(data))
|
|
82
|
-
const close = E.fail(Option.none())
|
|
83
|
-
|
|
84
|
-
const buildStream = (token: string) =>
|
|
85
|
-
pipe(
|
|
86
|
-
Stream.fromEffect(buildOptions(token)),
|
|
87
|
-
Stream.zip(StreamResponse),
|
|
88
|
-
Stream.flatMap(([options, { streamResponse }]) => streamResponse(options)),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
export const fetchPrincipal = (
|
|
92
|
-
request: PrincipalRequest,
|
|
93
|
-
): E.Effect<Principal, PrincipalErrors, StreamResponse | Config> => {
|
|
94
|
-
const stream = buildStream(request.token)
|
|
95
|
-
|
|
96
|
-
const json = pipe(
|
|
97
|
-
Stream.runCollect(stream),
|
|
98
|
-
E.map(Chunk.toReadonlyArray),
|
|
99
|
-
E.map(buffers => Buffer.concat(buffers)),
|
|
100
|
-
E.flatMap(buffer =>
|
|
101
|
-
E.try({
|
|
102
|
-
try: () => buffer.toString(),
|
|
103
|
-
catch: e =>
|
|
104
|
-
new InternalServerError({
|
|
105
|
-
message: 'Unable to convert response to string',
|
|
106
|
-
detail: String(e),
|
|
107
|
-
}),
|
|
108
|
-
}),
|
|
109
|
-
),
|
|
110
|
-
E.flatMap(buffer =>
|
|
111
|
-
E.try({
|
|
112
|
-
try: () => JSON.parse(buffer) as unknown,
|
|
113
|
-
catch: e =>
|
|
114
|
-
new InternalServerError({
|
|
115
|
-
message: 'Unable to parse response to json',
|
|
116
|
-
detail: String(e),
|
|
117
|
-
}),
|
|
118
|
-
}),
|
|
119
|
-
),
|
|
120
|
-
E.flatMap(json =>
|
|
121
|
-
pipe(
|
|
122
|
-
parsePrincipal(json),
|
|
123
|
-
E.tapError(error => Console.error(error.detail)),
|
|
124
|
-
E.mapError(
|
|
125
|
-
() => new InternalServerError({ message: 'Unable to parse response as Principal' }),
|
|
126
|
-
),
|
|
127
|
-
),
|
|
128
|
-
),
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
return json
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/* Live */
|
|
135
|
-
|
|
136
|
-
/* v8 ignore start */
|
|
137
|
-
export const StreamResponseLive = Layer.succeed(StreamResponse, {
|
|
138
|
-
streamResponse: options =>
|
|
139
|
-
Stream.async((emit: StreamEmit.Emit<never, PrincipalErrors, Buffer, void>) => {
|
|
140
|
-
https
|
|
141
|
-
.request(options, res => {
|
|
142
|
-
if (200 !== res.statusCode) void emit(fail(buildError(res)))
|
|
143
|
-
res.on('data', (data: Buffer) => void emit(succeed(data)))
|
|
144
|
-
res.on('close', () => void emit(close))
|
|
145
|
-
res.on('error', e => void emit(fail(new InternalServerError({ message: e.message }))))
|
|
146
|
-
})
|
|
147
|
-
.end()
|
|
148
|
-
}),
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
export const PrincipalServiceLive = Layer.effect(
|
|
152
|
-
PrincipalService,
|
|
153
|
-
E.gen(function* (_) {
|
|
154
|
-
const context = yield* _(E.context<Config | StreamResponse>())
|
|
155
|
-
return PrincipalService.of({
|
|
156
|
-
fetchPrincipal: flow(fetchPrincipal, E.provide(context)),
|
|
157
|
-
})
|
|
158
|
-
}),
|
|
159
|
-
)
|
|
160
|
-
/* v8 ignore stop */
|
package/src/version.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const PASSLOCK_CLIENT_VERSION = '#{LATEST}#'
|