Update a KM Search Security Script for Mandanten Security

User role: System Administrator

To enable Mandanten security for a library, you need to update its Search Security Script. The following steps use the Incident Library as an example.

  1. Open the Incident_Library_kmsearchsecurity JavaScript in the ScriptLibrary. The out-of-box security script is as follows:

    function getSecurityInfo(user, record)
    {
    return "";
    }
  2. Change the script above to the following.

    * Convert the security control from K2 to Solr.
    * The security settings come from the security group / field, and Mandanten security restriction query string.
    * There is no need to change the record type for different libraries,
    * because the record passed in defines the library and contains the record type information.
    */
    if (!('unique' in Array.prototype)) { /* add function to remove the duplicated element in array, sort the array first */
    	Array.prototype.unique = function(that /* opt */) {
    		this.sort();
    		for ( var i = 1; i < this.length; i++ ) {
    		    if ( this[i] === this[ i - 1 ] ) {
    		        this.splice( i--, 1 );
    		    }
    		}
    	};
    }
    if (!('forEach' in Array.prototype)) { /* add function to iterate element in array */
      Array.prototype.forEach = function(action, that /* opt */) {
        for ( var i = 0, n = this.length; i < n; i++)
          if (i in this)
            action.call(that, this[i], i, this);
      };
    }
    /**
     * generate the security information according to the user passed in.
     * @param user, user name
     * @param record, obsoleted parameter.
     * @returns {String}
     */
    function getSecurityInfo(user, record) {	/* record stands for the current library record */
    	var querystr = "";
    	/* as the table name is already stored in library record, no need to change it for different library*/
    	var tablename = record.sclibtablename;
    	var operatorFile = new SCFile("operator");
    	var rc = operatorFile.doSelect("name=\"" + user + "\"");
    	if (rc == RC_SUCCESS) {
    		var securitygroups = operatorFile.security_group;
    		var mandant = new SCFile("scmandant");
    		var rc_mandant = mandant.doSelect("filename=\"" + tablename + "\""); 
    		if (rc_mandant == RC_SUCCESS) {
    			var mandantField = mandant.fieldname;
    			querystr = checkSecurityGroup(securitygroups, mandantField, tablename);
    		}
    	}
    	if (querystr == "()") {
    		querystr = "";
    	}
    	return querystr;
    }
    
    /**
     * Joins all elements of an array into a string
     *
     * @param {Array} array
     * @returns {String}
     */
    function joinList(array) {
                   var i, strList = "";
                   for (i = 0; i < array.length; i++) {
                                  // convert " " to "\ "
                                  strList += array[i].replace(/ +/g, "\\ ");
                                  if (i < array.length - 1) {
                                                 strList += ", ";
                                  }
                   }
                   
                   return strList;
    }
    
    /**
     * generate the query string according to the security group passed in
     * @param groups, security group ID
     * @param field, field to add security control
     * @returns {String}, query string contains the mandant security setting.
     */
    function checkSecurityGroup(groups, field, tablename) {
    	if(field == null) field ="";
    	field = field.replace('.','');
    	var secgroup = new SCFile("scsecuritygroup");
    	var sm_query = "";
    	var includeList = new Array(); /* value list within the security control */
    	var excludeList = new Array(); /* value list outside the security control */
    	var restrictions = new Array();
    	var sql = "security.id isin " + system.functions.str(groups);
    	var rc = secgroup.doSelect(sql);
    	while(rc == RC_SUCCESS){
    		includeList = includeList.concat(secgroup.include.toArray());
    		excludeList = excludeList.concat(secgroup.exclude.toArray());
    		var restriction = get_scaccess_query(secgroup.security_id, tablename);
    		if(restriction) restrictions.push(restriction);
    		rc = secgroup.getNext();
    	}
    	if(field && includeList.length>0 ){
    		includeList.unique();
    		sm_query = field + ":(" + joinList(includeList) +")"; /* add include value list to query */	
    	}
    	
    	
    	if(excludeList.length>0 && field){ /** add exclude value list to query */
    		excludeList.unique();
    		if(sm_query) {
    			sm_query += " AND ";			
    		}
    		sm_query += "NOT " +  field + ":(" + joinList(excludeList) +")";
    	}
    	
    	restrictions.forEach(function(item){ /* add mandanten restrict query to km search string*/
    		if(sm_query){
    			sm_query += ' AND ' + item;	
    		} else {
    			sm_query += item;
    		}
    		
    	});
    	return sm_query;
    }
    
    /**
     * get restrict query for security group
     * TODO: get the solr field name from library
     * for now, the solr field name simply removes the '.'(dot) in SCFile field name.
     */
    function get_scaccess_query(groupId, tablename){
    	var restriction = '';
    	var scaccess=new SCFile("scaccess");
    	var rc=scaccess.doSelect("filename=\"" + tablename +"\"" + "and	security.id=\""+groupId+"\"");
    	if(rc == RC_SUCCESS){
    		var query = scaccess.restricting_query;
    		restriction = parse(query);
    	}
    	return restriction;
    }
    
    /**
     * convert the restrict query to solr query string 
     * @param sql query string in restrict query.
     * @returns {String}
     */
    function parse(sql){
    	sql = replaceAND(sql);
    	sql = replaceOR(sql);
    	sql = replaceISIN(sql);
    	sql = replaceNotEqual(sql);
    	sql = replaceEqual(sql);
    	sql = replaceEmbrace(sql);
    	sql = convertToSolrFields(sql);
    	sql = replaceQuote(sql);
    	return sql;
    }
    
    /**
     * convert 'and' to 'AND'
     * @param str
     * @returns
     */
    function replaceAND(str){
    	return str.replace(/\band\b/g,'AND');
    }
    
    /**
     * convert 'or' to 'OR'
     * @param str
     * @returns
     */
    function replaceOR(str){
    	return str.replace(/\bor\b/g,'OR');
    }
    
    /**
     * convert 'isin' to ':'
     * @param str
     * @returns
     */
    function replaceISIN(str){
    	return str.replace(/\s*isin\s*/,':');
    }
    
    /**
     * convert 'a~=b' to 'NOT a:b'
     * @param str
     * @returns
     */
    function replaceNotEqual(str) {
    	return str.replace(/\w+\s*~=\s*\w+/g,function(word){
    	word = word.replace(/\s+/g,'');
    	return 'NOT '+ word.replace(/~=/,':');
    });
    }
    
    /**
     * convert 'a=b' to 'a:b'
     * @param str
     * @returns
     */
    function replaceEqual(str) {
    	return str.replace(/=/g,':');
    }
    
    /**
     * convert to solr fields
     * 'a.b.c.d' => 'a_b_c_d'
     */
    function convertToSolrFields(str) {
    	return str.replace(/\./g,"_");
    }
    
    /**
     * convert '{a,b}' to '(a, b)', add a whitespace after comma to adjust solr query format
     * @param str
     * @returns
     */
    function replaceEmbrace(str) {
    	str = str.replace(/{/g,'(');
    	str = str.replace(/}/g,')');
    	str = str.replace(/,/g,', ');
    	return str;
    }
    /**
     * replace double/single quote in given string
     * @param str
     * @returns
     */
    function replaceQuote(str) {
    	str = str.replace(/"|'/g,'');
    	return str;
    }

Differences between Solr and K2 Search Security Scripts

Service Manager applications versions earlier than 9.30 support the K2 search engine. Instructions on how to enable Mandanten security for the Knowledge Management module that uses the K2 search engine can be found in this knowledge document: https://softwaresupport.softwaregrp.com/group/softwaresupport/search-result/-/facetsearch/document/KM436754

For Knowledge Management that uses the Solr search engine, you should follow the instructions in this document. Pay attention to the following differences:

  • For the Solr search engine, you no longer need to change the file names for individual libraries, because each knowledgebase record now already contains the record type information, which the search security script will parse as search configuration.
  • For the Solr search engine, each search security script already contains the security control defined in Mandanten security groups/ Mandanten security restriction queries. The operator in each query will be parsed based on the mappings listed in the following table to match the Solr query.

    K2 Solr
    field=value field:value
    filed~=value NOT field:value
    field='value' field:value
    field.fieldext:value fieldfieldext:value
    field isin {value0,value1}

    field:(value0, value1)

    Note: There is a whitespace after the comma.

    field=value or field=value1 field:value OR field:value1

Related topics

Modify Stop Words