/*
 * -------------------------------------------------------
 * Section: MFA
 * We create the policies and functions to enforce MFA
 * -------------------------------------------------------
 */

/*
* public.is_aal2
* Check if the user has aal2 access
*/
create
    or replace function public.is_aal2() returns boolean
    set
        search_path = '' as
$$
declare
    is_aal2 boolean;
begin
    select auth.jwt() ->> 'aal' = 'aal2' into is_aal2;

    return coalesce(is_aal2, false);
end
$$ language plpgsql;

-- Grant access to the function to authenticated users
grant execute on function public.is_aal2() to authenticated;

/*
* public.is_super_admin
* Check if the user is a super admin. 
* A Super Admin is a user that has the role 'super-admin' and has MFA enabled.
*/
create
    or replace function public.is_super_admin() returns boolean
    set
        search_path = '' as
$$
declare
    is_super_admin boolean;
begin
    if not public.is_aal2() then
        return false;
    end if;

    select (auth.jwt() ->> 'app_metadata')::jsonb ->> 'role' = 'super-admin' into is_super_admin;

    return coalesce(is_super_admin, false);
end
$$ language plpgsql;

-- Grant access to the function to authenticated users
grant execute on function public.is_super_admin() to authenticated;

/*
* public.is_mfa_compliant
* Check if the user meets MFA requirements if they have MFA enabled.
* If the user has MFA enabled, then the user must have aal2 enabled. Otherwise, the user must have aal1 enabled (default behavior).
*/
create or replace function public.is_mfa_compliant() returns boolean
    set search_path = '' as
$$
begin
    return array[(select auth.jwt()->>'aal')] <@ (
        select
            case
                when count(id) > 0 then array['aal2']
                else array['aal1', 'aal2']
                end as aal
        from auth.mfa_factors
        where ((select auth.uid()) = auth.mfa_factors.user_id) and auth.mfa_factors.status = 'verified'
    );
end
$$ language plpgsql security definer;

-- Grant access to the function to authenticated users
grant execute on function public.is_mfa_compliant() to authenticated;

-- MFA Restrictions:
-- the following policies are applied to the tables as a
-- restrictive policy to ensure that if MFA is enabled, then the policy will be applied.
-- For users that have not enabled MFA, the policy will not be applied and will keep the default behavior.

-- Restrict access to accounts if MFA is enabled
create policy restrict_mfa_accounts
    on public.accounts
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to accounts memberships if MFA is enabled
create policy restrict_mfa_accounts_memberships
    on public.accounts_memberships
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to subscriptions if MFA is enabled
create policy restrict_mfa_subscriptions
    on public.subscriptions
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to subscription items if MFA is enabled
create policy restrict_mfa_subscription_items
    on public.subscription_items
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to role permissions if MFA is enabled
create policy restrict_mfa_role_permissions
    on public.role_permissions
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to invitations if MFA is enabled
create policy restrict_mfa_invitations
    on public.invitations
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to orders if MFA is enabled
create policy restrict_mfa_orders
    on public.orders
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to orders items if MFA is enabled
create policy restrict_mfa_order_items
    on public.order_items
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());

-- Restrict access to orders if MFA is enabled
create policy restrict_mfa_notifications
    on public.notifications
    as restrictive
    to authenticated
    using (public.is_mfa_compliant());