<commit_before>jsoniq version "1.0";

module namespace session = "http://apps.28.io/session";

import module namespace random = "http://zorba.io/modules/random";
import module namespace user = "http://apps.28.io/user";
import module namespace api = "http://apps.28.io/api";
import module namespace req = "http://www.28msec.com/modules/http-request";
import module namespace mongo = "http://www.28msec.com/modules/mongodb";
import module namespace cred = "http://www.28msec.com/modules/credentials";

declare variable $session:tokens := "Tokens";
declare variable $session:VALID-TOKEN := "[<KEY>";
declare variable $session:ACCESS-ALLOWED := 0;
declare variable $session:ACCESS-DENIED := 1;
declare variable $session:ACCESS-AUTH-REQUIRED := 2;

declare variable $session:comment := "Generated by 28.io, for more information see http://www.28.io/xbrl (" || fn:current-dateTime() || ")";

declare function session:comment($format as string) as item*
{
    switch ($format)
    case "json" return { "Comment" : $session:comment }
    case "xml" return 
        (
            comment { $session:comment },
            comment { req:server-name() || req:uri() }
        )
    default return error()
};

declare function session:comment($format as string, $stats as object)
  as item*
{
    switch ($format)
    case "json" return 
        {| 
            session:comment($format),
            { "Statistics" : $stats }
        |}
    case "xml" return 
        (
            session:comment($format),
            comment { "Statistics: " || string-join(for $k in keys($stats) return concat ($k, ": ", $stats($k)), ", ") }
        )
    default return error()
};

declare function session:connect() as xs:anyURI
{
    let $cred := cred:credentials("MongoDB", "xbrl")
    return mongo:connect($cred)
};

declare function session:num-facts() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "facts")
};

declare function session:num-archives() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "archives")
};

declare function session:num-components() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "components")
};

declare function session:num-concepts() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "concepts")
};

declare function session:num-entities() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "entities")
};

declare %an:sequential function session:start($user-id as xs:string, $expiration-date as xs:dateTime)
as string
{
    session:cleanup();
    
    variable $token := random:uuid();
    variable $session := 
      {
        "_id"           : $token,
        user-id         : $user-id,
        expiration-date : $expiration-date
      };
    db:insert($session:tokens, $session);
    
    $token
};

declare function session:get($token as string)
as string
{
    api:validate-regexp("token", $token, $session:VALID-TOKEN);
    
    variable $session := find($session:tokens, { "_id" : $token });
    
    if (exists($session))
    then
        if ($session.expiration-date gt current-dateTime())
        then $session.user-id
        else fn:error(xs:QName("session:expired"), "Your session has expired at " || $session.expiration-date )
    else fn:error(xs:QName("session:expired"), "Your session is not valid")
};

declare %an:sequential function session:terminate($token as string)
as empty-sequence()
{
    api:validate-regexp("token", $token, $session:VALID-TOKEN);
    
    variable $session := find($session:tokens, { "_id" : $token });
    
    if (exists($session))
    then db:delete($session);
    else ();
};


declare %an:sequential function session:cleanup()
as empty-sequence()
{
    for $session in collection($session:tokens)
    where $session.expiration-date le current-dateTime()
    return db:delete($session);
};

declare function session:ensure-valid($token as string)
as string
{
    session:get($token) (: validates token :)
};

declare function session:is-valid($token as string?)
as boolean
{
    if (exists($token))
    then try {{ session:get($token); true }} catch * {{ false }} (: validates token :)
    else false
};

declare function session:ensure-right($token as string, $right-id as string)
as string
{    
    variable $user-id := session:get($token); (: validates token :)
    if (user:is-authorized($user-id, $right-id))
    then $user-id
    else fn:error(xs:QName("session:missing-authorization"), "User does not have required right " || $right-id)
};

declare function session:has-right($token as string?, $right-id as string)
as boolean
{
    variable $user-id := session:get($token); (: validates token :)
    if (user:is-authorized($user-id, $right-id))
    then true
    else false
};

declare function session:has-access($token as string?, $entities as object*, $right-id as string)
as integer
{
    if (session:only-dow30($entities))
    then $session:ACCESS-ALLOWED
    else
        try {{ 
            if (exists($token))
            then
            {
              if (session:has-right($token, $right-id))
              then $session:ACCESS-ALLOWED
              else $session:ACCESS-DENIED
            }
            else $session:ACCESS-AUTH-REQUIRED
        }}
        catch * {{
            $session:ACCESS-AUTH-REQUIRED
        }}
};

declare %private function session:only-dow30($entities as object*)
as boolean
{
    count(
        for $e in $entities
        where count($e.Profiles.SEC.Tags[]) gt 0 and $e.Profiles.SEC.Tags[] = "DOW30"
        return $e) eq count($entities)
};

declare function session:error($msg as string, $format as string?) as item
{
	switch ($format)
    case "xml" return <error><message>{$msg}</message></error>
	case "text" or "csv" or "excel" return $msg 
    default return
	{
		"success" : false,
		"description" : $msg
	}
};

declare %an:sequential function session:audit-call($token as string?) as empty-sequence()
{
    let $dist-aspects := [ "xbrl:Concept", "xbrl:Entity", "xbrl:Period" ]
    let $facts := {
        KeyAspects : $dist-aspects,
        Aspects : {
            "xbrl:Concept" : "secxbrl:ClientIP",
            "xbrl:Entity" : if (session:is-valid($token)) then user:get-by-id(session:get($token)).email else "Anonymous",
            "xbrl:Period" : string(fn:current-dateTime()),
            "xbrl:Unit" : "xbrl:NonNumeric",
            "secxbrl:Query" : req:path()
        },
        Type : "NonNumericValue",
        Value : (req:header-value("X-FORWARDED-FOR"), req:remote-addr())[1]
    }
    return db:insert("audit", $facts);
};
<commit_msg>Removing DOW30 tokenless behavior.<commit_after>jsoniq version "1.0";

module namespace session = "http://apps.28.io/session";

import module namespace random = "http://zorba.io/modules/random";
import module namespace user = "http://apps.28.io/user";
import module namespace api = "http://apps.28.io/api";
import module namespace req = "http://www.28msec.com/modules/http-request";
import module namespace mongo = "http://www.28msec.com/modules/mongodb";
import module namespace cred = "http://www.28msec.com/modules/credentials";

declare variable $session:tokens := "Tokens";
declare variable $session:VALID-TOKEN := "[<KEY>-]+";
declare variable $session:ACCESS-ALLOWED := 0;
declare variable $session:ACCESS-DENIED := 1;
declare variable $session:ACCESS-AUTH-REQUIRED := 2;

declare variable $session:comment := "Generated by 28.io, for more information see http://www.28.io/xbrl (" || fn:current-dateTime() || ")";

declare function session:comment($format as string) as item*
{
    switch ($format)
    case "json" return { "Comment" : $session:comment }
    case "xml" return 
        (
            comment { $session:comment },
            comment { req:server-name() || req:uri() }
        )
    default return error()
};

declare function session:comment($format as string, $stats as object)
  as item*
{
    switch ($format)
    case "json" return 
        {| 
            session:comment($format),
            { "Statistics" : $stats }
        |}
    case "xml" return 
        (
            session:comment($format),
            comment { "Statistics: " || string-join(for $k in keys($stats) return concat ($k, ": ", $stats($k)), ", ") }
        )
    default return error()
};

declare function session:connect() as xs:anyURI
{
    let $cred := cred:credentials("MongoDB", "xbrl")
    return mongo:connect($cred)
};

declare function session:num-facts() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "facts")
};

declare function session:num-archives() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "archives")
};

declare function session:num-components() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "components")
};

declare function session:num-concepts() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "concepts")
};

declare function session:num-entities() as xs:integer
{
    let $conn := session:connect()
    return mongo:count($conn, "entities")
};

declare %an:sequential function session:start($user-id as xs:string, $expiration-date as xs:dateTime)
as string
{
    session:cleanup();
    
    variable $token := random:uuid();
    variable $session := 
      {
        "_id"           : $token,
        user-id         : $user-id,
        expiration-date : $expiration-date
      };
    db:insert($session:tokens, $session);
    
    $token
};

declare function session:get($token as string)
as string
{
    api:validate-regexp("token", $token, $session:VALID-TOKEN);
    
    variable $session := find($session:tokens, { "_id" : $token });
    
    if (exists($session))
    then
        if ($session.expiration-date gt current-dateTime())
        then $session.user-id
        else fn:error(xs:QName("session:expired"), "Your session has expired at " || $session.expiration-date )
    else fn:error(xs:QName("session:expired"), "Your session is not valid")
};

declare %an:sequential function session:terminate($token as string)
as empty-sequence()
{
    api:validate-regexp("token", $token, $session:VALID-TOKEN);
    
    variable $session := find($session:tokens, { "_id" : $token });
    
    if (exists($session))
    then db:delete($session);
    else ();
};


declare %an:sequential function session:cleanup()
as empty-sequence()
{
    for $session in collection($session:tokens)
    where $session.expiration-date le current-dateTime()
    return db:delete($session);
};

declare function session:ensure-valid($token as string)
as string
{
    session:get($token) (: validates token :)
};

declare function session:is-valid($token as string?)
as boolean
{
    if (exists($token))
    then try {{ session:get($token); true }} catch * {{ false }} (: validates token :)
    else false
};

declare function session:ensure-right($token as string, $right-id as string)
as string
{    
    variable $user-id := session:get($token); (: validates token :)
    if (user:is-authorized($user-id, $right-id))
    then $user-id
    else fn:error(xs:QName("session:missing-authorization"), "User does not have required right " || $right-id)
};

declare function session:has-right($token as string?, $right-id as string)
as boolean
{
    variable $user-id := session:get($token); (: validates token :)
    if (user:is-authorized($user-id, $right-id))
    then true
    else false
};

declare function session:has-access($token as string?, $right-id as string)
as integer
{
	try {{ 
	    if (exists($token))
	    then
	    {
	      if (session:has-right($token, $right-id))
	      then $session:ACCESS-ALLOWED
	      else $session:ACCESS-DENIED
	    }
	    else $session:ACCESS-AUTH-REQUIRED
	}}
	catch * {{
	    $session:ACCESS-AUTH-REQUIRED
	}}
};

declare function session:error($msg as string, $format as string?) as item
{
	switch ($format)
    case "xml" return <error><message>{$msg}</message></error>
	case "text" or "csv" or "excel" return $msg 
    default return
	{
		"success" : false,
		"description" : $msg
	}
};

declare %an:sequential function session:audit-call($token as string?) as empty-sequence()
{
    let $dist-aspects := [ "xbrl:Concept", "xbrl:Entity", "xbrl:Period" ]
    let $facts := {
        KeyAspects : $dist-aspects,
        Aspects : {
            "xbrl:Concept" : "secxbrl:ClientIP",
            "xbrl:Entity" : if (session:is-valid($token)) then user:get-by-id(session:get($token)).email else "Anonymous",
            "xbrl:Period" : string(fn:current-dateTime()),
            "xbrl:Unit" : "xbrl:NonNumeric",
            "secxbrl:Query" : req:path()
        },
        Type : "NonNumericValue",
        Value : (req:header-value("X-FORWARDED-FOR"), req:remote-addr())[1]
    }
    return db:insert("audit", $facts);
};

{'fraction_non_alphanumeric': 0.15452847805788983, 'fraction_numerical': 0.004123871770930595, 'mean_word_length': 3.3159838817998657, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 8, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4316673', 'n_tokens_mistral': 4119, 'n_tokens_neox': 3977, 'n_words': 981}