OidcIntegration.java

package com.cloudforge.core.interfaces;

/**
 * Application-level OIDC integration interface.
 *
 * <p>This interface defines how applications integrate with OIDC providers
 * for authentication. Each application implements this to configure its
 * specific OIDC plugin/module.</p>
 *
 * <p><strong>CloudForge supports two separate authentication systems:</strong></p>
 * <ul>
 *   <li><strong>Amazon Cognito</strong> - Standalone user directory with OIDC</li>
 *   <li><strong>IAM Identity Center</strong> - Enterprise SSO with SAML/OIDC</li>
 * </ul>
 *
 * <p>These are completely separate systems and cannot be mixed.</p>
 *
 * @see ApplicationSpec#supportsOidcIntegration()
 */
public interface OidcIntegration {

    /**
     * Returns whether this application supports OIDC integration.
     *
     * @return true if application has OIDC support
     */
    boolean isSupported();

    /**
     * Returns the OIDC integration method for this application.
     *
     * <p>Examples:</p>
     * <ul>
     *   <li>jenkins: OIDC Plugin</li>
     *   <li>gitlab: Built-in OmniAuth</li>
     *   <li>grafana: Built-in generic_oauth</li>
     *   <li>sonarqube: OIDC Plugin</li>
     * </ul>
     *
     * @return integration method description
     */
    String getIntegrationMethod();

    /**
     * Returns environment variables needed for OIDC configuration.
     *
     * <p>These are passed to the container or EC2 userdata script.</p>
     *
     * <p>Example for Grafana:</p>
     * <pre>
     * GF_AUTH_GENERIC_OAUTH_ENABLED=true
     * GF_AUTH_GENERIC_OAUTH_NAME=Cognito
     * GF_AUTH_GENERIC_OAUTH_CLIENT_ID=${clientId}
     * GF_AUTH_GENERIC_OAUTH_AUTH_URL=${authUrl}
     * </pre>
     *
     * @param config OIDC configuration from provider
     * @return map of environment variable name to value
     */
    java.util.Map<String, String> getEnvironmentVariables(OidcConfiguration config);

    /**
     * Returns configuration file content for OIDC setup.
     *
     * <p>Some applications require configuration files instead of environment variables.</p>
     *
     * <p>Example for GitLab gitlab.rb:</p>
     * <pre>
     * gitlab_rails['omniauth_enabled'] = true
     * gitlab_rails['omniauth_providers'] = [
     *   {
     *     name: 'openid_connect',
     *     args: { ... }
     *   }
     * ]
     * </pre>
     *
     * @param config OIDC configuration from provider
     * @return configuration file content (optional)
     */
    default String getConfigurationFile(OidcConfiguration config) {
        return null;
    }

    /**
     * Returns the file path where configuration should be written.
     *
     * <p>Only used if getConfigurationFile() returns non-null.</p>
     *
     * @return configuration file path (optional)
     */
    default String getConfigurationFilePath() {
        return null;
    }

    /**
     * Returns UserData commands for setting up OIDC integration.
     *
     * <p>These commands are added to the EC2 userdata script to configure
     * OIDC integration during instance initialization.</p>
     *
     * @param config OIDC configuration from provider
     * @param context EC2 context with stack information
     * @return list of shell commands
     */
    java.util.List<String> getUserDataCommands(OidcConfiguration config, Ec2Context context);

    /**
     * Returns post-deployment instructions for completing OIDC setup.
     *
     * <p>Some applications require manual steps after deployment (e.g., installing plugins).</p>
     *
     * @return human-readable instructions (optional)
     */
    default String getPostDeploymentInstructions() {
        return null;
    }

    /**
     * Returns the application startup command for Fargate containers.
     *
     * <p>This command is used to start the application after the OIDC configuration
     * file has been created. Each application has a different startup script.</p>
     *
     * <p>Examples:</p>
     * <ul>
     *   <li>Jenkins: /usr/local/bin/jenkins.sh</li>
     *   <li>GitLab: /assets/wrapper</li>
     *   <li>Grafana: /run.sh</li>
     *   <li>Mattermost: /mattermost/bin/mattermost (distroless - Go binary)</li>
     * </ul>
     *
     * @return startup command path
     */
    default String getContainerStartupCommand() {
        // Default: assume standard Unix convention
        return "/usr/local/bin/start.sh";
    }

    /**
     * Returns the OIDC callback path for this application.
     *
     * <p>This is the path where the OIDC provider redirects after authentication.
     * Each application has a different callback path based on its OIDC implementation.</p>
     *
     * <p>Examples:</p>
     * <ul>
     *   <li>Jenkins: /securityRealm/finishLogin</li>
     *   <li>Mattermost: /signup/gitlab/complete (uses GitLab OAuth provider for OIDC)</li>
     *   <li>GitLab: /users/auth/openid_connect/callback</li>
     *   <li>Grafana: /login/generic_oauth</li>
     * </ul>
     *
     * @return callback path (e.g., "/securityRealm/finishLogin")
     */
    default String getOidcCallbackPath() {
        // Default: Jenkins callback path (most common)
        return "/securityRealm/finishLogin";
    }

    /**
     * Returns whether the container image is distroless (has no shell).
     *
     * <p>Distroless images contain only the application binary and dependencies,
     * without a shell (/bin/sh). This affects how OIDC configuration is applied:</p>
     * <ul>
     *   <li><strong>Normal images:</strong> Use /bin/sh -c to write config files at startup</li>
     *   <li><strong>Distroless images:</strong> Must use environment variables only</li>
     * </ul>
     *
     * <p>Examples of distroless images:</p>
     * <ul>
     *   <li>mattermost/mattermost-team-edition - Go binary, no shell</li>
     *   <li>gcr.io/distroless/* - Google distroless images</li>
     * </ul>
     *
     * @return true if the container is distroless and has no shell
     */
    default boolean isDistroless() {
        return false;
    }

    // ==================== Authentication Capability Methods ====================

    /**
     * Returns whether this application supports ALB-level OIDC authentication.
     *
     * <p>ALB-level auth means authentication is handled by the ALB before traffic
     * reaches the application. The application sees already-authenticated users
     * via headers (X-Amzn-Oidc-*).</p>
     *
     * <p>Most applications support this as it's transparent to the application.</p>
     *
     * @return true if ALB-level OIDC is supported (default: true)
     */
    default boolean supportsAlbOidc() {
        return true;
    }

    /**
     * Returns whether this application supports Cognito as an identity provider.
     *
     * <p>Cognito provides:</p>
     * <ul>
     *   <li>User pool with email/password authentication</li>
     *   <li>MFA support (TOTP, SMS)</li>
     *   <li>OAuth 2.0 / OIDC endpoints</li>
     *   <li>Hosted UI for login</li>
     * </ul>
     *
     * @return true if Cognito OIDC is supported (default: true)
     */
    default boolean supportsCognito() {
        return true;
    }

    /**
     * Returns whether this application supports IAM Identity Center SAML.
     *
     * <p>IAM Identity Center (formerly AWS SSO) provides:</p>
     * <ul>
     *   <li>SAML 2.0 authentication</li>
     *   <li>Enterprise directory integration</li>
     *   <li>Group-based access control</li>
     *   <li>Centralized user management</li>
     * </ul>
     *
     * <p>Applications that use SAML (Mattermost, Metabase) support this.
     * Applications that only use OIDC may not.</p>
     *
     * @return true if IAM Identity Center SAML is supported (default: false)
     */
    default boolean supportsIdentityCenterSaml() {
        return false;
    }

    /**
     * Returns whether this application supports application-level OIDC.
     *
     * <p>Application-level OIDC means the application handles authentication itself
     * using its built-in OIDC/OAuth support. The application needs:</p>
     * <ul>
     *   <li>Client ID and secret</li>
     *   <li>OIDC endpoints (issuer, auth, token, userinfo)</li>
     *   <li>Redirect URL configuration</li>
     * </ul>
     *
     * <p>Examples: Jenkins OIDC plugin, GitLab OmniAuth, Grafana generic_oauth</p>
     *
     * @return true if application-level OIDC is supported (default: true if integration exists)
     */
    default boolean supportsApplicationOidc() {
        return isSupported();
    }

    /**
     * Returns the authentication type this integration uses.
     *
     * @return "OIDC" or "SAML"
     */
    default String getAuthenticationType() {
        return "OIDC";
    }

    // ==================== SAML Certificate Configuration ====================

    /**
     * Returns whether this application needs a SAML IdP certificate at startup.
     *
     * <p>SAML applications typically need the IdP's X.509 certificate to verify
     * SAML assertions. This certificate is fetched from the IdP metadata URL
     * and mounted into the container via an init container.</p>
     *
     * @return true if SAML certificate is required (default: true for SAML apps)
     */
    default boolean needsSamlCertificate() {
        return "SAML".equals(getAuthenticationType());
    }

    /**
     * Returns the directory path where SAML certificate should be mounted.
     *
     * <p>This path is used by the init container to write the certificate
     * and by the main container to read it.</p>
     *
     * <p>Examples:</p>
     * <ul>
     *   <li>Mattermost: /mattermost/saml (NOT /mattermost/config to avoid conflicts)</li>
     *   <li>Metabase: /metabase/saml</li>
     * </ul>
     *
     * @return mount path for SAML certificate directory
     */
    default String getSamlCertificateMountPath() {
        return "/app/saml";
    }

    /**
     * Returns the full file path for the SAML IdP certificate.
     *
     * <p>This is the complete path including filename where the certificate
     * will be written. Applications reference this path in their SAML configuration.</p>
     *
     * @return full path to the certificate file
     */
    default String getSamlCertificateFilePath() {
        return getSamlCertificateMountPath() + "/idp.crt";
    }

    /**
     * Returns the environment variable name for the SAML certificate path.
     *
     * <p>Applications configure SAML via environment variables. This returns
     * the variable name that holds the certificate file path.</p>
     *
     * <p>Examples:</p>
     * <ul>
     *   <li>Mattermost: MM_SAMLSETTINGS_IDPCERTIFICATEFILE</li>
     *   <li>Metabase: MB_SAML_IDENTITY_PROVIDER_CERTIFICATE</li>
     * </ul>
     *
     * @return environment variable name for certificate path, or null if not applicable
     */
    default String getSamlCertificateEnvVar() {
        return null;
    }
}