/******************************************************************************* * Copyright 2018 The MIT Internet Trust Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ var ResourceSetModel = Backbone.Model.extend({ urlRoot: 'api/resourceset' }); var ResourceSetCollection = Backbone.Collection.extend({ model: ResourceSetModel, url: 'api/resourceset' }); var PolicyModel = Backbone.Model.extend({ urlRoot: function() { return 'api/resourceset/' + this.options.rsid + '/policy/'; }, initialize: function(model, options) { this.options = options; } }); var PolicyCollection = Backbone.Collection.extend({ model: PolicyModel, url: function() { return 'api/resourceset/' + this.options.rsid + '/policy/'; }, initialize: function(models, options) { this.options = options; } }); var ResourceSetListView = Backbone.View.extend({ tagName: 'span', initialize:function (options) { this.options = options; }, load:function(callback) { if (this.model.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { callback(); return; } $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('policy.resource-sets') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' ' ); $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-resourcesets').addClass('label-success');}}), this.options.clientList.fetchIfNeeded({success:function(e) {$('#loading-clients').addClass('label-success');}}), this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}})) .done(function() { $('#loadingbox').sheet('hide'); callback(); }); }, events: { "click .refresh-table":"refreshTable" }, render:function (eventName) { $(this.el).html($('#tmpl-resource-set-table').html()); var _self = this; _.each(this.model.models, function (resourceSet) { // look up client var client = this.options.clientList.getByClientId(resourceSet.get('clientId')); // if there's no client ID, this is an error! if (client != null) { var view = new ResourceSetView({model: resourceSet, client: client, systemScopeList: _self.options.systemScopeList}); view.parentView = _self; $('#resource-set-table', this.el).append(view.render().el); } }, this); this.togglePlaceholder(); $(this.el).i18n(); return this; }, togglePlaceholder:function() { if (this.model.length > 0) { $('#resource-set-table', this.el).show(); $('#resource-set-table-empty', this.el).hide(); } else { $('#resource-set-table', this.el).hide(); $('#resource-set-table-empty', this.el).show(); } }, refreshTable:function(e) { e.preventDefault(); var _self = this; $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('policy.resource-sets') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' ' ); $.when(this.model.fetch({success:function(e) {$('#loading-resourcesets').addClass('label-success');}}), this.options.clientList.fetch({success:function(e) {$('#loading-clients').addClass('label-success');}}), this.options.systemScopeList.fetch({success:function(e) {$('#loading-scopes').addClass('label-success');}})) .done(function() { $('#loadingbox').sheet('hide'); _self.render(); }); } }); var ResourceSetView = Backbone.View.extend({ tagName: 'tr', initialize:function(options) { this.options = options; if (!this.template) { this.template = _.template($('#tmpl-resource-set').html()); } if (!this.scopeTemplate) { this.scopeTemplate = _.template($('#tmpl-scope-list').html()); } if (!this.moreInfoTemplate) { this.moreInfoTemplate = _.template($('#tmpl-client-more-info-block').html()); } this.model.bind('change', this.render, this); }, render:function(eventName) { var json = {rs: this.model.toJSON(), client: this.options.client.toJSON()}; this.$el.html(this.template(json)); $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('scopes'), systemScopes: this.options.systemScopeList})); $('.client-more-info-block', this.el).html(this.moreInfoTemplate({client: this.options.client.toJSON()})); $(this.el).i18n(); return this; }, events:{ 'click .btn-edit': 'editPolicies', 'click .btn-delete': 'deleteResourceSet', 'click .toggleMoreInformation': 'toggleMoreInformation' }, editPolicies:function(e) { e.preventDefault(); app.navigate('user/policy/' + this.model.get('id'), {trigger: true}); }, deleteResourceSet:function(e) { e.preventDefault(); if (confirm($.t('policy.rs-table.confirm'))) { var _self = this; this.model.destroy({ dataType: false, processData: false, success:function () { _self.$el.fadeTo("fast", 0.00, function () { //fade $(this).slideUp("fast", function () { //slide up $(this).remove(); //then remove from the DOM _self.parentView.togglePlaceholder(); }); }); }, error:function (error, response) { console.log("An error occurred when deleting a resource set"); //Pull out the response text. var responseJson = JSON.parse(response.responseText); //Display an alert with an error message $('#modalAlert div.modal-header').html(responseJson.error); $('#modalAlert div.modal-body').html(responseJson.error_description); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", "keyboard" : true, "show" : true // ensure the modal is shown immediately }); } }); _self.parentView.delegateEvents(); } return false; }, toggleMoreInformation:function(e) { e.preventDefault(); if ($('.moreInformation', this.el).is(':visible')) { // hide it $('.moreInformation', this.el).hide('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-right'); $('.moreInformationContainer', this.el).removeClass('alert').removeClass('alert-info').addClass('muted'); } else { // show it $('.moreInformation', this.el).show('fast'); $('.toggleMoreInformation i', this.el).attr('class', 'icon-chevron-down'); $('.moreInformationContainer', this.el).addClass('alert').addClass('alert-info').removeClass('muted'); } }, }); var PolicyListView = Backbone.View.extend({ tagName: 'span', initialize:function(options) { this.options = options; }, load:function(callback) { if (this.model.isFetched && this.options.rs.isFetched && this.options.systemScopeList.isFetched) { callback(); return; } $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('policy.loading-policies') + ' ' + '' + $.t('policy.loading-rs') + ' ' + '' + $.t("common.scopes") + ' ' ); $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-policies').addClass('label-success');}}), this.options.rs.fetchIfNeeded({success:function(e) {$('#loading-rs').addClass('label-success');}}), this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}})) .done(function() { $('#loadingbox').sheet('hide'); callback(); }); }, events:{ 'click .btn-add':'addPolicy', 'click .btn-cancel':'cancel' }, cancel:function(e) { e.preventDefault(); app.navigate('user/policy', {trigger: true}); }, togglePlaceholder:function() { if (this.model.length > 0) { $('#policy-info', this.el).show(); $('#policy-table', this.el).show(); $('#policy-table-empty', this.el).hide(); } else { $('#policy-info', this.el).hide(); $('#policy-table', this.el).hide(); $('#policy-table-empty', this.el).show(); } }, addPolicy:function(e) { e.preventDefault(); app.navigate('user/policy/' + this.options.rs.get('id') +'/new', {trigger: true}); }, render:function (eventName) { $(this.el).html($('#tmpl-policy-table').html()); var _self = this; _.each(this.model.models, function (policy) { var view = new PolicyView({model: policy, systemScopeList: _self.options.systemScopeList, rs: _self.options.rs}); view.parentView = _self; $('#policy-table', this.el).append(view.render().el); }, this); this.togglePlaceholder(); $(this.el).i18n(); return this; } }); var PolicyView = Backbone.View.extend({ tagName: 'tr', initialize:function(options) { this.options = options; if (!this.template) { this.template = _.template($('#tmpl-policy').html()); } if (!this.scopeTemplate) { this.scopeTemplate = _.template($('#tmpl-scope-list').html()); } }, events:{ 'click .btn-edit':'editPolicy', 'click .btn-remove':'removePolicy' }, editPolicy:function(e) { e.preventDefault(); app.navigate('user/policy/' + this.options.rs.get("id") + '/' + this.model.get('id'), {trigger: true}); }, removePolicy:function(e) { e.preventDefault(); if (confirm($.t('policy.policy-table.confirm'))) { var _self = this; this.model.destroy({ dataType: false, processData: false, success:function () { _self.$el.fadeTo("fast", 0.00, function () { //fade $(this).slideUp("fast", function () { //slide up $(this).remove(); //then remove from the DOM _self.parentView.togglePlaceholder(); }); }); }, error:function (error, response) { console.log("An error occurred when deleting a client"); //Pull out the response text. var responseJson = JSON.parse(response.responseText); //Display an alert with an error message $('#modalAlert div.modal-header').html(responseJson.error); $('#modalAlert div.modal-body').html(responseJson.error_description); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", "keyboard" : true, "show" : true // ensure the modal is shown immediately }); } }); _self.parentView.delegateEvents(); } }, render:function (eventName) { var json = this.model.toJSON(); this.$el.html(this.template(json)); $('.scope-list', this.el).html(this.scopeTemplate({scopes: this.model.get('scopes'), systemScopes: this.options.systemScopeList})); $(this.el).i18n(); return this; } }); var PolicyFormView = Backbone.View.extend({ tagName: 'div', initialize:function(options) { this.options = options; if (!this.template) { this.template = _.template($('#tmpl-policy-form').html()); } this.issuerCollection = new Backbone.Collection(); }, events:{ 'click .btn-share': 'addWebfingerClaim', 'click .btn-share-advanced': 'addAdvancedClaim', 'click .btn-clear': 'clearAllClaims', 'click .btn-save': 'savePolicy', 'click .btn-cancel': 'cancel' }, load:function(callback) { if (this.model.isFetched && this.options.rs.isFetched && this.options.systemScopeList.isFetched) { callback(); return; } $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('policy.loading-policy') + ' ' + '' + $.t('policy.loading-rs') + ' ' + '' + $.t("common.scopes") + ' ' ); $.when(this.model.fetchIfNeeded({success:function(e) {$('#loading-policies').addClass('label-success');}}), this.options.rs.fetchIfNeeded({success:function(e) {$('#loading-rs').addClass('label-success');}}), this.options.systemScopeList.fetchIfNeeded({success:function(e) {$('#loading-scopes').addClass('label-success');}})) .done(function() { $('#loadingbox').sheet('hide'); callback(); }); }, addWebfingerClaim:function(e) { e.preventDefault(); // post to the webfinger helper and get the response back var _self = this; var email = $('#email', this.el).val(); $('#loadingbox').sheet('show'); $('#loading').html( 'Looking up identity provider...' ); var base = $('base').attr('href'); $.getJSON(base + '/api/emailsearch?' + $.param({'identifier': email}), function(data) { // grab the current state of the scopes checkboxes just in case var scopes = $('#scopes input[type="checkbox"]:checked').map(function(idx, elem) { return $(elem).val(); }).get(); _self.model.set({ scopes: scopes, claimsRequired: data }, {trigger: false}); _self.render(); $('#loadingbox').sheet('hide'); }).error(function(jqXHR, textStatus, errorThrown) { console.log("An error occurred when doing a webfinger lookup", errorThrown); $('#loadingbox').sheet('hide'); //Display an alert with an error message $('#modalAlert div.modal-header').html($.t('policy.webfinger-error')); $('#modalAlert div.modal-body').html($.t('policy.webfinger-error-description', {email: email})); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", "keyboard" : true, "show" : true // ensure the modal is shown immediately }); }); }, addAdvancedClaim:function(e) { e.preventDefault(); var name = $('#name', this.el).val(); var friendly = $('#friendly-name', this.el).val(); var rawValue = $('#value', this.el).val(); var valueType = $('#value-type', this.el).val(); var value = null; if (valueType == 'number') { value = Number(rawValue); } else if (valueType == 'boolean') { value = (rawValue.toLowerCase() == 'true'); } else if (valueType == 'json') { value = JSON.parse(rawValue); } else { // treat it as a string, the default value = rawValue; } var issuers = this.issuerCollection.pluck('item'); console.log(name, friendly, rawValue, valueType, value, issuers); if (!_.isEmpty(issuers) && name && value) { // we've got a valid claim, add it to our set // grab the current state of the scopes checkboxes just in case var scopes = $('#scopes input[type="checkbox"]:checked').map(function(idx, elem) { return $(elem).val(); }).get(); var claimsRequired = this.model.get('claimsRequired'); if (!claimsRequired) { claimsRequired = []; } claimsRequired.push({ name: name, friendlyName: friendly, value: value, issuer: issuers }); this.model.set({ scopes: scopes, claimsRequired: claimsRequired }, {trigger: false}); $('#name', this.el).val(''); $('#friendly-name', this.el).val(''); $('#value', this.el).val(''); $('#value-type', this.el).val('text'); this.render(); // re-select the advanced tab $('a[data-target="#policy-advanced-tab"]', this.el).tab('show') } else { // something is missing $('#loadingbox').sheet('hide'); //Display an alert with an error message $('#modalAlert div.modal-header').html($.t('policy.advanced-error')); $('#modalAlert div.modal-body').html($.t('policy.advanced-error-description')); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", "keyboard" : true, "show" : true // ensure the modal is shown immediately }); } }, clearAllClaims:function(e) { e.preventDefault(); if (confirm($.t('policy.policy-form.clear-all-confirm'))) { var scopes = $('#scopes input[type="checkbox"]:checked').map(function(idx, elem) { return $(elem).val(); }).get(); var claimsRequired = []; this.model.set({ scopes: scopes, claimsRequired: claimsRequired }, {trigger: false}); this.render(); } }, savePolicy:function(e) { e.preventDefault(); // get all the scopes that are checked var scopes = $('#scopes input[type="checkbox"]:checked').map(function(idx, elem) { return $(elem).val(); }).get(); var valid = this.model.set({ scopes: scopes }); if (valid) { var _self = this; this.model.save({}, { success:function() { app.systemScopeList.add(_self.model); // refresh the associated RS _self.options.rs.fetch({success: function() { app.navigate('user/policy/' + _self.options.rs.get('id'), {trigger: true}); }}); }, error:function(error, response) { //Pull out the response text. var responseJson = JSON.parse(response.responseText); //Display an alert with an error message $('#modalAlert div.modal-header').html(responseJson.error); $('#modalAlert div.modal-body').html(responseJson.error_description); $("#modalAlert").modal({ // wire up the actual modal functionality and show the dialog "backdrop" : "static", "keyboard" : true, "show" : true // ensure the modal is shown immediately }); } }); } return false; }, cancel:function(e) { e.preventDefault(); app.navigate('user/policy/' + this.options.rs.get('id'), {trigger: true}); }, render:function (eventName) { var json = this.model.toJSON(); var rs = this.options.rs.toJSON(); this.$el.html(this.template({policy: json, rs: rs})); // build and bind issuer view var issuerView = new ListWidgetView({ placeholder: $.t('policy.policy-form.issuer-placeholder'), helpBlockText: $.t('policy.policy-form.issuer-help'), collection: this.issuerCollection}); $("#issuers .controls",this.el).html(issuerView.render().el); $(this.el).i18n(); return this; } }); ui.routes.push({path: "user/policy", name: "policy", callback: function() { this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([ {text:$.t('admin.home'), href:""}, {text:$.t('policy.resource-sets'), href:"manage/#user/policy"} ]); this.updateSidebar('user/policy'); var view = new ResourceSetListView({model: this.resourceSetList, clientList: this.clientList, systemScopeList: this.systemScopeList}); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('policy.resource-sets')); }); } }); ui.routes.push({path: "user/policy/:rsid", name: "editPolicies", callback: function(rsid) { this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([ {text:$.t('admin.home'), href:""}, {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid} ]); this.updateSidebar('user/policy'); var rs = this.resourceSetList.get(rsid); var policies = null; if (rs == null) { // need to load it directly policies = new PolicyCollection([], {rsid: rsid}); rs = new ResourceSetModel({id: rsid}); this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future } else { // the resource set is loaded, preload the claims policies = new PolicyCollection(rs.get('policies'), {rsid: rsid}); policies.isFetched = true; } var view = new PolicyListView({model: policies, rs: rs, systemScopeList: this.systemScopeList}); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('policy.edit-policy')); }); } }); ui.routes.push({path: "user/policy/:rsid/new", name: "newPolicy", callback: function(rsid) { this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([ {text:$.t('admin.home'), href:""}, {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, {text:$.t('policy.new-policy'), href:"manage/#user/policy/" + rsid + "/new"} ]); this.updateSidebar('user/policy'); var policy = policy = new PolicyModel({}, {rsid: rsid}); var rs = this.resourceSetList.get(rsid); if (rs == null) { // need to load it directly rs = new ResourceSetModel({id: rsid}); this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future } var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('policy.edit-policy')); }); } }); ui.routes.push({path: "user/policy/:rsid/:pid", name: "editPolicy", callback: function(rsid, pid) { this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([ {text:$.t('admin.home'), href:""}, {text:$.t('policy.resource-sets'), href:"manage/#user/policy"}, {text:$.t('policy.edit-policies'), href:"manage/#user/policy/" + rsid}, {text:$.t('policy.edit-policy'), href:"manage/#user/policy/" + rsid + "/" + pid} ]); this.updateSidebar('user/policy'); var rs = this.resourceSetList.get(rsid); var policy = null; if (rs == null) { // need to load it directly policy = new PolicyModel({id: pid}, {rsid: rsid}); rs = new ResourceSetModel({id: rsid}); this.resourceSetList.add(rs); // it will be loaded below, don't need to load it again in the future } else { // the resource set is loaded, preload the claims _.each(rs.get('policies'), function(p) { if (p.id == pid) { policy = new PolicyModel(p, {rsid: rsid}); policy.isFetched = true; } }); if (policy == null) { // need to load it directly policy = new PolicyModel({id: pid}, {rsid: rsid}); } } var view = new PolicyFormView({model: policy, rs: rs, systemScopeList: this.systemScopeList}); view.load(function() { $('#content').html(view.render().el); setPageTitle($.t('policy.edit-policy')); }); } }); ui.templates.push('resources/template/policy.html'); ui.init.push(function(app) { app.resourceSetList = new ResourceSetCollection(); });