<?xml version="1.0"?>
<component>
<?component error="true" debug="true"?>
<registration
	description="scimusers"
	progid="scimusers.WSC"
	version="1.00"
	classid="{6D817B6F-9D08-4636-AAD9-8BD7C5EFF56A}"
>
</registration>

<public>

<!-- properties -->

<!-- methods -->
	<method name="initialize">
		<PARAMETER name="params"/>
	</method>
	<method name="error_handler">
		<PARAMETER name="code"/>
		<PARAMETER name="msg"/>
		<PARAMETER name="orgHandler"/>
	</method>
	<method name="transform_filter">
		<PARAMETER name="filter"/>
	</method>
	<method name="transform_outgoing">
		<PARAMETER name="params"/>
		<PARAMETER name="data"/>
	</method>
	<method name="transform_incoming">
		<PARAMETER name="params"/>
		<PARAMETER name="data"/>
	</method>
	<method name="get_eTag">
		<PARAMETER name="params"/>
		<PARAMETER name="data"/>
	</method>
</public>

<script language="javascript">
<![CDATA[

/*	properties	*/


/*	methods	*/
/*
/*
    $Id$

    File:   scimusers.wsc
*/
/* Globals */
var FCLT;

function initialize(params)
{
    FCLT = params;
    FCLT.DEZE.settings.overrule_setting("prs_dep_string", "d.prs_afdeling_omschrijving"); // om te kunnen updaten zonder al te veel problemen
}

function error_handler(code, msg, orgHandler)
{
    code = code || 500;
    var data = {
        "schemas" : ["urn:ietf:params:scim:api:messages:2.0:Error"],
        "scimType": "invalidValue",
        "detail"  : msg,
        "status"  : code
    };

    FCLT.DEZE.Response.ContentType = "application/json";
    FCLT.DEZE.Response.Write(FCLT.DEZE.JSON.stringify(data, null, 2));
    var codestr = String(code);
    if (typeof code == "number")
    {
        var codestr = { 400: "Bad request",
                        403: "Forbidden",
                        404: "Not Found",
                        422: "Unprocessable Entity" }[code];
        if (codestr)
            code = code + " " + codestr;
    }
    FCLT.DEZE.Response.Status = code;

    FCLT.DEZE.__Log("Aborting with status: " + code);
    FCLT.DEZE.__Log(data);

    FCLT.DEZE.Response.End;
}

function transform_filter(filter)
{
    if ("count" in filter)
        filter.limit = filter.count;
    if ("startindex" in filter)
        filter.offset = filter.startindex - 1;
    if ("filter" in filter)
    {
        // Ontvangen filter "filter": "id eq \"KFSG_person_703\""
        //               of "filter": "userName eq \"5acbeddb-f95f-4d46-b4ec-0fac4725b3d3\"" (Azure connectivity test)
        //               of "filter": "externalId eq \"d07972fc-a127-495b-82c3-255cf2ea68ad\"" (Azure ZLMV)
        // Vooralsnog ondersteunen we alleen 'eq' en maximaal 1 conditie
        var a_filter = filter.filter.split(' eq ');
        if (a_filter.length == 2)
        {
            var v_filter = a_filter[1].replace(/\"/g, ""); // quotes er af
            switch (a_filter[0])
            {
                case "id":
                    var v_filter = v_filter.replace(FCLT.DEZE.customerId + "_person_", "");
                    filter.id = v_filter;
                    break;
                case "userName":
                    filter.login = v_filter;
                    break;
                case "externalId":
                    filter.externalid = v_filter;
                    break;
                default:
                    FCLT.DEZE.__Log("Unsupported SCIM filter ignored");
                    FCLT.DEZE.__Log(filter);
            }
        }
        else
        {
            FCLT.DEZE.__Log("Unsupported SCIM filter ignored");
            FCLT.DEZE.__Log(filter);
        }
    }

    return filter;
}

function get_eTag(params, data)
{
    if (data && typeof data == "object" && "meta" in data)
        return data.meta.version;
    return null;
}

function get_element(data)
{
   if (data == undefined) data = "";
   return data;
}

function assign(person, field, data, valfield)
{
    if (valfield in data)
    {
        person[field] = get_element(data[valfield]);
    }
}

var enterprise_urn = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
var facilitor_urn = "urn:ietf:params:scim:schemas:extension:facilitor:2.0:User";
function transform_incoming(params, data)
{

if (0 && FCLT.DEZE.customerId == "ALKM" && params.method == "PATCH")
{
    FCLT.DEZE.__Logging = 1; // voor dit bestand
}


FCLT.DEZE.__Log("transform_incoming");
FCLT.DEZE.__Log(params);
FCLT.DEZE.__Log(data);
    if (params.method == "PATCH") // https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2
    {
        if (!("schemas" in data) || data.schemas[0] != "urn:ietf:params:scim:api:messages:2.0:PatchOp")
            FCLT.DEZE.api2.error(400, "Missing or bad schema");

        var newdata = {};
        for (var i = 0; i < data.Operations.length; i++)
        {
            var oper = data.Operations[i];
            // de extensions hebben met de 2.0 een punt in zich. Dat maakt splitten lastig
            if (oper.path.indexOf(facilitor_urn) == 0) // "path": "urn:ietf:params:scim:schemas:extension:facilitor:2.0:User:title",
            {
                path = oper.path.substring(facilitor_urn.length + 1).split(".");
                path.unshift(facilitor_urn);
            }
            else if (oper.path.indexOf(enterprise_urn) == 0) //
            {
                path = oper.path.substring(enterprise_urn.length + 1).split(".");
                path.unshift(enterprise_urn);
            }
            else
                var path = oper.path.split(".");
            var datapath = newdata;
            while (path.length > 1)
            {
                if (!(path[0] in datapath))
                {
                    datapath[path[0]] = {};
                }
                datapath = datapath[path[0]];
                path.shift();
            }
            switch (oper.op.toLowerCase())
            {
                case "add":
                    datapath[path[0]] = oper.value;
                    break;
                case "remove":
                    datapath[path[0]] = null;
                    break;
                case "replace":
                    datapath[path[0]] = oper.value;
                    break;
                default:
                    // unknown op?
            }
        }
        FCLT.DEZE.__Log("newdata");
        FCLT.DEZE.__Log(newdata);
        data = newdata;
        /* IN
          {
            "schemas": [
                "urn:ietf:params:scim:api:messages:2.0:PatchOp"
            ],
            "Operations": [
                {
                    "op": "Replace",
                    "path": "displayName",
                    "value": "facilitortest2"
                },
                {
                    "op": "Replace",
                    "path": "name.givenName",
                    "value": "Facilitor Jos"
                },
                {
                    "op": "Replace",
                    "path": "name.formatted",
                    "value": "Facilitor Jos Test2"
                },
                {
                    "op": "Replace",
                    "path": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber",
                    "value": "EmploID_JGL"
                }
            ]
          }
        UIT:
          {
            "schemas": [
                "urn:ietf:params:scim:schemas:core:2.0:User",
                "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
            ],
            "externalId": "facilitortest",
            "userName": "facilitortest@outlook.com",
            "active": true,
            "displayName": "facilitortest",
            "emails": [
                {
                    "primary": true,
                    "type": "work",
                    "value": "facilitortest@outlook.com"
                }
            ],
            "meta": {
                "resourceType": "User"
            },
            "name": {
                "formatted": "Fac Test",
                "familyName": "Test",
                "givenName": "Fac"
            }
          }
        */
    }

    var enterprise = data[enterprise_urn];
    var facilitor = data[facilitor_urn];

    var person = {};
    assign(person, "login", data, "userName");
    assign(person, "externallogin", data, "userName"); // SCIM only, not uppercased to prevent update war
    assign(person, "externalid", data, "externalId");
    if ("name" in data)
    {
        assign(person, "lastname", data["name"], "familyName");
        assign(person, "middlename", data["name"], "middleName");
        assign(person, "firstname", data["name"], "givenName");
    }
    if ("active" in data)
    {
        // Azure stuurt bij een patch "value": "False" wat "value": false
        // zou moeten zijn (https://stackoverflow.com/questions/59264809)
        // Pad van de minste weerstand is om zelf ook string "False" maar te ondersteunen
        person.deactivated = data["active"]&&data["active"]!="False"?null:new Date();
    }
    if (params.method == "POST")
    {
        person["function"] = { name: "Onbekend" }; // mogelijk geen onderdeel van de interface
        person["department"] = { id: 1 }; // bij het aanmaken van een nieuwe persoon is de afdeling nog leeg.
                                          // Dan tijdelijk even onder onbekend hangen.
                                          // TODO: Die key '1' is niet gegarandeerd?
    }

    if (enterprise)
    {
        assign(person, "employeenumber", enterprise, "employeeNumber");
        if (enterprise["organization"]) // alleen gebruikt om unieke afdeling te vinden, niet afzonderlijk opgeslagen
        {
            var companyname = get_element(enterprise["organization"]);
        }

        if (enterprise["department"])
        {
            // We hebben S("prs_dep_string") op "d.prs_afdeling_omschrijving" geforceerd. Daarmee werkt het volgende
            if (companyname)
            {
                var sql = "SELECT prs_bedrijf_key"
                        + "  FROM prs_bedrijf "
                        + " WHERE prs_bedrijf_verwijder IS NULL "
                        + "   AND prs_bedrijf_naam_upper = " + FCLT.DEZE.safe.quoted_sql_upper(companyname, 60);
                var oRs = FCLT.DEZE.Oracle.Execute(sql);
                if (oRs.EOF) // Auto aanmaken
                {
                    oRs.Close();
                    var sqli = "INSERT INTO prs_bedrijf"
                             + "    (prs_bedrijf_naam, prs_bedrijf_intern) "
                             + "    VALUES (" + FCLT.DEZE.safe.quoted_sql(companyname, 60) + ", 1)";
                    FCLT.DEZE.Oracle.Execute(sqli);
                    var oRs = FCLT.DEZE.Oracle.Execute(sql); // opnieuw zoeken
                }
                var bedrijf_key = oRs("prs_bedrijf_key").Value;
                oRs.Close()
            }
            else if (params.filter.id)
            {
                var sql = "SELECT prs_bedrijf_key"
                        + "  FROM prs_perslid p, prs_v_afdeling a"
                        + " WHERE p.prs_afdeling_key = a.prs_afdeling_key "
                        + "   AND prs_perslid_key = " + params.filter.id;
                var oRs = FCLT.DEZE.Oracle.Execute(sql);
                var bedrijf_key = oRs("prs_bedrijf_key").Value;
                oRs.Close()
            }
            else if (FCLT.DEZE.S("scim_default_bedrijf") > 0)
            {
                var bedrijf_key = FCLT.DEZE.S("scim_default_bedrijf");
            }
            else
            {
                FCLT.DEZE.api2.error(400, "Missing organization for department");
            }

            var depoms = get_element(enterprise["department"]);
             // Als de department (prs_afdeling) nog niet bestaat gaan we deze aanmaken.
            var sql = "SELECT prs_afdeling_key"
                    + "  FROM prs_afdeling "
                    + " WHERE prs_afdeling_verwijder IS NULL "
                    + "   AND prs_bedrijf_key = " + bedrijf_key // alleen afdelingen op hoogste niveau worden ondersteund
                    + "   AND UPPER(prs_afdeling_omschrijving) = " + FCLT.DEZE.safe.quoted_sql_upper(depoms, 60);
            var oRs = FCLT.DEZE.Oracle.Execute(sql);
            if (oRs.eof)
            {
                oRs.Close();
                // unieke depnaam verzinnen
                // Als je prs_dep_string op d.prs_afdeling_omschrijving zet wordt
                // deze gegenereerde code grotendeels verborgen
                var sqls = "SELECT prs_s_prs_afdeling_key.nextval FROM DUAL";
                var oRs = FCLT.DEZE.Oracle.Execute( sqls );
                var seq_key = oRs(0).value;
                oRs.Close();
                var depnaam = FCLT.DEZE.customerId + "_" + seq_key;
                var sqli = "INSERT INTO prs_afdeling"
                         + "    (prs_afdeling_key, prs_bedrijf_key, prs_afdeling_naam, prs_afdeling_omschrijving) "
                         + "    VALUES (" + seq_key + ","
                                          + bedrijf_key + ","
                                          + FCLT.DEZE.safe.quoted_sql(depnaam) + ", "
                                          + FCLT.DEZE.safe.quoted_sql(depoms, 60) + ")";
                FCLT.DEZE.Oracle.Execute(sqli);
                var oRs = FCLT.DEZE.Oracle.Execute(sql); // opnieuw zoeken
            }
            var afdeling_key = oRs("prs_afdeling_key").Value;

            oRs.Close();
            person["department"] = { id: afdeling_key }; // hier gaat het uiteindelijk om
        }
    }

    if (facilitor) // is die schema extension meegegeven?
    {
        if (facilitor["title"])
        {
            var srtperslidname = get_element(facilitor["title"]);
             // Als de functie (srt_perslid) nog niet bestaat gaan we deze aanmaken.
            var sql = "SELECT prs_srtperslid_omschrijving"
                    + "  FROM prs_srtperslid "
                    + " WHERE prs_srtperslid_verwijder IS NULL "
                    + "   AND prs_srtperslid_upper = " + FCLT.DEZE.safe.quoted_sql_upper(srtperslidname);
            var oRs = FCLT.DEZE.Oracle.Execute(sql);
            if (oRs.eof)
            {
                sql = "INSERT INTO prs_srtperslid (prs_srtperslid_omschrijving) "
                    + "VALUES (" + FCLT.DEZE.safe.quoted_sql(srtperslidname) + ")";
                FCLT.DEZE.Oracle.Execute(sql);
            }
            else
                srtperslidname = oRs("prs_srtperslid_omschrijving").Value;
            oRs.Close();
            person["function"] = { name: srtperslidname }; // id mag achterwege blijven omdat desc_is_unique
        }

        if (facilitor["orgUnit"] && facilitor["orgUnit"]["value"] )
        {
            var dep_id = parseInt(facilitor["orgUnit"]["value"].replace (FCLT.DEZE.customerId + "_department_", ""), 10);
            person["department"] = { id: dep_id };
        }

        if (facilitor["initials"])
            person.initials = get_element(facilitor["initials"]);
    }

    if (data["emails"] && data["emails"][0])
    {
       person.email = get_element(data["emails"][0].value); // type:work/primary:true opzoeken ?
    }

    /*
        "Operations": [
        {
            "op": "Add",
            "path": "phoneNumbers[type eq \"mobile\"].value",
            "value": "+31657419073"
        }
    */
    if (data["emails[type eq \"work\"]"]) // Zo komt een PATCH effectief binnen
    {
        person.email = get_element(data["emails[type eq \"work\"]"].value);
    }

    if (data.phoneNumbers)
    {
        for (var j = 0; j < data["phoneNumbers"].length; j++)
        {   if (data["phoneNumbers"][j]["type"] == 'mobile')
            {
                person.mobile = get_element(data["phoneNumbers"][j].value);
            }
            if (data["phoneNumbers"][j]["type"] == 'work')
            {
                person.phone = get_element(data["phoneNumbers"][j].value);
            }
        }
    }
    if (data["phoneNumbers[type eq \"mobile\"]"])
    {
        person.mobile = get_element(data["phoneNumbers[type eq \"mobile\"]"].value);
    }
    if (data["phoneNumbers[type eq \"work\"]"])
    {
        person.phone = get_element(data["phoneNumbers[type eq \"work\"]"].value);
    }

FCLT.DEZE.__Log({ person: person });
    return { person: person };
}

function transform_one_person(params, person)
{
    var unique = FCLT.DEZE.customerId + "_person_" + String(person.id);

    var oneuser = {
           "userName":person.externallogin || person.login, // externallogin is SCIM only, not uppercased to prevent update war
           "id":unique,
           "name":{
              "formatted":person.name,
              "familyName":person.lastname
           },
           "schemas":[
              "urn:ietf:params:scim:schemas:core:2.0:User",
              "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
           ],
           "displayName":person.name,
           "active": !person.deactivated,
           "meta":{
              "created":person.created,
              "lastModified":person.lastchange?person.lastchange:person.created,
              "location":FCLT.DEZE.HTTP.urlzelf() + "/appl/SCIM/Users/" + unique,
              "resourceType":"User"
           },
           "groups":[],
           "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" :
               {
                   "organization":person.company.name,
                   "department":person.department.name, // Let op: we hebben S("prs_dep_string") op "d.prs_afdeling_omschrijving" geforceerd.
                   "employeeNumber":person.employeenumber
               },
           "urn:ietf:params:scim:schemas:extension:facilitor:2.0:User" :
               {
               "orgunit":
                  {
                     "value":FCLT.DEZE.customerId + "_department_" + String(person.department.id),
                     "$ref":FCLT.DEZE.HTTP.urlzelf() + "/appl/SCIM/OrgUnits/" + FCLT.DEZE.customerId + "_department_" + String(person.department.id)
                  },
               "initials":person.initials,
               "title":person["function"].name
               }
        }

    if (person.middlename) oneuser.name.middleName = person.middlename;
    if (person.firstname) oneuser.name.givenName = person.firstname;
    if (person.externalid) oneuser.externalId =person.externalid;
    if (person.email)
    {
        oneuser.emails = [
              {
                 "value":person.email,
                 "type":"work",
                 "primary":true
              }];
    }

    var phoneNumbers = [];

    if (person.mobile)
           phoneNumbers.push(
              {
                 "value":person.mobile,
                 "type":"mobile",
                 "primary":true
              });
    if (person.phone)
            phoneNumbers.push(
              {
                 "value":person.phone,
                 "type":"work",
                 "primary":false
              });
    oneuser.phoneNumbers = phoneNumbers;

    var allgroups = [];
    for (var j = 0; j < person.authorization.length; j++)
    {
        var group = person.authorization[j];
        var unique = FCLT.DEZE.customerId + "_authorizationgroup_" + String(group.authorizationgroup.id);
        oneuser.groups.push({
                 "value":unique,
                 "$ref":FCLT.DEZE.HTTP.urlzelf() + "/appl/SCIM/Groups/" + unique,
                 "display":group.authorizationgroup.name
              })
    }
    var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
    var eTag = '"' + oCrypto.hex_sha1(FCLT.DEZE.JSON.stringify(oneuser)).toLowerCase() + '"';
    oneuser.meta["version"] = "W/" + eTag; // Weak eTag?
    return oneuser;
}

function transform_outgoing(params, data)
{
    if ("person" in data)
    {
        var oneperson = transform_one_person(params, data.person);
        return oneperson;
    }
    if ("persons" in data)
    {
        var allusers = [];
        for (var i = 0; i < data.persons.length; i++)
        {
            var person = data.persons[i]; // _ users zijn er al wel uitgefiltert
            allusers.push(transform_one_person(params, person));
        }

        return {
        	"totalResults": data.total_count,
        	"itemsPerPage": allusers.length,
        	"startIndex": (data.offset || 0) + 1,
        	"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
        	"Resources": allusers
        };
    }
}

]]>
</script>
</component>
