/*global define */
define(['jquery', 'underscore', 'backbone'],
function($, _, Backbone) {
'use strict';
/**
* @class AccessRule
* @classdesc A model that specifies a single permission set on a DataONEObject
* @classcategory Models
*/
var AccessRule = Backbone.Model.extend(
/** @lends AccessRule */
{
defaults: function(){
return{
subject: null,
read: null,
write: null,
changePermission: null,
name: null,
dataONEObject: null
}
},
initialize: function(){
},
/**
* Translates the access rule XML DOM into a JSON object to be set on the model.
* @param {Element} accessRuleXML An <allow> DOM element that contains a single access rule
* @return {JSON} The Access Rule values to be set on this model
*/
parse: function(accessRuleXML) {
// If there is no access policy, do not attempt to parse anything
if (typeof accessRuleXML === "undefined" || !accessRuleXML) {
return {};
}
var accessRuleXMLObj = $(accessRuleXML);
// Start an access rule object with the given subject
var parsedAccessRule = {
subject: accessRuleXMLObj.find("subject").text()
};
_.each(accessRuleXMLObj.find("permission"), function(permissionNode, idx) {
let permissionText = $(permissionNode).text().trim();
// Check if the permission text is not empty
if (permissionText.length) {
// Save the parsed permission
parsedAccessRule[permissionText] = true;
} else {
// This is added as a workaround for malformed permission XML
// introduced by Chromium 120.X
// See https://github.com/NCEAS/metacatui/issues/2235
// Define the regular expression
let globalPermRegex = /<permission><\/permission>(.*)/g;
// Define the regular expression
let permRegex = /<permission><\/permission>(.*)/;
let accessRoleStr = accessRuleXMLObj.html();
let matches = accessRoleStr.match(globalPermRegex);
// Check if matches exist and have a length
if (matches && matches.length && idx < matches.length) {
let permMatch = matches[idx].match(permRegex);
// Check if permMatch exists and has a length
if (permMatch && permMatch.length) {
parsedAccessRule[permMatch[1]] = true;
}
}
}
});
return parsedAccessRule;
},
/**
* Takes the values set on this model's attributes and creates an XML string
* to be inserted into a DataONEObject's system metadata access policy.
* @returns {object} The access rule XML object or null if not created
*/
serialize: function() {
// Serialize the allow rules
if (this.get("read") || this.get("write") || this.get("changePermission")) {
// Create the <allow> element
var allowElement = document.createElement('allow');
// Create the <subject> element and set its text content
var subjectElement = document.createElement('subject');
subjectElement.textContent = this.get("subject");
// Append the <subject> and <permission> elements to <allow>
allowElement.appendChild(subjectElement);
// Create the <permission> elements and set their text content
var permissions = ['read', 'write', 'changePermission'];
for (var i = 0; i < permissions.length; i++) {
if (this.get(permissions[i])) {
var permissionElement = document.createElement('permission');
permissionElement.textContent = permissions[i];
allowElement.appendChild(permissionElement);
}
}
// Return the <allow> element
return allowElement;
}
// If no access rule is created, return null
return null;
},
/**
* Gets and sets the subject info for the subjects in this access policy.
*/
getSubjectInfo: function(){
//If there is no subject, exit now since there is nothing to retrieve
if( !this.get("subject") ){
return;
}
//If the subject is "public", there is no subject info to retrieve
if( this.get("subject") == "public" ){
this.set("name", "Anyone");
return;
}
//If this is the current user, we can use the name we already have in the app.
if( this.get("subject") == MetacatUI.appUserModel.get("username") ){
if( MetacatUI.appUserModel.get("fullName") ){
this.set("name", MetacatUI.appUserModel.get("fullName"));
return;
}
}
var model = this;
var ajaxOptions = {
url: MetacatUI.appModel.get("accountsUrl") + encodeURIComponent(this.get("subject")),
type: "GET",
dataType: "text",
processData: false,
parse: false,
success: function(response) {
//If there was no response, exit now
if(!response){
return;
}
var xmlDoc;
try{
xmlDoc = $.parseXML(response);
}
catch(e){
//If the parsing XML failed, exit now
console.error("The accounts service did not return valid XML.", e);
return;
}
//If the XML string was not parsed correctly, exit now
if( !XMLDocument.prototype.isPrototypeOf(xmlDoc) ){
return;
}
var subjectNode;
if( model.isGroup() ){
//Find the subject XML node for this person, by matching the text content with the subject
subjectNode = $(xmlDoc).find("group subject:contains(" + model.get("subject") + ")");
}
else{
//Find the subject XML node for this person, by matching the text content with the subject
subjectNode = $(xmlDoc).find("person subject:contains(" + model.get("subject") + ")");
}
//If no subject XML node was found, exit now
if( !subjectNode || !subjectNode.length ){
return;
}
//If more than one subject was found (should be very unlikely), then find the one with the exact matching subject
if( subjectNode.length > 1 ){
_.each(subjectNode, function(subjNode){
if( $(subjNode).text() == model.get("subject") ){
subjectNode = $(subjNode);
}
});
}
var name;
if( model.isGroup() ){
//Get the group name
name = $(subjectNode).siblings("groupName").text();
//If there is no group name, then just use the name parsed from the subject
if( !name ){
name = model.get("subject").substring(3, model.get("subject").indexOf(",DC=dataone") );
}
}
else{
//Get the first and last name for this person
name = $(subjectNode).siblings("givenName").text() + " " + $(subjectNode).siblings("familyName").text();
}
//Set the name on the model
model.set("name", name);
}
}
//Send the XHR
$.ajax(ajaxOptions);
},
/**
* Returns true if the subbject set on this AccessRule is for a group of people.
* @returns {boolean}
*/
isGroup: function(){
try{
//Check if the subject is a group subject
var matches = this.get("subject").match(/CN=.+,DC=dataone,DC=org/);
return (Array.isArray(matches) && matches.length);
}
catch(e){
console.error("Couldn't determine if the subject in this AccessRule is a group: ", e);
return false;
}
}
});
return AccessRule;
});