"use strict"; define(["jquery", "underscore", "backbone", "models/AccessRule"], function($, _, Backbone, AccessRule) { /** * @class AccessPolicy * @classdesc An AccessPolicy collection is a collection of AccessRules that specify * the permissions set on a DataONEObject * @classcategory Collections */ var AccessPolicy = Backbone.Collection.extend( /** @lends AccessPolicy.prototype */ { model: AccessRule, /** * The DataONEObject that will be saved with this AccessPolicy * @type {DataONEObject} */ dataONEObject: null, initialize: function(){ //When a model triggers the event "removeMe", remove it from this collection this.on("removeMe", this.removeAccessRule); }, /** * Parses the given access policy XML and creates AccessRule models for * each rule in the access policy XML. Adds these models to this collection. * @param {Element} The XML DOM that contains a set of * access rules. */ parse: function(accessPolicyXML){ var originalLength = this.length, newLength = 0; //Parse each "allow" access rule _.each( $(accessPolicyXML).children(), function(accessRuleXML, i){ var accessRuleModel; //Update the AccessRule models that already exist in the collection, first. // This is important to keep listeners thoughout the app intact. if( AccessRule.prototype.isPrototypeOf(this.models[i]) ){ accessRuleModel = this.models[i]; } //Create new AccessRules for all others else{ accessRuleModel = new AccessRule(); this.add( accessRuleModel ); } newLength++; //Reset all the values first accessRuleModel.set( accessRuleModel.defaults() ); //Parse the AccessRule model and update the model attributes accessRuleModel.set( accessRuleModel.parse(accessRuleXML) ); //Save a reference to the DataONEObbject accessRuleModel.set("dataONEObject", this.dataONEObject); }, this); //If there are more AccessRules in this collection than were in the // system metadata XML, then remove the extras if( originalLength > newLength ){ for(var i=0; i < (originalLength - newLength); i++){ this.pop(); } } }, /** * Creates AccessRule member models from the `defaultAccessPolicy` * setting in the AppModel. */ createDefaultPolicy: function(){ //For each access policy in the AppModel, create an AccessRule model _.each(MetacatUI.appModel.get("defaultAccessPolicy"), function(accessRule){ accessRule.dataONEObject = this.dataONEObject; this.add( new AccessRule(accessRule) ); }, this); }, /** * Copies all the AccessRules from the given AccessPolicy and replaces this AccessPolicy * @param {AccessPolicy} otherAccessPolicy * @fires Backbone.Collection#reset * @since 2.15.0 */ copyAccessPolicy: function(otherAccessPolicy){ try{ let accessRules = []; //For each access policy in the AppModel, create an AccessRule model otherAccessPolicy.each(function(accessRule){ //Convert the AccessRule model to JSON and update the reference to the DataONEObject let accessRuleJSON = accessRule.toJSON(); accessRuleJSON.dataONEObject = this.dataONEObject; accessRules.push(accessRuleJSON); }, this); //Reset the Collection with these AccessRules this.reset(accessRules); } catch(e){ console.error(e); } }, /** * Creates an access policy XML from the values set on the member * AccessRule models. * @return {string} A string of the access policy XML */ serialize: function(){ if( this.length == 0 ) return ""; //Create the access policy node which will contain all the rules var xml = "\n"; //Serialize each AccessRule member model and add to the policy DOM this.each(function(accessRule){ xml += accessRule.serialize(); }); xml += "\n" //Convert the access policy DOM to a string and return it return xml; }, /** * Removes access rules that grant public access and sets an access rule * that denies public read. */ makePrivate: function(){ var alreadyPrivate = false; //Find the public access rules and remove them this.each( function(accessRule){ if( typeof accessRule === "undefined" ) return; //If the access rule subject is `public` and they are given any kind of access, if( accessRule.get("subject") == "public" && (accessRule.get("read") || accessRule.get("write") || accessRule.get("changePermission")) ){ //Remove this AccessRule model from the collection this.remove(accessRule); } }, this); }, /** * Removes any AccessRule that denies public read and adds an AccessRule * that allows public read */ makePublic: function(){ var alreadyPublic = false; //Find any public read rule and set read=true this.each( function(accessRule){ if( typeof accessRule === "undefined" ) return; //If the access rule subject is `public` and they are denied read access if( accessRule.get("subject") == "public" ){ //Remove this AccessRule model from the collection accessRule.set("read", true); alreadyPublic = true; } }, this); //If this policy does not already allow the public read access, then add that rule if( !alreadyPublic ){ //Create an access rule that allows public read var publicAllow = new AccessRule({ subject: "public", read: true, dataONEObject: this.dataONEObject }); //Add this access rule this.add(publicAllow); } }, /** * Returns true if this access policy specifies that it is accessible to * the public in any way * @return {boolean} */ isPublic: function(){ var isPublic = false; this.each(function(accessRule){ if( accessRule.get("subject") == "public" && (accessRule.get("read") || accessRule.get("write") || accessRule.get("changePermission")) ){ isPublic = true; } }); return isPublic; }, /** * Checks if the current user is authorized to perform the given action * based on the current access rules in this collection * * @param {string} action - The action to check authorization for. Can * be either `read`, `write`, or `changePermission` * @return {boolean} - Returns true is the user can perform this action, * false if not. */ isAuthorized: function(action){ if( typeof action == "undefined" || !action ) return false; //Get the access rules for the user's subject or groups var allSubjects = []; if( !MetacatUI.appUserModel.get("loggedIn") ) allSubjects = "public"; else{ allSubjects = _.union(MetacatUI.appUserModel.get("identities"), _.pluck(MetacatUI.appUserModel.get("isMemberOf"), "groupId"), [MetacatUI.appUserModel.get("username")]); } //Find the access rules that match the given action and user subjects var applicableRules = this.filter(function(accessRule){ if( accessRule.get(action) && _.contains(allSubjects, accessRule.get("subject")) ) { return true; } }, this); if( applicableRules.length ) return true; else if( _.contains(allSubjects, this.dataONEObject.get("rightsHolder")) ) return true; else return false; }, /** * Checks if the user is authorized to update the system metadata. * Updates to system metadata will fail if the user doesn't have changePermission permission, * *unless* the user is performing an update() at the same time and has `write` permission * @returns {boolean} * @since 2.15.0 */ isAuthorizedUpdateSysMeta: function(){ try{ //Yes, if the user has changePermission if( this.isAuthorized("changePermission") ){ return true; } //Yes, if the user just uploaded this object and is saving it for the first time else if( this.isAuthorized("write") && this.dataONEObject.isNew() ){ return true; } else{ return false; } } catch(e){ console.error("Failed to determing authorization: " , e); return false; } }, /** * Gets the subject info for all of the subjects in this access policy. * Sets the subject info on each corresponding model. */ getSubjectInfo: function(){ //If there are more than 5 subjects in the access policy, then get the entire list of subjects in the DataONE/CN system /* if( this.length > 5 ){ //TODO: Get everything from the /accounts endpoint } */ //If there are less than 5, then send individual requests to get the subject info this.invoke("getSubjectInfo"); }, /** * Remove the given AccessRule from this AccessPolicy * @param {AccessRule} accessRule - The AccessRule model to remove */ removeAccessRule: function(accessRule){ this.remove(accessRule); }, /** * Checks if there is at least one AccessRule with changePermission permission * in this AccessPolicy. * @returns {boolean} */ hasOwner: function(){ try{ var owners = this.where({ changePermission: true }); //Check if there are any other subjects with ownership levels if( !owners || owners.length == 0 ){ //If there is a rightsHolder, that counts as an owner /* if( this.dataONEObject && this.dataONEObject.get("rightsHolder") ){ return true; } */ return false; } else{ return true; } } catch(e){ console.error("Error getting the owners of this AccessPolicy: ", e); } }, replaceRightsHolder: function(){ var owner = this.findWhere({ changePermission: true }); //Make sure the owner model was found if( !owner ){ return; } //Set this other owner as the rightsHolder this.dataONEObject.set("rightsHolder", owner.get("subject")); //Remove them as an AccessRule in the AccessPolicy this.remove(owner); } }); return AccessPolicy; });