/******************************************************************************* * 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 AccessTokenModel = Backbone.Model.extend({ idAttribute: 'id', defaults: { id: null, value: null, refreshTokenId: null, scopes: [], clientId: null, userId: null, expiration: null }, urlRoot: 'api/tokens/access' }); var AccessTokenCollection = Backbone.Collection.extend({ idAttribute: 'id', model: AccessTokenModel, url: 'api/tokens/access' }); var AccessTokenView = Backbone.View.extend({ tagName: 'tr', initialize: function(options) { this.options = options; if (!this.template) { this.template = _.template($('#tmpl-access-token').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); }, events: { 'click .btn-delete': 'deleteToken', 'click .token-substring': 'showTokenValue', 'click .toggleMoreInformation': 'toggleMoreInformation' }, render: function(eventName) { var expirationDate = this.model.get("expiration"); if (expirationDate == null) { expirationDate = "Never"; } else if (!moment(expirationDate).isValid()) { expirationDate = "Unknown"; } else { expirationDate = moment(expirationDate).calendar(); } var json = { token: this.model.toJSON(), client: this.options.client.toJSON(), formattedExpiration: expirationDate }; this.$el.html(this.template(json)); // hide full value $('.token-full', this.el).hide(); // show scopes $('.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; }, deleteToken: function(e) { e.preventDefault(); if (confirm($.t("token.token-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 // refresh the table in case we removed an id token, // too _self.parentView.refreshTable(); }); }); }, error: app.errorHandlerView.handleError() }); this.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'); } }, close: function() { $(this.el).unbind(); $(this.el).empty(); }, showTokenValue: function(e) { e.preventDefault(); $('.token-substring', this.el).hide(); $('.token-full', this.el).show(); } }); var RefreshTokenModel = Backbone.Model.extend({ idAttribute: 'id', defaults: { id: null, value: null, scopes: [], clientId: null, userId: null, expiration: null }, urlRoot: 'api/tokens/refresh' }); var RefreshTokenCollection = Backbone.Collection.extend({ idAttribute: 'id', model: RefreshTokenModel, url: 'api/tokens/refresh' }); var RefreshTokenView = Backbone.View.extend({ tagName: 'tr', initialize: function(options) { this.options = options; if (!this.template) { this.template = _.template($('#tmpl-refresh-token').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); }, events: { 'click .btn-delete': 'deleteToken', 'click .token-substring': 'showTokenValue', 'click .toggleMoreInformation': 'toggleMoreInformation' }, render: function(eventName) { var expirationDate = this.model.get("expiration"); if (expirationDate == null) { expirationDate = "Never"; } else if (!moment(expirationDate).isValid()) { expirationDate = "Unknown"; } else { expirationDate = moment(expirationDate).calendar(); } var json = { token: this.model.toJSON(), client: this.options.client.toJSON(), formattedExpiration: expirationDate, accessTokenCount: this.options.accessTokenCount }; this.$el.html(this.template(json)); // hide full value $('.token-full', this.el).hide(); // show scopes $('.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; }, deleteToken: function(e) { e.preventDefault(); if (confirm($.t('token.token-table.confirm-refresh'))) { 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 // refresh the table in case the access tokens have // changed, too _self.parentView.refreshTable(); }); }); }, error: app.errorHandlerView.handleError() }); _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'); } }, close: function() { $(this.el).unbind(); $(this.el).empty(); }, showTokenValue: function(e) { e.preventDefault(); $('.token-substring', this.el).hide(); $('.token-full', this.el).show(); } }); var TokenListView = Backbone.View.extend({ tagName: 'span', initialize: function(options) { this.options = options; }, events: { "click .refresh-table": "refreshTable", 'page .paginator-access': 'changePageAccess', 'page .paginator-refresh': 'changePageRefresh' }, load: function(callback) { if (this.model.access.isFetched && this.model.refresh.isFetched && this.options.clientList.isFetched && this.options.systemScopeList.isFetched) { callback(); return; } $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('token.token-table.access-tokens') + ' ' + '' + $.t('token.token-table.refresh-tokens') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); $.when(this.model.access.fetchIfNeeded({ success: function(e) { $('#loading-access').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.model.refresh.fetchIfNeeded({ success: function(e) { $('#loading-refresh').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.options.clientList.fetchIfNeeded({ success: function(e) { $('#loading-clients').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.options.systemScopeList.fetchIfNeeded({ success: function(e) { $('#loading-scopes').addClass('label-success'); }, error: app.errorHandlerView.handleError() })).done(function() { $('#loadingbox').sheet('hide'); callback(); }); }, changePageAccess: function(event, num) { $('.paginator-access', this.el).bootpag({ page: num }); $('#access-token-table tbody tr', this.el).each(function(index, element) { if (Math.ceil((index + 1) / 10) != num) { $(element).hide(); } else { $(element).show(); } }); }, changePageRefresh: function(event, num) { $('.paginator-refresh', this.el).bootpag({ page: num }); $('#refresh-token-table tbody tr', this.el).each(function(index, element) { if (Math.ceil((index + 1) / 10) != num) { $(element).hide(); } else { $(element).show(); } }); }, refreshTable: function(e) { $('#loadingbox').sheet('show'); $('#loading').html( '' + $.t('token.token-table.access-tokens') + ' ' + '' + $.t('token.token-table.refresh-tokens') + ' ' + '' + $.t('common.clients') + ' ' + '' + $.t('common.scopes') + ' '); var _self = this; $.when(this.model.access.fetch({ success: function(e) { $('#loading-access').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.model.refresh.fetch({ success: function(e) { $('#loading-refresh').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.options.clientList.fetch({ success: function(e) { $('#loading-clients').addClass('label-success'); }, error: app.errorHandlerView.handleError() }), this.options.systemScopeList.fetch({ success: function(e) { $('#loading-scopes').addClass('label-success'); }, error: app.errorHandlerView.handleError() })).done(function() { _self.render(); $('#loadingbox').sheet('hide'); }); }, togglePlaceholder: function() { if (this.model.access.length > 0) { $('#access-token-table', this.el).show(); $('#access-token-table-empty', this.el).hide(); } else { $('#access-token-table', this.el).hide(); $('#access-token-table-empty', this.el).show(); } if (this.model.refresh.length > 0) { $('#refresh-token-table', this.el).show(); $('#refresh-token-table-empty', this.el).hide(); } else { $('#refresh-token-table', this.el).hide(); $('#refresh-token-table-empty', this.el).show(); } $('#access-token-count', this.el).html(this.model.access.length); $('#refresh-token-count', this.el).html(this.model.refresh.length); }, render: function(eventName) { // append and render the table structure $(this.el).html($('#tmpl-token-table').html()); var _self = this; // set up pagination var numPagesAccess = Math.ceil(this.model.access.length / 10); if (numPagesAccess > 1) { $('.paginator-access', this.el).show(); $('.paginator-access', this.el).bootpag({ total: numPagesAccess, page: 1 }); } else { $('.paginator-access', this.el).hide(); } // count up refresh tokens var refreshCount = {}; _.each(this.model.access.models, function(token, index) { // look up client var client = _self.options.clientList.getByClientId(token.get('clientId')); var view = new AccessTokenView({ model: token, client: client, systemScopeList: _self.options.systemScopeList }); view.parentView = _self; var element = view.render().el; $('#access-token-table', _self.el).append(element); if (Math.ceil((index + 1) / 10) != 1) { $(element).hide(); } // console.log(token.get('refreshTokenId')); var refId = token.get('refreshTokenId'); if (refId != null) { if (refreshCount[refId]) { refreshCount[refId] += 1; } else { refreshCount[refId] = 1; } } }); // console.log(refreshCount); // set up pagination var numPagesRefresh = Math.ceil(this.model.refresh.length / 10); if (numPagesRefresh > 1) { $('.paginator-refresh', this.el).show(); $('.paginator-refresh', this.el).bootpag({ total: numPagesRefresh, page: 1 }); } else { $('.paginator-refresh', this.el).hide(); } _.each(this.model.refresh.models, function(token, index) { // look up client var client = _self.options.clientList.getByClientId(token.get('clientId')); var view = new RefreshTokenView({ model: token, client: client, systemScopeList: _self.options.systemScopeList, accessTokenCount: refreshCount[token.get('id')] }); view.parentView = _self; var element = view.render().el; $('#refresh-token-table', _self.el).append(element); if (Math.ceil((index + 1) / 10) != 1) { $(element).hide(); } }); /* * _.each(this.model.models, function (scope) { $("#scope-table", * this.el).append(new SystemScopeView({model: scope}).render().el); }, * this); */ this.togglePlaceholder(); $(this.el).i18n(); return this; } }); ui.routes.push({ path: "user/tokens", name: "tokens", callback: function() { this.breadCrumbView.collection.reset(); this.breadCrumbView.collection.add([{ text: $.t('admin.home'), href: "" }, { text: $.t('token.manage'), href: "manage/#user/tokens" }]); this.updateSidebar('user/tokens'); var view = new TokenListView({ model: { access: this.accessTokensList, refresh: this.refreshTokensList }, clientList: this.clientList, systemScopeList: this.systemScopeList }); view.load(function(collection, response, options) { $('#content').html(view.render().el); setPageTitle($.t('token.manage')); }); } }); ui.templates.push('resources/template/token.html'); ui.init.push(function(app) { app.accessTokensList = new AccessTokenCollection(); app.refreshTokensList = new RefreshTokenCollection(); });