#!/usr/bin/env bash # policy.module.sh — policy system # Policies define behavioral flags for subnets, identities, and future contexts. # Chain: Subnet → Policy → Identity → Peer function policy::_hardcoded_field() { local name="${1:-}" field="${2:-}" # Only fallback for 'default' policy if policies.json is unavailable [[ "$name" != "default" ]] && echo "" && return 0 case "$field" in tunnel_mode) echo "split" ;; default_rule) echo "" ;; strict_rule) echo "false" ;; auto_apply) echo "true" ;; *) echo "" ;; esac } # ====================================================== # Core Accessors # ====================================================== function policy::exists() { local name="${1:-}" json::policy_exists "$(ctx::policies)" "$name" 2>/dev/null } function policy::require_exists() { local name="${1:-}" if ! policy::exists "$name"; then log::error "Policy '${name}' not found. Use 'wgctl policy list' to see available policies." return 1 fi } function policy::get() { local name="${1:-}" field="${2:-}" local result result=$(json::policy_get "$(ctx::policies)" "$name" "$field" 2>/dev/null) || true if [[ -n "$result" ]]; then echo "$result" return 0 fi # Fallback to hardcoded [[ -n "$field" ]] && policy::_hardcoded_field "$name" "$field" } function policy::tunnel_mode() { local name="${1:-default}" policy::get "$name" "tunnel_mode" } function policy::default_rule() { local name="${1:-default}" policy::get "$name" "default_rule" } function policy::strict_rule() { local name="${1:-default}" local val val=$(policy::get "$name" "strict_rule") [[ "$val" == "true" ]] } function policy::auto_apply() { local name="${1:-default}" local val val=$(policy::get "$name" "auto_apply") [[ "$val" != "false" ]] } function policy::list_data() { json::policy_list "$(ctx::policies)" 2>/dev/null || true } # ====================================================== # Subnet Policy Resolution # ====================================================== # policy::for_subnet [type_key] # Returns the policy name for a subnet entry. # Falls back to "default" if no policy set on the subnet. function policy::for_subnet() { local subnet_name="${1:-}" type_key="${2:-}" local result result=$(json::subnet_policy "$(ctx::subnets)" "$subnet_name" "$type_key" 2>/dev/null) || true echo "${result:-default}" } # policy::resolve_for_add [type_key] # Returns the fully resolved policy for use during wgctl add. # Output: policy_name function policy::resolve_for_add() { local subnet_name="${1:-}" type_key="${2:-}" if [[ -z "$subnet_name" ]]; then echo "default" return 0 fi policy::for_subnet "$subnet_name" "$type_key" } # ====================================================== # Identity Policy Resolution # ====================================================== # policy::for_identity # Returns the policy name stored in an identity file. # Falls back to "default". function policy::for_identity() { local identity_name="${1:-}" local id_file id_file=$(ctx::identity::path "${identity_name}.identity") local result result=$(json::get "$id_file" "policy" 2>/dev/null) || true echo "${result:-default}" } # policy::effective # Returns the effective policy name for a peer being added. # Priority: identity policy > subnet policy > default function policy::effective() { local subnet_name="${1:-}" type_key="${2:-}" identity_name="${3:-}" # Identity policy takes precedence if explicitly set if [[ -n "$identity_name" ]]; then local id_policy id_policy=$(policy::for_identity "$identity_name") if [[ "$id_policy" != "default" ]]; then echo "$id_policy" return 0 fi fi # Subnet policy next if [[ -n "$subnet_name" ]]; then local subnet_policy subnet_policy=$(policy::for_subnet "$subnet_name" "$type_key") echo "$subnet_policy" return 0 fi echo "default" } # ====================================================== # Warning Helpers (called from add.command.sh) # ====================================================== function policy::warn_strict_rule() { local identity_name="${1:-}" policy_name="${2:-}" identity_rule="${3:-}" log::warn "Identity '${identity_name}' has policy '${policy_name}' with strict rule enabled — peer rule will not be applied, '${identity_rule}' is the only active rule" } function policy::warn_additive_rule() { local identity_name="${1:-}" identity_rule="${1:-}" peer_rule="${2:-}" log::info "Identity '${identity_name}' has strict rule disabled — '${identity_rule}' and '${peer_rule}' will both apply" } function policy::warn_no_rule() { local peer_name="${1:-}" log::warn "'${peer_name}' has no rule assigned — peer has unrestricted access" }