fix(auth): respect server username in clients (#25596)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { ConfigService } from "@/effect/config-service"
|
||||
import { Config, Context, Effect, Encoding, Layer, Option, Redacted } from "effect"
|
||||
import { ServerAuth } from "@/server/auth"
|
||||
import { Effect, Encoding, Layer, Redacted } from "effect"
|
||||
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiError, HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"
|
||||
|
||||
@@ -18,41 +18,18 @@ export class Authorization extends HttpApiMiddleware.Service<Authorization>()(
|
||||
},
|
||||
) {}
|
||||
|
||||
export class ServerAuthConfig extends ConfigService.Service<ServerAuthConfig>()(
|
||||
"@opencode/ExperimentalHttpApiServerAuthConfig",
|
||||
{
|
||||
password: Config.string("OPENCODE_SERVER_PASSWORD").pipe(Config.option),
|
||||
username: Config.string("OPENCODE_SERVER_USERNAME").pipe(Config.withDefault("opencode")),
|
||||
},
|
||||
) {}
|
||||
|
||||
function validateCredential<A, E, R>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
credential: { readonly username: string; readonly password: Redacted.Redacted },
|
||||
config: Context.Service.Shape<typeof ServerAuthConfig>,
|
||||
credential: ServerAuth.DecodedCredentials,
|
||||
config: ServerAuth.Info,
|
||||
) {
|
||||
return Effect.gen(function* () {
|
||||
if (!isAuthRequired(config)) return yield* effect
|
||||
if (!isCredentialAuthorized(credential, config)) return yield* new HttpApiError.Unauthorized({})
|
||||
if (!ServerAuth.required(config)) return yield* effect
|
||||
if (!ServerAuth.authorized(credential, config)) return yield* new HttpApiError.Unauthorized({})
|
||||
return yield* effect
|
||||
})
|
||||
}
|
||||
|
||||
function isAuthRequired(config: Context.Service.Shape<typeof ServerAuthConfig>) {
|
||||
return Option.isSome(config.password) && config.password.value !== ""
|
||||
}
|
||||
|
||||
function isCredentialAuthorized(
|
||||
credential: { readonly username: string; readonly password: Redacted.Redacted },
|
||||
config: Context.Service.Shape<typeof ServerAuthConfig>,
|
||||
) {
|
||||
return (
|
||||
Option.isSome(config.password) &&
|
||||
credential.username === config.username &&
|
||||
Redacted.value(credential.password) === config.password.value
|
||||
)
|
||||
}
|
||||
|
||||
function decodeCredential(input: string) {
|
||||
const emptyCredential = {
|
||||
username: "",
|
||||
@@ -78,11 +55,11 @@ function decodeCredential(input: string) {
|
||||
|
||||
function validateRawCredential<A, E, R>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
credential: { readonly username: string; readonly password: Redacted.Redacted },
|
||||
config: Context.Service.Shape<typeof ServerAuthConfig>,
|
||||
credential: ServerAuth.DecodedCredentials,
|
||||
config: ServerAuth.Info,
|
||||
) {
|
||||
if (!isAuthRequired(config)) return effect
|
||||
if (!isCredentialAuthorized(credential, config))
|
||||
if (!ServerAuth.required(config)) return effect
|
||||
if (!ServerAuth.authorized(credential, config))
|
||||
return Effect.succeed(
|
||||
HttpServerResponse.empty({
|
||||
status: UNAUTHORIZED,
|
||||
@@ -94,8 +71,8 @@ function validateRawCredential<A, E, R>(
|
||||
|
||||
export const authorizationRouterMiddleware = HttpRouter.middleware()(
|
||||
Effect.gen(function* () {
|
||||
const config = yield* ServerAuthConfig
|
||||
if (!isAuthRequired(config)) return (effect) => effect
|
||||
const config = yield* ServerAuth.Config
|
||||
if (!ServerAuth.required(config)) return (effect) => effect
|
||||
|
||||
return (effect) =>
|
||||
Effect.gen(function* () {
|
||||
@@ -122,7 +99,7 @@ export const authorizationRouterMiddleware = HttpRouter.middleware()(
|
||||
export const authorizationLayer = Layer.effect(
|
||||
Authorization,
|
||||
Effect.gen(function* () {
|
||||
const config = yield* ServerAuthConfig
|
||||
const config = yield* ServerAuth.Config
|
||||
return Authorization.of({
|
||||
basic: (effect, { credential }) => validateCredential(effect, credential, config),
|
||||
authToken: (effect, { credential }) =>
|
||||
|
||||
Reference in New Issue
Block a user