Class ::nsmcp::FileCredentials (public)

 ::nx::Class ::nsmcp::FileCredentials[i]

Defined in /usr/local/ns/tcl/nsmcp/credentials.tcl

Testcases:
No testcase defined.
Source code:
    :property authenticator:required
    :property section:required
    :property store:required
    :variable mutex

    :method init {} {
        set :mutex [ns_mutex create "nsmcp:credentials:[ns_info server]:[self]"]
    }

    :method names {} {
        set names [ns_config ${:section} credentials {}]
        if {[llength $names] != [llength [lsort -unique $names]]} {
            error "duplicate credential names"
        }
        foreach name $names {
            if {![regexp {^[A-Za-z0-9][A-Za-z0-9_.-]*$} $name]} {
                error "invalid symbolic credential name"
            }
        }
        return $names
    }

    :method prepare {name} {
        set section ${:section}/credential/$name
        set path [ns_config $section tokenFile {}]
        if {[file pathtype $path] ne "absolute" || ![file isfile $path] || ![file readable $path]} {
            error "credential $name requires a readable absolute tokenFile"
        }
        set principal [ns_config $section principal $name]
        set permissions [ns_config $section permissions {}]
        if {$principal eq ""} {
            error "credential $name requires a principal"
        }
        llength $permissions
        set expires [ns_config $section expires 0]
        if {![string is wideinteger -strict $expires] || ($expires != 0 && $expires <= [clock seconds])} {
            error "credential $name has an invalid or expired expiry"
        }
        set f [open $path rb]
        try {
            set raw [read $f 4097]
        } finally {
            close $f
        }
        if {[string length $raw] > 4096} {
            error "credential $name has an oversized token file"
        }
        set token [string trim $raw "\r\n"]
        if {[string length $token] < 32 || [string length $token] > 4096 ||
            ![regexp {^[A-Za-z0-9._~+/-]+=*$} $token]} {
            error "credential $name has an invalid token file"
        }
        set digest [ns_crypto::md string -digest sha256 -- $token]
        return [dict create token $token digest $digest tokenFile $path principal $principal  permissions $permissions targets [list [ns_info server]] expires $expires]
    }

    :method publish {name prepared} {
        ${:authenticator} grant [dict get $prepared token] [dict get $prepared principal]  [dict get $prepared permissions] [dict get $prepared targets] [dict get $prepared expires]
        dict unset prepared token
        nsv_set ${:store} $name $prepared
        return $prepared
    }

    :public method loadAll {} {
        #
        # Load all configured credential files as one validated update.
        #
        # Stage every credential under the mutex before changing active
        # authenticator bindings.
        #
        # @return Credential metadata without bearer-token contents.
        #

        ns_mutex lock ${:mutex}
        try {
            # Validate every file/policy before changing any existing binding.
            set staged {}
            set digests {}
            foreach name [:names] {
                set prepared [:prepare $name]
                set digest [dict get $prepared digest]
                if {[dict exists $digests $digest]} {
                    error "configured credentials must use distinct tokens"
                }
                dict set digests $digest $name
                dict set staged $name $prepared
            }
            foreach name [nsv_array names ${:store}] {
                ${:authenticator} revoke [dict get [nsv_get ${:store} $name] digest]
                nsv_unset ${:store} $name
            }
            dict for {name prepared} $staged {
                :publish $name $prepared
            }
            return [:describeUnlocked]
        } finally {
            ns_mutex unlock ${:mutex}
        }
    }

    :public method load {name} {
        #
        # Reload one configured credential file.
        #
        # Validate and install the selected credential while serializing updates
        # with the instance mutex.
        #
        # @param name Name of the configured or registered item to resolve.
        #
        # @return The loaded credential's metadata dictionary.
        #

        ns_mutex lock ${:mutex}
        try {
            if {$name ni [:names]} {
                error "unknown configured credential name"
            }
            set prepared [:prepare $name]
            foreach other [nsv_array names ${:store}] {
                if {$other ne $name && [dict get [nsv_get ${:store} $other] digest] eq [dict get $prepared digest]} {
                    error "configured credentials must use distinct tokens"
                }
            }
            set previous {}
            if {[nsv_exists ${:store} $name]} {
                set previous [dict get [nsv_get ${:store} $name] digest]
            }
            set metadata [:publish $name $prepared]
            if {$previous ne "" && $previous ne [dict get $prepared digest]} {
                ${:authenticator} revoke $previous
            }
            return $metadata
        } finally {
            ns_mutex unlock ${:mutex}
        }
    }

    :public method revoke {name} {
        #
        # Revoke the active binding for a loaded credential.
        #
        # Resolve the configured name to its stored digest and revoke that
        # authenticator entry under the mutex.
        #
        # @param name Name of the configured or registered item to resolve.
        #
        # @return The authenticator's revocation result; unknown credentials raise
        #         an error.
        #

        ns_mutex lock ${:mutex}
        try {
            if {![nsv_exists ${:store} $name]} {
                error "unknown loaded credential name"
            }
            ${:authenticator} revoke [dict get [nsv_get ${:store} $name] digest]
        } finally {
            ns_mutex unlock ${:mutex}
        }
    }

    :method describeUnlocked {} {
        set result {}
        foreach name [lsort [nsv_array names ${:store}]] {
            set metadata [nsv_get ${:store} $name]
            set current [${:authenticator} credentialInfo [dict get $metadata digest]]
            set status revoked
            if {$current ne ""} {
                set status active
                if {[dict get $current expires] != 0 && [dict get $current expires] <= [clock seconds]} {
                    set status expired
                }
            }
            dict set metadata status $status
            dict set result $name $metadata
        }
        return $result
    }

    :public method describe {} {
        #
        # Describe loaded credential bindings without revealing tokens.
        #
        # Read the credential metadata under the instance mutex.
        #
        # @return The credential metadata returned by the unlocked descriptor
        #         helper.
        #

        ns_mutex lock ${:mutex}
        try {
            return [:describeUnlocked]
        } finally {
            ns_mutex unlock ${:mutex}
        }
    }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: