"use strict";
/*
 * Copyright 2025 Matti Airas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseEnvConfig = parseEnvConfig;
exports.mergeConfigs = mergeConfigs;
exports.validateOIDCConfig = validateOIDCConfig;
exports.parseOIDCConfig = parseOIDCConfig;
exports.isOIDCEnabled = isOIDCEnabled;
const types_1 = require("./types");
/**
 * Parse OIDC configuration from environment variables
 */
function parseEnvConfig() {
    const config = {};
    if (process.env.SIGNALK_OIDC_ENABLED !== undefined) {
        config.enabled = process.env.SIGNALK_OIDC_ENABLED.toLowerCase() === 'true';
    }
    if (process.env.SIGNALK_OIDC_ISSUER) {
        config.issuer = process.env.SIGNALK_OIDC_ISSUER;
    }
    if (process.env.SIGNALK_OIDC_CLIENT_ID) {
        config.clientId = process.env.SIGNALK_OIDC_CLIENT_ID;
    }
    if (process.env.SIGNALK_OIDC_CLIENT_SECRET) {
        config.clientSecret = process.env.SIGNALK_OIDC_CLIENT_SECRET;
    }
    if (process.env.SIGNALK_OIDC_REDIRECT_URI) {
        config.redirectUri = process.env.SIGNALK_OIDC_REDIRECT_URI;
    }
    if (process.env.SIGNALK_OIDC_SCOPE) {
        config.scope = process.env.SIGNALK_OIDC_SCOPE;
    }
    if (process.env.SIGNALK_OIDC_DEFAULT_PERMISSION) {
        const perm = process.env.SIGNALK_OIDC_DEFAULT_PERMISSION.toLowerCase();
        if (perm === 'readonly' || perm === 'readwrite' || perm === 'admin') {
            config.defaultPermission = perm;
        }
    }
    if (process.env.SIGNALK_OIDC_AUTO_CREATE_USERS !== undefined) {
        config.autoCreateUsers =
            process.env.SIGNALK_OIDC_AUTO_CREATE_USERS.toLowerCase() === 'true';
    }
    // Parse admin groups from comma-separated string
    if (process.env.SIGNALK_OIDC_ADMIN_GROUPS) {
        const groups = process.env.SIGNALK_OIDC_ADMIN_GROUPS.split(',')
            .map((g) => g.trim())
            .filter((g) => g.length > 0);
        if (groups.length > 0) {
            config.adminGroups = groups;
        }
    }
    // Parse readwrite groups from comma-separated string
    if (process.env.SIGNALK_OIDC_READWRITE_GROUPS) {
        const groups = process.env.SIGNALK_OIDC_READWRITE_GROUPS.split(',')
            .map((g) => g.trim())
            .filter((g) => g.length > 0);
        if (groups.length > 0) {
            config.readwriteGroups = groups;
        }
    }
    // Parse groups attribute (ID token claim key for groups)
    if (process.env.SIGNALK_OIDC_GROUPS_ATTRIBUTE) {
        config.groupsAttribute = process.env.SIGNALK_OIDC_GROUPS_ATTRIBUTE;
    }
    // Parse provider name (display name for login button)
    if (process.env.SIGNALK_OIDC_PROVIDER_NAME) {
        config.providerName = process.env.SIGNALK_OIDC_PROVIDER_NAME;
    }
    // Parse auto login (auto-redirect to OIDC when not authenticated)
    if (process.env.SIGNALK_OIDC_AUTO_LOGIN !== undefined) {
        config.autoLogin =
            process.env.SIGNALK_OIDC_AUTO_LOGIN.toLowerCase() === 'true';
    }
    return config;
}
/**
 * Merge configuration from security.json and environment variables
 * Priority: env vars > security.json > defaults
 */
function mergeConfigs(securityJsonConfig, envConfig) {
    return {
        enabled: envConfig.enabled ?? securityJsonConfig.enabled ?? types_1.OIDC_DEFAULTS.enabled,
        issuer: envConfig.issuer ?? securityJsonConfig.issuer ?? '',
        clientId: envConfig.clientId ?? securityJsonConfig.clientId ?? '',
        clientSecret: envConfig.clientSecret ?? securityJsonConfig.clientSecret ?? '',
        redirectUri: envConfig.redirectUri ?? securityJsonConfig.redirectUri ?? '',
        scope: envConfig.scope ?? securityJsonConfig.scope ?? types_1.OIDC_DEFAULTS.scope,
        defaultPermission: envConfig.defaultPermission ??
            securityJsonConfig.defaultPermission ??
            types_1.OIDC_DEFAULTS.defaultPermission,
        autoCreateUsers: envConfig.autoCreateUsers ??
            securityJsonConfig.autoCreateUsers ??
            types_1.OIDC_DEFAULTS.autoCreateUsers,
        adminGroups: envConfig.adminGroups ?? securityJsonConfig.adminGroups,
        readwriteGroups: envConfig.readwriteGroups ?? securityJsonConfig.readwriteGroups,
        groupsAttribute: envConfig.groupsAttribute ?? securityJsonConfig.groupsAttribute,
        providerName: envConfig.providerName ??
            securityJsonConfig.providerName ??
            types_1.OIDC_DEFAULTS.providerName,
        autoLogin: envConfig.autoLogin ??
            securityJsonConfig.autoLogin ??
            types_1.OIDC_DEFAULTS.autoLogin
    };
}
/**
 * Validate OIDC configuration
 * @throws OIDCError if configuration is invalid
 */
function validateOIDCConfig(config) {
    // If OIDC is disabled, no validation needed
    if (config.enabled === false || config.enabled === undefined) {
        return;
    }
    // Validate required fields when enabled
    if (!config.issuer) {
        throw new types_1.OIDCError('OIDC issuer is required when enabled', 'CONFIG_INVALID');
    }
    if (!config.clientId) {
        throw new types_1.OIDCError('OIDC clientId is required when enabled', 'CONFIG_INVALID');
    }
    if (!config.clientSecret) {
        throw new types_1.OIDCError('OIDC clientSecret is required when enabled', 'CONFIG_INVALID');
    }
    // Validate redirectUri is required and is a valid URL
    if (!config.redirectUri) {
        throw new types_1.OIDCError('OIDC redirectUri is required when enabled. Set it via SIGNALK_OIDC_REDIRECT_URI or in security.json.', 'CONFIG_INVALID');
    }
    let redirectUrl;
    try {
        redirectUrl = new URL(config.redirectUri);
    }
    catch {
        throw new types_1.OIDCError(`OIDC redirectUri must be a valid URL: ${config.redirectUri}`, 'CONFIG_INVALID');
    }
    if (redirectUrl.hash) {
        throw new types_1.OIDCError('OIDC redirectUri must not contain a fragment (RFC 6749 §3.1.2)', 'CONFIG_INVALID');
    }
    const LOCALHOST_HOSTS = ['localhost', '127.0.0.1', '[::1]'];
    if (redirectUrl.protocol !== 'https:' &&
        !LOCALHOST_HOSTS.includes(redirectUrl.hostname)) {
        throw new types_1.OIDCError('OIDC redirectUri must use https (http is only allowed for localhost)', 'CONFIG_INVALID');
    }
    // Validate issuer is a valid URL
    try {
        new URL(config.issuer);
    }
    catch {
        throw new types_1.OIDCError(`OIDC issuer must be a valid URL: ${config.issuer}`, 'CONFIG_INVALID');
    }
    // Validate scope contains 'openid'
    if (config.scope && !config.scope.split(' ').includes('openid')) {
        throw new types_1.OIDCError('OIDC scope must contain "openid"', 'CONFIG_INVALID');
    }
    // Validate defaultPermission
    if (config.defaultPermission &&
        !['readonly', 'readwrite', 'admin'].includes(config.defaultPermission)) {
        throw new types_1.OIDCError(`OIDC defaultPermission must be one of: readonly, readwrite, admin`, 'CONFIG_INVALID');
    }
}
/**
 * Parse OIDC configuration from security.json and environment variables
 * @param securityConfig The security configuration object (may contain oidc section)
 * @returns Complete OIDC configuration
 * @throws OIDCError if configuration is invalid
 */
function parseOIDCConfig(securityConfig) {
    const securityJsonConfig = securityConfig.oidc ?? {};
    const envConfig = parseEnvConfig();
    const merged = mergeConfigs(securityJsonConfig, envConfig);
    validateOIDCConfig(merged);
    return merged;
}
/**
 * Check if OIDC is enabled
 */
function isOIDCEnabled(config) {
    return config.enabled === true;
}
//# sourceMappingURL=config.js.map