File manager - Edit - /usr/local/cpanel/base/frontend/jupiter/api_tokens/index.cmb.js
Back
/* # api_tokens/services/apiTokens.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define */ define( 'app/services/apiTokens',[ "angular", "lodash", "cjt/util/locale", "cjt/io/uapi-request", "cjt/io/batch-request", "cjt/modules", "cjt/io/api", "cjt/io/uapi", "cjt/services/APICatcher" ], function(angular, _, LOCALE, APIRequest, BatchAPIRequest) { "use strict"; var MODULE_NAMESPACE = "cpanel.apiTokens.services.apiTokens"; var SERVICE_NAME = "APITokensService"; var MODULE_REQUIREMENTS = [ "cjt2.services.apicatcher" ]; var SERVICE_INJECTABLES = ["APICatcher", "$q", "$log"]; var _currentDateTime = new Date(); _currentDateTime = _currentDateTime.getTime() / 1000; var _twentyFourHours = 24 * 60 * 60; var APIToken = function(id, unrestricted, features, createdOn, expiresAt) { this.id = id; this.new = false; this.label = id; this.unrestricted = unrestricted; if (!_.isBoolean(unrestricted)) { this.unrestricted = unrestricted && unrestricted.toString() === "1"; } this.features = features; this.createdOn = parseInt(createdOn, 10); this.expired = false; this.expiresSoon = false; this.hasExpiry = false; if (expiresAt) { this.hasExpiry = true; this.expiresAt = parseInt(expiresAt, 10); if (this.expiresAt <= _currentDateTime) { this.expired = true; } else if (this.expiresAt - _currentDateTime < _twentyFourHours) { this.expiresSoon = true; } } return this; }; /** * * Service Factory to generate the Account Preferences service * * @module ApiTokensService * @memberof cpanel.apiTokens * * @param {Object} APICatcher base service * @returns {Service} instance of the Domains service */ var SERVICE_FACTORY = function(APICatcher, $q, $log) { var Service = function() {}; Service.prototype = Object.create(APICatcher); _.assign(Service.prototype, { _featuresMetadata: null, _tokens: [], /** * Wrapper for .promise method from APICatcher * * @param {Object} apiCall api call to pass to .promise * @returns {Promise} * * @example $service._promise( $service._apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } ) ); */ _promise: function _promise() { // Because nested inheritence is annoying return APICatcher.promise.apply(this, arguments); }, /** * Wrapper for building an apiCall * * @private * * @param {String} module module name to call * @param {String} func api function name to call * @param {Object} args key value pairs to pass to the api * @returns {UAPIRequest} returns the api call * * @example _apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } ) */ _apiCall: function _createApiCall(module, func, args) { var apiCall = new APIRequest.Class(); apiCall.initialize(module, func, args); return apiCall; }, /** * Wrapper for building an batch apiCall * * @private * * @param {Array<APIRequest>} apiCalls Api calls to process as a batch * @returns {BatchAPIRequest} returns the api call * * @example _batchAPICall( [_apiCall("Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" }), _apiCall("Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" })] ) */ _batchAPICall: function _createBatchAPICall(apiCalls) { var batchAPICall = new BatchAPIRequest.Class(apiCalls); return batchAPICall; }, /** * Process a single result of a fetchTokens call * * @private * * @param {Object} result single result item from a fetchTokens() call * @returns {Object} sanitized result object * * @example fetchTokens() */ _processAPITokenResult: function _processAPITokenResult(apiTokenResult) { var id = apiTokenResult.name; var unrestricted = apiTokenResult.has_full_access; var features = apiTokenResult.features; var createdOn = apiTokenResult.create_time; var expiresAt = apiTokenResult.expires_at; return new APIToken(id, unrestricted, features, createdOn, expiresAt); }, /** * Process the results of a fetchTokens call * * @private * * @param {Object} results fetchTokens() api results object with a .data param * @returns {Array<Object>} returns an array of processed items * * @example _processAPITokensResults({data:[]}) */ _processAPITokensResults: function _processAPITokensResults(results) { var apiTokens = results.data; this._tokens = apiTokens.map(this._processAPITokenResult.bind(this)); return this._tokens; }, /** * Create a fetchTokens api call, but don't process it * * @private * * @returns {APIRequest} returns the APIRequest for fetchTokens(); * * @example _getAPITokens() */ _fetchTokens: function _getAPITokens() { var apiCall = this._apiCall("Tokens", "list"); return apiCall; }, /** * Create a deleteToken api call, but don't process it * * @private * * @param {String} id token name to delete * @returns {APIRequest} returns the APIRequest for fetchTokens(); * * @example _deleteToken("mytokenhash") */ _deleteToken: function _deleteAPIToken(id) { var apiCall = this._apiCall("Tokens", "revoke", { name: id }); return apiCall; }, /** * Process A Feature Result * * @param {string} installed perl boolean 1|0 * @param {string} featureKey id of the feature * @param {string} featureLabel descriptive label of the feature * @param {Array} badges array of badge strings associated with the feature * @returns {object} feature object */ _processFeatureResult: function _processFeatureResult(installed, featureKey, featureLabel, badges) { return { label: featureLabel, id: featureKey, installed: installed && installed.toString() === "1", badges: badges }; }, /** * For each feature item in the results, create a feature object * * @param {Object} results * @returns {Array<Object>} array of feature objects */ _processFeaturesResults: function _processFeaturesResults(results) { var self = this; var data = results.data; if (data) { var features = []; angular.forEach(data, function(installed, featureKey) { var featureLabel = featureKey; var badges = []; if (self._featuresMetadata && self._featuresMetadata[featureKey]) { var featureMeta = self._featuresMetadata[featureKey]; featureLabel = featureMeta.name; if (featureMeta.is_plugin.toString() === "1") { badges.push( LOCALE.maketext("Plugin") ); } } features.push( self._processFeatureResult(installed, featureKey, featureLabel, badges ) ); }); // Only return the installed ones var installedFeatures = features.filter(function(feature) { return feature.installed; }); // Always sort them by label return _.sortBy(installedFeatures, function(feature) { return feature.label; }); } }, /** * Build the getFeatures APICall * * @returns {UAPIRequest} uapi request for Features list_features */ _getFeatures: function _getFeatures() { var apiCall = this._apiCall("Features", "list_features"); return apiCall; }, /** * Process the result of a Features get_features_metadata call * * @param {Object} result object with a data parameter */ _processFeatureMetadataResult: function _processFeatureMetadataResult(result) { var data = result.data; this._featuresMetadata = {}; if (data) { data.forEach(function(featureMetaItem) { this._featuresMetadata[featureMetaItem.id] = featureMetaItem; }, this); } }, /** * Wrapper for building an fetchTokens apiCall * * @public * * @returns {Promise<Object>} returns the promise and then the processed api tokens * * @example fetchTokens() */ fetchTokens: function getAPITokens() { var apiCall = this._fetchTokens(); return this._promise(apiCall).then(this._processAPITokensResults.bind(this)); }, /** * Get the current list of tokens * * @returns {Array<APIToken>} array of APIToken objects */ getTokens: function getTokens() { return this._tokens; }, /** * Wrapper for building an deleteTokens apiCall * * @public * * @param {Array<String>} tokens array of token hashes to delete * @returns {Promise<Object>} returns the promise and then the updated api tokens * * @example deleteTokens(["tokenHASH","otherTokenHash"]) */ deleteTokens: function deleteAPITokens(tokens) { var self = this; var tokenCalls = tokens.map( self._deleteToken.bind(self) ).map( self._promise.bind(self) ); return $q.all(tokenCalls).then( self.fetchTokens.bind(self) ); }, /** * API Wrapper call for obtaining the features list * * @returns {Array<Object>} array of feature objects */ getFeatures: function getFeatures() { var self = this; var featuresAPICall = this._getFeatures(); var apiCalls = [featuresAPICall]; if (!self._featuresMetadata) { apiCalls.unshift( this._apiCall("Features", "get_feature_metadata") ); } var batchCall = self._batchAPICall(apiCalls); return self._promise(batchCall).then(function _separateBatchResults(result) { if (apiCalls.length > 1) { // Extract Feature Metadata for Processing var featureMetadataResult = result.data.shift(); self._processFeatureMetadataResult.call(self, featureMetadataResult); } return result.data.pop(); }).then(self._processFeaturesResults.bind(self)); }, /** * Process the results of a token creation API call * * @param {String} id name of the token created * @param {Boolean} unrestricted whether the token is unrestricted * @param {Array<String>} features added features * @param {Object} results api results object * @returns {String} newly created token */ _processTokenCreationResults: function _processTokenCreationResults(id, unrestricted, features, expiresAt, results) { var data = results.data; var newToken = new APIToken(id, unrestricted, features, new Date().getTime() / 1000, expiresAt); newToken.new = true; this._tokens.push(newToken); return data.token; }, /** * API Wrapper call for createing tokens * * @param {String} id name of the token created * @param {Boolean} unrestricted whether the token is unrestricted * @param {Array<String>} features added features * @param {Array<String>} expiresAt (optional) epoch time in seconds that token expires * @returns {Promise<String>} promise and then the newly created token */ createToken: function createToken(id, unrestricted, features, expiresAt) { var apiCall; if (unrestricted) { apiCall = this._apiCall("Tokens", "create_full_access", { name: id, expires_at: expiresAt }); } else { apiCall = this._apiCall("Tokens", "create_limited", { name: id, feature: features, expires_at: expiresAt }); } return this._promise(apiCall).then(this._processTokenCreationResults.bind(this, id, unrestricted, features, expiresAt)); }, /** * Get an API Token by id * * @param {String} id * @returns {APIToken} api token with the matching id */ getTokenById: function getTokenById(id) { var tokens = this.getTokens(); for (var i = 0; i < tokens.length; i++) { if (tokens[i].id === id) { return tokens[i]; } } return; }, /** * Update the Token restrictions for a token * * @param {String} tokenName id of the token to update * @param {Boolean} unrestricted whether it should be treated as restricted * @param {Array} features what features to restrict by * @returns {Promise} restriction updating promise */ updateTokenRestrictions: function updateTokenRestrictions(tokenName, unrestricted, features) { var apiCall; var self = this; if (unrestricted) { apiCall = this._apiCall("Tokens", "set_full_access", { name: tokenName }); } else { apiCall = this._apiCall("Tokens", "set_features", { name: tokenName, feature: features }); } return self._promise(apiCall).then(function() { var token = self.getTokenById(tokenName); token.unrestricted = unrestricted; token.features = features; }); }, /** * Wrapper to call Token rename * * @param {String} tokenName old token name * @param {Strng} newTokenName new token name * @returns {Promise} rename token promise */ renameToken: function renameToken(tokenName, newTokenName) { var apiCall = this._apiCall("Tokens", "rename", { name: tokenName, new_name: newTokenName }); return this._promise(apiCall); } }); return new Service(); }; SERVICE_INJECTABLES.push(SERVICE_FACTORY); var app = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS); app.factory(SERVICE_NAME, SERVICE_INJECTABLES); return { "class": SERVICE_FACTORY, "serviceName": SERVICE_NAME, "namespace": MODULE_NAMESPACE }; } ); /* # api_tokens/directives/tableShowingDirecitve.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ define( 'app/directives/tableShowing',[ "angular", "cjt/util/locale", "cjt/core" ], function(angular, LOCALE, CJT) { "use strict"; /** * Directive to render the "Showing 1 - 4 of 10" * * @module table-showing * @memberof cpanel.apiTokens * * @param {Number} start first number in range ([1]-4) * @param {Number} limit second number in range (1-[4]) * @param {Number} total total number of items (10) * * @example * <table-showing start="1" limit="4" total="10"></table-showing> * */ var TEMPLATE = "directives/tableShowing.phtml"; var RELATIVE_PATH = "api_tokens/" + TEMPLATE; var TEMPLATE_PATH = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE; var MODULE_NAMESPACE = "cpanel.apiTokens.tableShowing.directive"; var module = angular.module(MODULE_NAMESPACE, []); var CONTROLLER = function($scope) { /** * Get the rendered string from LOCALE * * @method getShowingText * @public * * @return {String} localized string * */ $scope.getShowingText = function getShowingText() { return LOCALE.maketext("[_1] - [_2] of [_3]", $scope.start, $scope.limit, $scope.total); }; }; module.directive("tableShowing", function tableShowing() { return { templateUrl: TEMPLATE_PATH, restrict: "EA", scope: { start: "=", limit: "=", total: "=" }, transclude: true, controller: ["$scope", CONTROLLER] }; }); return { "class": CONTROLLER, "namespace": MODULE_NAMESPACE, "template": TEMPLATE }; } ); /* # cjt/decorators/paginationDecorator.js Copyright(c) 2020 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ define( 'app/decorators/paginationDecorator',[ "angular", "cjt/core", "cjt/util/locale", "uiBootstrap" ], function(angular, CJT, LOCALE) { "use strict"; var module; var MODULE_NAMESPACE = "cpanel.emailAccounts"; var TEMPLATE_PATH = "decorators/pagination.phtml"; var RELATIVE_PATH = "email_accounts/" + TEMPLATE_PATH; try { module = angular.module(MODULE_NAMESPACE); } catch (e) { module = angular.module(MODULE_NAMESPACE, ["ui.bootstrap.pagination"]); } module.config(["$provide", function($provide) { // Extend the ngModelDirective to interpolate its name attribute $provide.decorator("uibPaginationDirective", ["$delegate", function($delegate) { var uiPaginationDirective = $delegate[0]; /** * Update the ids in the page collection * * @method updateIds * @param {Array} pages * @param {string} id Id of the directive, used as a prefix */ var updateIds = function(pages, id) { if (!pages) { return; } pages.forEach(function(page) { page.id = id + "_" + page.text; }); }; /** * Update aria labels page collection * * @method updateIds * @param {Array} pages */ var updateAriaLabel = function(pages) { if (!pages) { return; } pages.forEach(function(page) { page.ariaLabel = LOCALE.maketext("Go to page “[_1]”.", page.text); }); }; /** * Update current selected text * * @method updateCurrentSelectedText * @param {string} page - Current page number * @param {string} totalPages - Total pages * @returns {string} Text to display */ var updateCurrentSelectedText = function(page, totalPages) { return LOCALE.maketext("Page [numf,_1] of [numf,_2]", page, totalPages); }; // Use a local template uiPaginationDirective.templateUrl = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH; // Extend the page model with the id field. var linkFn = uiPaginationDirective.link; /** * Compile function for uiPagination Directive * * @method uiPaginationDirective.compile */ uiPaginationDirective.compile = function() { return function(scope, element, attrs, ctrls) { var paginationCtrl = ctrls[0]; linkFn.apply(this, arguments); scope.parentId = attrs.id; scope.ariaLabels = { title: LOCALE.maketext("Pagination"), firstPage: LOCALE.maketext("Go to first page."), previousPage: LOCALE.maketext("Go to previous page."), nextPage: LOCALE.maketext("Go to next page."), lastPage: LOCALE.maketext("Go to last page."), }; scope.updateCurrentSelectedText = updateCurrentSelectedText; var render = paginationCtrl.render; paginationCtrl.render = function() { render.apply(paginationCtrl); updateIds(scope.pages, scope.parentId); updateAriaLabel(scope.pages); }; }; }; return $delegate; }]); }]); return { namespace: MODULE_NAMESPACE, template: TEMPLATE_PATH }; } ); /* # api_tokens/directives/itemLister.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ define( 'app/directives/itemLister',[ "angular", "lodash", "cjt/core", "app/directives/tableShowing", "app/decorators/paginationDecorator", "ngSanitize", "ngRoute", "cjt/modules", "cjt/directives/pageSizeButtonDirective", "cjt/services/cpanel/componentSettingSaverService", "cjt/directives/toggleSortDirective", "cjt/directives/searchDirective", "cjt/directives/pageSizeDirective", "cjt/directives/indeterminateState", "cjt/filters/startFromFilter", "cjt/decorators/paginationDecorator" ], function(angular, _, CJT, TableShowingDirective, PaginationDecorator) { "use strict"; /** * Item Lister combines the typical table functions, pageSize, * showing, paginator, search, and allows you to plug in multiple * views. * * @module item-lister * @memberof cpanel.apiTokens * @restrict EA * * @param {String} id disseminated to other objects * @param {Array} items Items that will be paginated, array of objs * @param {Array} header-items represents the columns of the table * * @example * <item-lister * id="MyItemLister" * items="[a,b,c,d,e]" * create-route="/create" * can-select-all * header-items="[{field:"blah",label:"Blah",sortable:false}]"> * <my-item-lister-view></my-item-lister-view> * </item-lister> * */ var MODULE_REQUIREMENTS = [ TableShowingDirective.namespace, PaginationDecorator.namespace, "ngRoute", "ngSanitize", "cjt2.filters.startFrom" ]; var MODULE_NAMESPACE = "cpanel.apiTokens.itemLister.directive"; var CSSS_COMPONENT_NAME = "apiTokensItemLister"; var CONTROLLER_INJECTABLES = ["$routeParams", "$scope", "$filter", "$log", "$window", "componentSettingSaverService", "ITEM_LISTER_CONSTANTS"]; var CONTROLLER = function itemListerController($routeParams, $scope, $filter, $log, $window, $CSSS, ITEM_LISTER_CONSTANTS) { $scope.viewCallbacks = []; $scope.checkAll = { indeterminate: false, all: false }; var filters = { filter: $filter("filter"), orderBy: $filter("orderBy"), startFrom: $filter("startFrom"), limitTo: $filter("limitTo") }; /** * * Filter items based on filterValue * * @private * * @param {Array} filteredItems items to filter * @returns {Array} filtered items */ $scope._filter = function _filter(filteredItems) { // filter list based on search text if ($scope.filterValue !== "") { return filters.filter(filteredItems, $scope.filterValue, false); } return filteredItems; }; /** * * Sort items based on sort.sortDirection and sort.sortBy * * @private * * @param {Array} filteredItems items to sort * @returns {Array} sorted items */ $scope._sort = function _sort(filteredItems) { // sort the filtered list if ($scope.sort.sortDirection !== "" && $scope.sort.sortBy !== "") { return filters.orderBy(filteredItems, $scope.sort.sortBy, $scope.sort.sortDirection !== "asc"); } return filteredItems; }; /** * * Paginate the items based on pageSize and currentPage * * @private * * @param {Array} filteredItems items to paginate * @returns {Array} paginated items */ $scope._paginate = function _paginate(filteredItems) { // filter list based on page size and pagination if ($scope.totalItems > _.min($scope.pageSizes)) { var start = ($scope.currentPage - 1) * $scope.pageSize; var limit = $scope.pageSize; filteredItems = filters.startFrom(filteredItems, start); filteredItems = filters.limitTo(filteredItems, limit); $scope.showPager = true; // table statistics $scope.start = start + 1; $scope.limit = start + filteredItems.length; } else { // hide pager and pagination $scope.showPager = false; if (filteredItems.length === 0) { $scope.start = 0; } else { // table statistics $scope.start = 1; } $scope.limit = filteredItems.length; } return filteredItems; }; /** * * Update the NVData stored settings for the directive * * @private * * @param {String} lastInteractedItem last item interacted with */ $scope._updatedListerState = function _updatedListerState(lastInteractedItem) { if ($scope.loadingInitialState) { return; } var storedSettings = { pageSize: $scope.pageSize, sort: { sortDirection: $scope.sort.sortDirection, sortBy: $scope.sort.sortBy } }; $CSSS.set(CSSS_COMPONENT_NAME, storedSettings); }; /** * * Event function called on interaction with an item * * @private * * @param {Object} event event object * @param {Object} parameters event parameters {interactionID:...} */ $scope._itemInteracted = function _itemInteracted(event, parameters) { if (parameters.interactionID) { $scope._updatedListerState(parameters.interactionID); } }; /** * Register a callback to call on the update of the lister * * @method registerViewCallback * * @param {Function} callback function to callback to * */ this.registerViewCallback = function registerViewCallback(callback) { $scope.viewCallbacks.push(callback); callback($scope.filteredItems); }; /** * Get the header items * * @method getHeaderItems * * @return {Array} returns array of objects containing labels * */ this.getHeaderItems = function getHeaderItems() { return $scope.headerItems; }; /** * Deregister a callback (useful for view changes) * * @method deregisterViewCallback * * @param {Function} callback callback to deregister * */ this.deregisterViewCallback = function deregisterViewCallback(callback) { for (var i = $scope.viewCallbacks.length - 1; i >= 0; i--) { if ($scope.viewCallbacks[i] === callback) { $scope.viewCallbacks.splice(i, 1); } } }; /** * Function called to rebuild the view from internal components * * @return {Array} filtered items */ $scope.fetch = function fetch() { var filteredItems = []; if ($scope.items) { filteredItems = $scope._filter($scope.items); } // update the total items after search $scope.totalItems = filteredItems.length; filteredItems = $scope._sort(filteredItems); filteredItems = $scope._paginate(filteredItems); $scope.filteredItems = filteredItems; $scope._updatedListerState(); angular.forEach($scope.viewCallbacks, function updateCallback(viewCallback) { viewCallback($scope.filteredItems); }); $scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_UPDATED_EVENT, { meta: { filterValue: $scope.filterValue }, items: filteredItems }); $scope._updateSelectAllState(); return filteredItems; }; /** * Return the focus of the page to the search at the top and scroll to it * */ $scope.focusSearch = function focusSearch() { angular.element(document).find("#" + $scope.parentID + "_search_input").focus(); $window.scrollTop = 0; }; /** * * Event function for a table configuration being clicked * * @param {Object} config which config was clicked */ $scope.tableConfigurationClicked = function tableConfigurationClicked(config) { $scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_BUTTON_EVENT, { actionType: "tableConfigurationClicked", config: config }); }; $scope._updateSelectAllState = function _updateSelectAllState() { $scope.selectedItems = $scope.filteredItems.filter(function filterSelectedItems(item) { return item.selected; }); if ($scope.selectedItems.length && $scope.filteredItems.length && $scope.selectedItems.length === $scope.filteredItems.length) { $scope.checkAll.all = true; } else { $scope.checkAll.all = false; } }; $scope.getIndeterminateState = function getIndeterminateState() { return $scope.selectedItems.length && $scope.filteredItems.length && $scope.filteredItems.length !== $scope.selectedItems.length; }; $scope.toggleSelectAll = function toggleSelectAll() { if ($scope.selectedItems.length < $scope.filteredItems.length) { $scope.filteredItems.forEach(function selectAll(item) { item.selected = true; }); $scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_SELECT_ALL, { items: $scope.filteredItems }); } else { $scope.filteredItems.forEach(function selectAll(item) { item.selected = false; }); $scope.$emit(ITEM_LISTER_CONSTANTS.ITEM_LISTER_DESELECT_ALL, { items: $scope.filteredItems }); } $scope._updateSelectAllState(); }; $scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_BUTTON_EVENT, $scope._itemInteracted); $scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_SELECTED, $scope._updateSelectAllState); $scope.$on(ITEM_LISTER_CONSTANTS.TABLE_ITEM_DESELECTED, $scope._updateSelectAllState); angular.extend($scope, { maxPages: 5, totalItems: $scope.items.length, filteredItems: [], currentPage: 1, pageSize: 20, pageSizes: [20, 100, 500], start: 0, limit: 20, filterValue: "", sort: { sortDirection: "asc", sortBy: $scope.headerItems.length ? $scope.headerItems[0].field : "" } }, { filterValue: $routeParams["q"] }); /** * * Initiate CSSS saved state is loaded * * @private * * @param {Object} initialState saved state for directive */ $scope._savedStateLoaded = function _savedStateLoaded(initialState) { angular.extend($scope, initialState, { filterValue: $routeParams["q"] }); }; var registerSuccess = $CSSS.register(CSSS_COMPONENT_NAME); if ( registerSuccess ) { $scope.loadingInitialState = true; registerSuccess.then($scope._savedStateLoaded, $log.error).finally(function() { $scope.loadingInitialState = false; $scope.fetch(); }); } $scope.$on("$destroy", function() { $CSSS.unregister(CSSS_COMPONENT_NAME); }); $scope.fetch(); $scope.$watch("items", $scope.fetch); }; var TEMPLATE_PATH = "directives/itemLister.ptt"; var RELATIVE_PATH = "api_tokens/" + TEMPLATE_PATH; var TEMPLATE_URL = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH; var DIRECTIVE_INJECTABLES = ["$window", "$log", "componentSettingSaverService"]; var DIRECTIVE_LINK = function(scope, element, attrs) { if (!_.isUndefined(attrs.canSelectAll) && attrs.canSelectAll !== "0") { scope.canSelectAll = true; } scope.controlsBlock = null; scope.contentBlock = null; /** * * Attach controls to the view * * @private * * @param {HTMLElement} elem html element to transclude as controls */ scope._attachControls = function _attachControls(elem) { scope.controlsBlock.append(elem); }; /** * * Attach Other items to view * * @private * * @param {HTMLElement} elem element to treat as the table body */ scope._attachOthers = function _attachOthers(elem) { elem.setAttribute("id", scope.parentID + "_transcludePoint"); elem.setAttribute("ng-if", "filteredItems.length"); scope.contentBlock.replaceWith(elem); }; /** * * Attach a transclude item * * @private * * @param {HTMLElement} elem html element to determine attachment point for */ scope._attachTransclude = function _attachTransclude(elem) { if (angular.element(elem).hasClass("lister-controls")) { scope._attachControls(elem); } else { scope._attachOthers(elem); } }; /** * * Find transclude items to attach to the view * */ scope._findTranscludes = function _findTranscludes() { // *cackles maniacally* // *does a multi-transclude anyways* scope.controlsBlock = element.find("#" + scope.parentID + "_transcludedControls"); scope.contentBlock = element.find("#" + scope.parentID + "_transcludePoint"); var transcludedBlock = element.find("div.transcluded"); var transcludedItems = transcludedBlock.children(); angular.forEach(transcludedItems, scope._attachTransclude, scope); transcludedBlock.remove(); }; /* There is a dumb race condition here */ /* So we have to delay to get the content transcluded */ setTimeout(scope._findTranscludes, 2); }; var DIRECTIVE = function itemLister($window, $log, $CSSS) { return { templateUrl: TEMPLATE_URL, restrict: "EA", scope: { parentID: "@id", items: "=", headerItems: "=", tableConfigurations: "=", createRoute: "@" }, transclude: true, replace: true, link: DIRECTIVE_LINK, controller: CONTROLLER_INJECTABLES.concat(CONTROLLER) }; }; var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS); module.directive("itemLister", DIRECTIVE_INJECTABLES.concat(DIRECTIVE)); return { "class": CONTROLLER, "namespace": MODULE_NAMESPACE, "link": DIRECTIVE_LINK, "template": TEMPLATE_PATH }; } ); /* # api_tokens/filters/htmlSafeString.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ define( 'app/filters/htmlSafeString',[ "angular", "lodash" ], function(angular, _) { "use strict"; /** * Wrapper for lodash escape * * @module htmlSafeString * @memberof cpanel.apiTokens * * @example * {{ domain.domain | htmlSafeString }} * */ var MODULE_NAMESPACE = "cpanel.apiTokens.htmlSafeString.filter"; var MODULE_REQUIREMENTS = [ ]; var CONTROLLER_INJECTABLES = []; var CONTROLLER = function CopyFieldController() { return _.escape; }; var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS); module.filter("htmlSafeString", CONTROLLER_INJECTABLES.concat(CONTROLLER)); return { "class": CONTROLLER, "namespace": MODULE_NAMESPACE }; } ); /* # api_tokens/directives/itemListerView.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ define( 'app/directives/itemListerView',[ "angular", "lodash", "cjt/core", "cjt/util/locale", "app/filters/htmlSafeString", ], function(angular, _, CJT, LOCALE, HTMLSafeString) { "use strict"; /** * Item Lister View is a view that pairs with the item lister to * display items manage link. It must * be nested within an item lister * * @module item-lister-view * @restrict EA * @memberof cpanel.apiTokens * * @example * <item-lister> * <item-lister-view></item-lister-view> * </item-lister> * */ var MODULE_NAMESPACE = "cpanel.apiTokens.itemListerView.directive"; var MODULE_REQUIREMENTS = [ HTMLSafeString.namespace ]; var TEMPLATE = "directives/itemListerView.ptt"; var RELATIVE_PATH = "api_tokens/" + TEMPLATE; var TEMPLATE_PATH = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE; var CONTROLLER_INJECTABLES = ["$scope", "$location", "ITEM_LISTER_CONSTANTS"]; var CONTROLLER = function ItemListViewController($scope, $location, ITEM_LISTER_CONSTANTS) { $scope.toggleSelection = function toggleSelection(item) { if (item.selected) { $scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_SELECTED, { item: item } ); } else { $scope.$emit(ITEM_LISTER_CONSTANTS.TABLE_ITEM_DESELECTED, { item: item } ); } }; $scope.getCreationLabel = function getCreationLabel(createdOn) { return LOCALE.local_datetime(createdOn, "datetime_format_medium"); }; $scope.getExpirationLabel = function getExpirationLabel(expiresAt) { return expiresAt ? LOCALE.local_datetime(expiresAt, "datetime_format_medium") : ""; }; $scope.getRestrictionLabel = function getRestrictionLabel(isUnrestricted) { return isUnrestricted ? LOCALE.maketext("Unrestricted") : LOCALE.maketext("Limited"); }; $scope.getItems = function getItems() { return $scope.items; }; $scope.manageToken = function manageToken(token) { $location.path("/manage").search("token", token.id); }; }; var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS); module.value("PAGE", PAGE); var DIRECTIVE_LINK = function($scope, $element, $attrs, $ctrl) { $scope.items = []; $scope.headerItems = $ctrl.getHeaderItems(); $scope.updateView = function updateView(viewData) { $scope.items = viewData; }; $ctrl.registerViewCallback($scope.updateView.bind($scope)); $scope.$on("$destroy", function() { $ctrl.deregisterViewCallback($scope.updateView); }); }; module.directive("itemListerView", function itemListerItem() { return { templateUrl: TEMPLATE_PATH, restrict: "EA", replace: true, require: "^itemLister", link: DIRECTIVE_LINK, controller: CONTROLLER_INJECTABLES.concat(CONTROLLER) }; }); return { "class": CONTROLLER, "namespace": MODULE_NAMESPACE, "link": DIRECTIVE_LINK, "template": TEMPLATE }; } ); /* # cpanel - api_tokens/validators/uniqueTokenName.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define: false */ /** @namespace cpanel.domains.validators.tokenNameIsUnique */ define( 'app/validators/uniqueTokenName',[ "angular", "cjt/util/locale", "cjt/validator/validator-utils", "app/services/apiTokens", "cjt/validator/validateDirectiveFactory" ], function(angular, LOCALE, validationUtils, APITokensService) { "use strict"; var _apiTokens; var factoryMethods = { /** For mocking */ _lastFetch: 0, _processLoadedTokens: function(tokens) { _apiTokens = null; if (tokens) { _apiTokens = {}; tokens.forEach(function(token) { _apiTokens[token.id] = token; }); } return _apiTokens; }, _fetchTokens: function() { return _apiTokens; }, }; /** * Validator to check that the token name is unique compared to the apiTokens.fetchTokens() tokens * * @module tokenIsUniqueValidator * * @example * <input token-name-is-unique ng-model="myModel" /> * */ var tokenIsUniqueValidator = { /** * Check if the domain is unique * * @method tokenNameIsUnique * * @param {String} tokenName value to check against the validator * * @return {Boolean} returns a boolean value determined by the validity of the view * */ tokenNameIsUnique: function(tokenName, validatorArgument) { var result = validationUtils.initializeValidationResult(); var _apiTokens = factoryMethods._fetchTokens(); // Allows passing in of base value for instances where it would be valid if it stayed the same if (validatorArgument && tokenName === validatorArgument) { return result; } if (!_apiTokens || (tokenName && _apiTokens[tokenName])) { result.isValid = false; result.add("tokenNameIsUnique", LOCALE.maketext("This [asis,API] token name already exists on this account. Enter a different name."), "tokenNameIsUnique"); } return result; }, validate: function($service, $q, value, argument, scope) { // debounce the reload // If tokens exist, and it's not been more than 1000ms, don't re-fetch var curDate = new Date().getTime(); if (factoryMethods._fetchTokens() && curDate - factoryMethods._lastFetch < 1000) { var result = tokenIsUniqueValidator.tokenNameIsUnique(value, argument, scope); return $q.resolve(result); } // It's been more thatn 1000ms or no tokens exist factoryMethods._lastFetch = curDate; return $service.fetchTokens().then(function(tokens) { factoryMethods._processLoadedTokens(tokens); var result = tokenIsUniqueValidator.tokenNameIsUnique(value, argument, scope); return $q.resolve(result); }); } }; var validatorModule = angular.module("cjt2.validate"); validatorModule.run(["validatorFactory", "$q", APITokensService.serviceName, function(validatorFactory, $q, $service) { var validators = { tokenNameIsUnique: tokenIsUniqueValidator.validate.bind(tokenIsUniqueValidator, $service, $q) }; validators.tokenNameIsUnique.async = true; validatorFactory.generate(validators, $q); } ]); return { methods: tokenIsUniqueValidator, factoryMethods: factoryMethods, name: "token-name-is-unique", description: "Validation to ensure the api Token is unique for this account.", version: 1.0 }; } ); /* # api_tokens/views/create.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define */ define( 'app/views/create',[ "angular", "lodash", "cjt/util/locale", "app/services/apiTokens", "cjt/directives/copyField", "cjt/directives/timePicker", "cjt/directives/datePicker", "app/validators/uniqueTokenName", "cjt/modules", "cjt/directives/actionButtonDirective", "cjt/services/cpanel/componentSettingSaverService", "cjt/directives/validationContainerDirective", "cjt/directives/validationItemDirective", "cjt/directives/toggleSwitchDirective", "cjt/directives/searchDirective", "cjt/directives/indeterminateState", "cjt/services/alertService", ], function(angular, _, LOCALE, APITokensService, CopyFieldDirective, TimePickerDirective, DatePickerDirective) { "use strict"; var CSSS_COMPONENT_NAME = "createAPITokenView"; var VIEW_TITLE = LOCALE.maketext("Create API Token"); var MODULE_NAMESPACE = "cpanel.apiTokens.views.create"; var TEMPLATE_URL = "views/create.ptt"; var MODULE_DEPENDANCIES = [ "cjt2.directives.validationContainer", "cjt2.directives.validationItem", "cjt2.directives.toggleSwitch", "cjt2.directives.search", "cjt2.directives.indeterminateState", TimePickerDirective.namespace, DatePickerDirective.namespace, CopyFieldDirective.namespace, ]; var CONTROLLER_INJECTABLES = ["$scope", "$location", "alertService", APITokensService.serviceName, "componentSettingSaverService", "CAN_CREATE_LIMITED", "apiTokens", "features"]; var CONTROLLER_NAME = "CreateTokenController"; var CONTROLLER = function APITokensListController($scope, $location, $alertService, $service, $CSSS, CAN_CREATE_LIMITED, apiTokens, features) { // For contingency of shipping without limited $scope.canCreateLimited = CAN_CREATE_LIMITED; $scope.pageTitle = VIEW_TITLE; $scope.RTL = LOCALE.is_rtl(); $scope.showAllHelp = false; $scope.selectedFeatures = []; $scope.ui = { stayAfterCopy: false }; $scope.checkAll = { all: false }; $scope.features = features; $scope.apiTokens = apiTokens; $scope.working = {}; $scope.datePickerOptions = {}; $scope.timePickerOptions = {}; var minDate = new Date(); minDate.setHours(0); minDate.setMinutes(0); minDate.setSeconds(0, 0); var defaultExpiresDate = new Date(minDate.getTime()); defaultExpiresDate.setHours(23); defaultExpiresDate.setMinutes(59); defaultExpiresDate.setSeconds(59, 999); defaultExpiresDate.setFullYear(defaultExpiresDate.getFullYear() + 1); /** * Iniitate the view * */ $scope.init = function init() { $CSSS.register(CSSS_COMPONENT_NAME).then(function CSSSLoaded(data) { if (data) { $scope.showAllHelp = data.showAllHelp; $scope.ui.stayAfterCopy = data.stayAfterCopy; } }); $scope.reset(); }; /** * Reset the form values * * @param {HTMLDomElement} form form to set pristine */ $scope.reset = function reset(form) { $scope.datePickerOptions.minDate = minDate; $scope.timePickerOptions.min = minDate; $scope.working = { name: "", unrestricted: CAN_CREATE_LIMITED ? false : true, features: {}, tokenExpires: false, expiresAt: defaultExpiresDate }; $scope.generatedToken = null; $scope.pageTitle = VIEW_TITLE; $scope.selectedFeatures = []; if (form) { form.$setPristine(); } }; /** * Update the nvdata saved * * @private * */ $scope._updateCSSS = function _updateCSSS() { $CSSS.set(CSSS_COMPONENT_NAME, { showAllHelp: $scope.showAllHelp, stayAfterCopy: $scope.ui.stayAfterCopy }); }; /** * Toggle Showing or Hiding All help * */ $scope.toggleHelp = function toggleHelp() { $scope.showAllHelp = !$scope.showAllHelp; $scope._updateCSSS(); }; /** * Set the generatedToken and prepare the display for showing it. * * @param {String} newToken */ $scope._tokenCreated = function _tokenCreated(newToken) { $scope.generatedToken = newToken; $scope.pageTitle = LOCALE.maketext("Token Created Successfully"); $scope.apiTokens = $service.getTokens(); var message; if ($scope.working.unrestricted) { message = LOCALE.maketext("You successfully created an [output,strong,unrestricted] [asis,API] token “[_1]”.", $scope.working.name); } else { message = LOCALE.maketext("You successfully created a [output,strong,limited-access] [asis,API] token “[_1]”.", $scope.working.name); } $alertService.success(message); return newToken; }; /** * Create a new token * * @param {Object} workingToken * @returns {Promise<String>} returns the promise and then the newly created token */ $scope.create = function create(workingToken) { if ( workingToken.tokenExpires ) { workingToken.expiresAt.setHours(23); workingToken.expiresAt.setMinutes(59); workingToken.expiresAt.setSeconds(59, 999); } var expiresAt = workingToken.tokenExpires ? Math.floor(workingToken.expiresAt / 1000) : null; return $service.createToken(workingToken.name, workingToken.unrestricted, $scope.selectedFeatures, expiresAt).then($scope._tokenCreated); }; $scope.newTokenExpiresMessage = function newTokenExpiresMessage(token) { var expirationDate = LOCALE.datetime(token.expiresAt, "datetime_format_medium"); return LOCALE.maketext("This [asis,API] token will expire on [_1][comment,Bareword is a date].", expirationDate); }; /** * Toggle (de)selecting all features in the feature chooser * */ $scope.toggleSelectAllFeatures = function toggleSelectAllFeatures() { if ($scope.selectedFeatures.length < $scope.features.length) { $scope.features.forEach(function selectAll(feature) { $scope.working.features[feature.id] = true; }); } else { $scope.features.forEach(function selectAll(feature) { $scope.working.features[feature.id] = false; }); } $scope.updateSelectedFeatures(); }; /** * Determine if a partial number of items is selected * * @returns {Booolean} indeterminate state */ $scope.getFeaturesIndeterminateState = function getFeaturesIndeterminateState() { return $scope.selectedFeatures.length && $scope.features.length && $scope.features.length !== $scope.selectedFeatures.length; }; /** * Update the selected features list * */ $scope.updateSelectedFeatures = function updateSelectedFeatures() { $scope.selectedFeatures = []; angular.forEach($scope.working.features, function(featureSelected, featureKey) { if (featureSelected) { $scope.selectedFeatures.push(featureKey); } }); }; /** * Return to the lister view * */ $scope.backToListView = function backToListView() { $location.path("/"); }; /** * Upon completion of a token copy, determine whether to return to the list * * @param {HTMLFormElement} form passed to reset for resetting to pristine */ $scope.tokenCopied = function tokenCopied(form) { if ($scope.ui.stayAfterCopy) { $scope.reset(form); } else { $scope.backToListView(); } }; /** * Called when the stayAfterCopy is altered * */ $scope.stayAfterCopyChanged = function stayAfterCopyChanged() { $scope._updateCSSS(); }; $scope.dateValidator = function dateValidator(input) { if ($scope.working.tokenExpires && $scope.working.expiresAt) { $scope.working.expiresAt.setHours(23); $scope.working.expiresAt.setMinutes(59); $scope.working.expiresAt.setSeconds(59, 999); } if ($scope.working.tokenExpires && $scope.datePickerOptions.minDate > $scope.working.expiresAt) { input.$invalid = true; input.$valid = false; } }; $scope.resetDate = function resetDate() { if ($scope.working.tokenExpires) { $scope.working.expiresAt = defaultExpiresDate; } }; $scope.$on("$destroy", $CSSS.unregister.bind($CSSS, CSSS_COMPONENT_NAME)); $scope.init(); }; var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES); app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER)); var resolver = { "apiTokens": [ APITokensService.serviceName, function($service) { return $service.fetchTokens(); }], "features": [ APITokensService.serviceName, function($service) { return $service.getFeatures(); }] }; return { "id": "createAPIToken", "route": "/create", "controller": CONTROLLER_NAME, "class": CONTROLLER, "templateUrl": TEMPLATE_URL, "title": VIEW_TITLE, "namespace": MODULE_NAMESPACE, "showResourcePanel": true, "resolve": resolver }; } ); /* # api_tokens/views/list.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define */ define( 'app/views/list',[ "angular", "lodash", "cjt/util/locale", "app/directives/itemLister", "app/directives/itemListerView", "app/services/apiTokens", "app/views/create", "cjt/modules", "cjt/services/alertService", "cjt/directives/actionButtonDirective", ], function(angular, _, LOCALE, ItemLister, ItemListerView, APITokensService, CreateView) { "use strict"; var VIEW_TITLE = LOCALE.maketext("List API Tokens"); var MODULE_NAMESPACE = "cpanel.apiTokens.views.list"; var TEMPLATE_URL = "views/list.ptt"; var MODULE_DEPENDANCIES = [ ItemLister.namespace, ItemListerView.namespace, APITokensService.namespace ]; var CONTROLLER_INJECTABLES = ["$scope", "$location", APITokensService.serviceName, "alertService", "apiTokens", "ITEM_LISTER_CONSTANTS"]; var CONTROLLER_NAME = "ListController"; var CONTROLLER = function APITokensListController($scope, $location, $service, $alertService, apiTokens, ITEM_LISTER_CONSTANTS) { /** * * Initialize the controller, called internally. * * @private * */ $scope.init = function init() { $scope._apiTokens = apiTokens; $scope._filteredItems = []; $scope.selectedItems = []; $scope.confirmingDelete = false; $scope.deletingTokens = false; $scope.tableHeaderItems = [ { field: "label", label: LOCALE.maketext("Token Name"), sortable: true }, { field: "createdOn", label: LOCALE.maketext("Created"), hiddenOnMobile: true, sortable: true }, { field: "expiresAt", label: LOCALE.maketext("Expires"), hiddenOnMobile: true, sortable: true }, { field: "actions", label: "", sortable: false } ]; ["TABLE_ITEM_SELECTED", "TABLE_ITEM_DESELECTED", "ITEM_LISTER_SELECT_ALL", "ITEM_LISTER_DESELECT_ALL"].forEach(function(updateEventKey) { $scope.$on(ITEM_LISTER_CONSTANTS[updateEventKey], $scope._updatedSelected); }); $scope.$on(ITEM_LISTER_CONSTANTS.ITEM_LISTER_UPDATED_EVENT, function(event, parameters) { $scope._filteredItems = parameters.items; $scope._updatedSelected(); }); }; /** * * On success of deleted tokens, this function notifies the user. * * @private * * @param {Array<String>} deletedTokenNames tokens that were deleted * @param {} updatedApiTokens */ $scope._deleteTokenSuccess = function _deleteTokenSuccess(deletedTokenNames, updatedApiTokens) { $scope._apiTokens = updatedApiTokens; $alertService.success(LOCALE.maketext("The system successfully revoked the following [asis,API] [numerate,_1,token,tokens]: [list_and_quoted,_1]", deletedTokenNames)); if ($scope._apiTokens.length === 0) { $location.path(CreateView.route); } $scope.confirmingDelete = false; $scope.deletingTokens = false; }; /** * * On update of the selected items, as emitted from the lister, this updates the local array * */ $scope._updatedSelected = function _updatedSelected() { $scope.selectedItems = $scope._filteredItems.filter(function(item) { return item.selected; }); }; /** * * Delete tokens * * @param {*} items token items to be deleted * @returns {Promise<Array>} returns the deletion promise and updated api tokens */ $scope.deleteTokens = function deleteAPITokens(items) { var tokenNames = []; var tokenHashes = []; items.forEach(function(item) { tokenHashes.push(item.id); tokenNames.push(_.escape(item.label)); }); $scope.deletingTokens = true; return $service.deleteTokens(tokenHashes).then($scope._deleteTokenSuccess.bind($scope, tokenNames)); }; /** * Show the deletion confirmation message * */ $scope.showDeletionConfirmationMessage = function showDeletionConfirmationMessage() { $scope.confirmingDelete = true; }; /** * Hide the deletion confirmation message * */ $scope.hideDeletionConfirmationMessage = function hideDeletionConfirmationMessage() { $scope.confirmingDelete = false; }; /** * Generate a Label for the Delete Confirmation Button * * @returns {String} Delete Confirmation Button Label */ $scope.confirmDeleteButtonLabel = function confirmDeleteButtonLabel() { return LOCALE.maketext("Revoke Selected [asis,API] [numerate,_1,Token,Tokens]", $scope.selectedItems.length); }; /** * Get the current api tokens * * @returns {Array} current api tokens */ $scope.getItems = function getItems() { return $scope._apiTokens; }; /** * Return just the ids of the selected features * * @return {Array<String>} array of feature names * */ $scope.getSelectedFeatureNames = function getSelectedFeatureNames() { return $scope.selectedItems.map(function(selectedFeature) { return selectedFeature.id; }); }; /** * Get the delete confirmation message based on selected features. * * @returns {String} delete confirmation message */ $scope.confirmDeleteMessage = function confirmDeleteMessage() { var selectedFeatureNames = $scope.getSelectedFeatureNames().map(_.escape); return LOCALE.maketext("Are you sure that you want to revoke the following [asis,API] [numerate,_1,token,tokens]: [list_and_quoted,_2]", selectedFeatureNames.length, selectedFeatureNames); }; $scope.init(); }; var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES); app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER)); var resolver = { "apiTokens": [ APITokensService.serviceName, "$location", function($service, $location) { return $service.fetchTokens().then(function(apiTokens) { if (apiTokens.length) { return apiTokens; } else { $location.path(CreateView.route); return false; } }); }] }; return { "id": "listAPITokens", "route": "/", "controller": CONTROLLER_NAME, "class": CONTROLLER, "templateUrl": TEMPLATE_URL, "title": VIEW_TITLE, "namespace": MODULE_NAMESPACE, "showResourcePanel": false, "resolve": resolver }; } ); /* # api_tokens/views/manage.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* global define */ define( 'app/views/manage',[ "angular", "lodash", "cjt/util/locale", "app/services/apiTokens", "app/filters/htmlSafeString", "app/validators/uniqueTokenName", "cjt/modules", "cjt/services/alertService", "cjt/directives/actionButtonDirective", "cjt/services/cpanel/componentSettingSaverService", "cjt/directives/validationContainerDirective", "cjt/directives/validationItemDirective", "cjt/directives/toggleSwitchDirective", "cjt/directives/searchDirective", "cjt/directives/indeterminateState" ], function(angular, _, LOCALE, APITokensService, HTMLSafeStringFilter) { "use strict"; var CSSS_COMPONENT_NAME = "manageAPITokenView"; var VIEW_TITLE = LOCALE.maketext("Manage [asis,API] Token"); var MODULE_NAMESPACE = "cpanel.apiTokens.views.manage"; var TEMPLATE_URL = "views/manage.ptt"; var MODULE_DEPENDANCIES = [ "cjt2.directives.validationContainer", "cjt2.directives.validationItem", "cjt2.directives.toggleSwitch", "cjt2.directives.search", "cjt2.directives.indeterminateState", HTMLSafeStringFilter.namespace ]; var CONTROLLER_INJECTABLES = ["$scope", "$location", "$routeParams", "alertService", APITokensService.serviceName, "componentSettingSaverService", "CAN_CREATE_LIMITED", "apiTokens"]; var CONTROLLER_NAME = "ManageTokenController"; var CONTROLLER = function APITokensListController($scope, $location, $routeParams, $alertService, $service, $CSSS, CAN_CREATE_LIMITED, apiTokens) { $scope.pageTitle = VIEW_TITLE; $scope.RTL = LOCALE.is_rtl(); $scope.showAllHelp = false; $scope.ui = { confirmingRevocation: false }; $scope.selectedFeatures = []; $scope.checkAll = { all: false }; $scope.apiTokens = apiTokens; $scope.working = {}; $scope.expiringMessage = function expiringMessage(expiresAt) { if ($scope.current.expired) { return LOCALE.maketext("[output,strong,Danger]: This [asis,API] token expired on [datetime,_1,datetime_format_medium].", $scope.current.expiresAt); } else if ($scope.current.expiresSoon) { return LOCALE.maketext("[output,strong,Warning]: This [asis,API] token will expire on [datetime,_1,datetime_format_medium].", $scope.current.expiresAt); } }; $scope.getExpirationLabel = function getExpirationLabel(expiresAt) { return expiresAt ? LOCALE.local_datetime(expiresAt, "datetime_format_medium") : ""; }; /** * Initate the view * */ $scope.init = function init() { $CSSS.register(CSSS_COMPONENT_NAME).then(function CSSSLoaded(data) { if (data) { $scope.showAllHelp = data.showAllHelp; $scope.stayOnView = data.stayOnView; } }); var currentTokenID = $routeParams["token"]; var currentToken = $service.getTokenById(currentTokenID); if (!currentToken) { $location.path("/"); return; } // For contingency of shipping without limited $scope.canEditFeatureRestrictions = !currentToken.unrestricted || CAN_CREATE_LIMITED; $scope.current = currentToken; $scope.working = { name: currentToken.id, unrestricted: currentToken.unrestricted, features: {} }; currentToken.features.forEach(function(feature) { $scope.working.features[feature] = true; $scope.selectedFeatures.push(feature); }); if (!$scope.current.unrestricted) { $service.getFeatures().then($scope._featuresLoaded); } }; /** * Update the nvdata saved * * @private * */ $scope._updateCSSS = function _updateCSSS() { $CSSS.set(CSSS_COMPONENT_NAME, { showAllHelp: $scope.showAllHelp }); }; /** * Set the scope features to the passed features * * @param {Array<Object>} features */ $scope._featuresLoaded = function _featuresLoaded(features) { $scope.features = features; }; /** * Toggle Showing or Hiding All help * */ $scope.toggleHelp = function toggleHelp() { $scope.showAllHelp = !$scope.showAllHelp; $scope._updateCSSS(); }; $scope._tokenRenamed = function _tokenRenamed(originalName, newName) { $alertService.success(LOCALE.maketext("You have successfully renamed the API Token “[_1]” to “[_2]”.", _.escape(originalName), _.escape(newName))); }; $scope._renameToken = function _renameToken(originalName, newName) { return $service.renameToken(originalName, newName).then($scope._tokenRenamed.bind($scope, originalName, newName)); }; $scope._getFeatureById = function _getFeatureById(id) { var features = $scope.features; for (var i = 0; i < features.length; i++) { if (features[i].id === id) { return features[i]; } } return; }; $scope._tokenRestrictionUpdated = function _tokenRestrictionUpdated(tokenName, unrestricted, features) { if (unrestricted) { $alertService.success({ message: LOCALE.maketext("You have successfully set the [asis,API] token “[_1]” to unrestricted.", _.escape(tokenName)), replace: false }); } else { var featureNames = features.map(function(featureId) { return $scope._getFeatureById(featureId).label; }).map(_.escape); $alertService.success({ message: LOCALE.maketext("The [asis,API] Token “[_1]” is now limited to the following features: [list_and_quoted,_2].", tokenName, featureNames), replace: false }); } }; /** * Update the token * * @param {Object} workingToken * @returns {Promise<String>} returns the promise chain */ $scope.update = function update(workingToken) { var originalToken = $scope.current; var updatePromise; var updatingRestrictions = workingToken.unrestricted !== $scope.current.unrestricted || $scope.selectedFeatures.sort().join() !== $scope.current.features.sort().join(); if (originalToken.id !== workingToken.name) { // Rename it updatePromise = $scope._renameToken(originalToken.id, workingToken.name); // If we're not also updating restrictions, add the finally. if (!updatingRestrictions) { updatePromise.finally($scope.backToListView); } } if (updatingRestrictions) { var updateRestriction = $service.updateTokenRestrictions.bind($service, workingToken.name, workingToken.unrestricted, $scope.selectedFeatures); var _restrictionUpdated = $scope._tokenRestrictionUpdated.bind($scope, workingToken.name, workingToken.unrestricted, $scope.selectedFeatures); if (updatePromise) { updatePromise.then(updateRestriction).then(_restrictionUpdated); } else { updatePromise = updateRestriction().then(_restrictionUpdated); } updatePromise.finally($scope.backToListView); } return updatePromise; }; /** * Hide or Reveal the Feature Chooser * */ $scope.unrestrictedToggled = function unrestrictedToggled() { if (!$scope.working.unrestricted && !$scope.features) { // load the features $service.getFeatures().then($scope._featuresLoaded); } }; /** * Toggle (de)selecting all features in the feature chooser * */ $scope.toggleSelectAllFeatures = function toggleSelectAllFeatures() { $scope.working.features = $scope.working.features || {}; if ($scope.selectedFeatures.length < $scope.features.length) { $scope.features.forEach(function selectAll(feature) { $scope.working.features[feature.id] = true; }); } else { $scope.features.forEach(function selectAll(feature) { $scope.working.features[feature.id] = false; }); } $scope.updateSelectedFeatures(); }; /** * Determine if a partial number of items is selected * * @returns {Booolean} indeterminate state */ $scope.getFeaturesIndeterminateState = function getFeaturesIndeterminateState() { return $scope.selectedFeatures.length && $scope.features.length && $scope.features.length !== $scope.selectedFeatures.length; }; /** * Update the selected features list * */ $scope.updateSelectedFeatures = function updateSelectedFeatures() { $scope.selectedFeatures = []; angular.forEach($scope.working.features, function(featureSelected, featureKey) { if (featureSelected) { $scope.selectedFeatures.push(featureKey); } }); }; /** * Return to the lister view * */ $scope.backToListView = function backToListView() { $location.path("/"); }; /** * Show the revocation confirmation dialog * */ $scope.showRevokeConfirm = function showRevokeConfirm() { $scope.ui.confirmingRevocation = true; }; /** * Hide the revocation confirmation dialog * */ $scope.hideRevokeConfirm = function hideRevokeConfirm() { $scope.ui.confirmingRevocation = false; }; /** * Notify user of the success of a token revocation * * @param {String} token */ $scope._tokenRevoked = function _tokenRevoked(token) { $alertService.success( LOCALE.maketext("You have successfully revoked the following [asis,API] [numerate,_1,token,tokens]: “[_1]”", _.escape(token)) ); }; /** * Revoke a token * * @param {APIToken} token * @returns {Promise} revocation promise */ $scope.revokeToken = function revokeToken(token) { return $service.deleteTokens([token.id]).then( $scope._tokenRevoked.bind($scope, token.id) ).then( $scope.backToListView ); }; $scope.$on("$destroy", $CSSS.unregister.bind($CSSS, CSSS_COMPONENT_NAME)); $scope.init(); }; var app = angular.module(MODULE_NAMESPACE, MODULE_DEPENDANCIES); app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES.concat(CONTROLLER)); var resolver = { "apiTokens": [ APITokensService.serviceName, function($service) { return $service.fetchTokens(); }] }; return { "id": "manageAPIToken", "route": "/manage", "controller": CONTROLLER_NAME, "class": CONTROLLER, "templateUrl": TEMPLATE_URL, "title": VIEW_TITLE, "namespace": MODULE_NAMESPACE, "showResourcePanel": true, "resolve": resolver }; } ); // Copyright 2025 WebPros International, LLC // All rights reserved. // copyright@cpanel.net http://cpanel.net // This code is subject to the cPanel license. Unauthorized copying is prohibited. /* global require, define */ define( 'app/index',[ "angular", "cjt/core", "cjt/util/locale", "app/services/apiTokens", "app/views/list", "app/views/create", "app/views/manage", "app/filters/htmlSafeString", "cjt/modules", "cjt/directives/alertList", "cjt/services/alertService", "cjt/services/APICatcher", "cjt/directives/loadingPanel", "cjt/directives/breadcrumbs", "ngRoute", ], function(angular, CJT, LOCALE, ApiTokensService, ListView, CreateView, ManageView, HTMLSafeStringFilter) { "use strict"; var MODULE_NAME = "cpanel.apiTokens"; return function() { var ROUTES = []; var MODULE_INJECTABLES = [ "ngRoute", "cjt2." + CJT.applicationName, ApiTokensService.namespace, HTMLSafeStringFilter.namespace, ]; [ListView, CreateView, ManageView].forEach(function(routeView) { routeView.breadcrumb = routeView.breadcrumb ? routeView.breadcrumb : { id: routeView.id, name: routeView.title, path: routeView.route, parentID: routeView.parentID, }; ROUTES.push(routeView); MODULE_INJECTABLES.push(routeView.namespace); }); // First create the application var appModule = angular.module(MODULE_NAME, MODULE_INJECTABLES); appModule.value("ITEM_LISTER_CONSTANTS", { TABLE_ITEM_BUTTON_EVENT: "TableItemActionButtonEmitted", TABLE_ITEM_SELECTED: "TableItemSelectedEmitted", TABLE_ITEM_DESELECTED: "TableItemDeselectedEmitted", ITEM_LISTER_UPDATED_EVENT: "ItemListerUpdatedEvent", ITEM_LISTER_SELECT_ALL: "ItemListerSelectAllEvent", ITEM_LISTER_DESELECT_ALL: "ItemListerDeselectAllEvent", }); appModule.value("CAN_CREATE_LIMITED", PAGE.canCreateLimited); appModule.controller("MainController", ["$scope", "$rootScope", "alertService", function MainController($scope, $rootScope, $alertService) { $scope.showResourcePanel = true; $scope.mainPanelClasses = ""; $scope.sidePanelClasses = ""; $scope.updatePanelClasses = function updatePanelClasses() { $scope.sidePanelClasses = "col-sm-4 col-md-4 hidden-xs"; $scope.sidePanelClasses += " "; $scope.sidePanelClasses += LOCALE.is_rtl() ? "pull-left" : "pull-right"; $scope.mainPanelClasses = $scope.showResourcePanel ? "col-xs-12 col-sm-8 col-md-8" : "col-xs-12"; }; /** * Find a Route by the Path * * @private * * @method _getRouteByPath * @param {String} path route to match against the .route property of the existing routes * * @returns {Object} route that matches the provided path * */ function _getRouteByPath(path) { var foundRoute; ROUTES.forEach(function(route, key) { if (route.route === path) { foundRoute = key; } }); return foundRoute; } $rootScope.$on("$routeChangeStart", function() { $scope.loading = true; $alertService.clear("danger"); }); $rootScope.$on("$routeChangeSuccess", function(event, current) { $scope.loading = false; if (current) { var currentRouteKey = _getRouteByPath(current.$$route.originalPath); if (ROUTES[currentRouteKey]) { $scope.currentTab = ROUTES[currentRouteKey]; $scope.showResourcePanel = $scope.currentTab.showResourcePanel; $scope.activeTab = currentRouteKey; $scope.updatePanelClasses(); } } }); $rootScope.$on("$routeChangeError", function() { $scope.loading = false; }); }]); // Then load the application dependencies require(["cjt/bootstrap"], function(BOOTSTRAP) { appModule.config([ "$routeProvider", "$animateProvider", function($routeProvider, $animateProvider) { $animateProvider.classNameFilter(/^((?!no-animate).)*$/); ROUTES.forEach(function(route, key) { $routeProvider.when(route.route, route); }); $routeProvider.otherwise({ "redirectTo": "/", }); }, ]); BOOTSTRAP("#content", MODULE_NAME); }); return appModule; }; } );
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings