1/** 2 * http://www.privacyidea.org 3 * (c) cornelius kölbel, [email protected] 4 * 5 * 2015-01-11 Cornelius Kölbel, <[email protected]> 6 * 7 * This code is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE 9 * License as published by the Free Software Foundation; either 10 * version 3 of the License, or any later version. 11 * 12 * This code is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU AFFERO GENERAL PUBLIC LICENSE for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public 18 * License along with this program. If not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22myApp.controller("tokenMenuController", ['$scope', '$location', '$rootScope', 'AuthFactory', 'ConfigFactory', 23 function ($scope, $location, $rootScope, AuthFactory, ConfigFactory) { 24 $scope.loggedInUser = AuthFactory.getUser(); 25 26 // set default path 27 if ($location.path() === "/token") { 28 $location.path("/token/list"); 29 $scope.tokenMenu = true; 30 } 31 32 // watch the location to change the side menu from token to container 33 $rootScope.$on('$locationChangeSuccess', function () { 34 if ($location.path().includes("container")) { 35 $scope.tokenMenu = false;
36 } else { 37 $scope.tokenMenu = true; 38 } 39 }) 40 }]); 41 42myApp.controller("tokenController", ['TokenFactory', 'ConfigFactory', '$scope', 43 '$location', 'AuthFactory', 'instanceUrl', '$rootScope', 44 function (TokenFactory, ConfigFactory, $scope, $location, AuthFactory, instanceUrl, $rootScope) { 45 $scope.tokensPerPage = $scope.token_page_size; 46 $scope.params = {page: 1, sortdir: "asc"}; 47 $scope.reverse = false; 48 $scope.loggedInUser = AuthFactory.getUser(); 49 $scope.selectedToken = {serial: null}; 50 $scope.clientpart = ""; 51 52 // Change the pagination 53 $scope.pageChanged = function () { 54 //debug: console.log('Page changed to: ' + $scope.params.page); 55 $scope.get(); 56 }; 57 58 // This function fills $scope.tokendata 59 $scope.get = function (live_search) { 60 if ((!$rootScope.search_on_enter) || ($rootScope.search_on_enter && !live_search)) { 61 $scope.params.serial = "*" + ($scope.serialFilter || "") + "*"; 62 $scope.params.tokenrealm = "*" + ($scope.tokenrealmFilter || "") + "*"; 63 $scope.params.type = "*" + ($scope.typeFilter || "") + "*"; 64 $scope.params.description = "*" + ($scope.descriptionFilter || "") + "*"; 65 $scope.params.rollout_state = "*" + ($scope.rolloutStateFilter || "") + "*"; 66 $scope.params.userid = "*" + ($scope.userIdFilter || "") + "*"; 67 $scope.params.resolver = "*" + ($scope.resolverFilter || "") + "*"; 68 $scope.params.pagesize = $scope.token_page_size; 69 $scope.params.sortby = $scope.sortby; 70 if ($scope.reverse) { 71 $scope.params.sortdir = "desc"; 72 } else { 73 $scope.params.sortdir = "asc"; 74 } 75 TokenFactory.getTokens(function (data) { 76 if (data) { 77 $scope.tokendata = data.result.value; 78 } 79 }, $scope.params); 80 } 81 }; 82 83 // single token function 84 $scope.reset = function (serial) { 85 TokenFactory.reset(serial, $scope.get); 86 }; 87 $scope.disable = function (serial) { 88 TokenFactory.disable(serial, $scope.get); 89 }; 90 $scope.enable = function (serial) { 91 TokenFactory.enable(serial, $scope.get); 92 }; 93 94 // go to the list view by default 95 if ($location.path() === "/token") { 96 $location.path("/token/list"); 97 } 98 if ($location.path() === "/token/list") { 99 $scope.get(); 100 } 101 // go to token.wizard, if the wizard is defined 102 if ($scope.token_wizard) { 103 $location.path("/token/wizard"); 104 } 105 106 // go to change PIN, if we should change the PIN 107 if ($scope.pin_change) { 108 $location.path("/pinchange"); 109 } 110 111 // listen to the reload broadcast 112 $scope.$on("piReload", function () { 113 /* Due to the parameter "live_search" in the get function 114 we can not bind the get-function to piReload below. This 115 will break in Chrome, would work in Firefox. 116 So we need this wrapper function 117 */ 118 $scope.get(); 119 }); 120 121 }]); 122 123 124myApp.controller("tokenAssignController", ['$scope', 'TokenFactory', 125 '$stateParams', 'AuthFactory', 'UserFactory', '$state', 126 function tokenAssignController($scope, TokenFactory, $stateParams, AuthFactory, UserFactory, $state) { 127 $scope.assignToken = function () { 128 TokenFactory.assign({ 129 serial: fixSerial($scope.newToken.serial), 130 pin: $scope.newToken.pin 131 }, function () { 132 $state.go('token.list'); 133 }); 134 }; 135 }]); 136 137myApp.controller("tokenEnrollController", ["$scope", "TokenFactory", "$timeout", "$stateParams", "AuthFactory", 138 "UserFactory", "$state", "ConfigFactory", "instanceUrl", "$http", "hotkeys", "gettextCatalog", "inform", 139 "U2fFactory", "webAuthnToken", "versioningSuffixProvider", "$location", 140 function tokenEnrollController($scope, TokenFactory, $timeout, $stateParams, AuthFactory, UserFactory, $state, 141 ConfigFactory, instanceUrl, $http, hotkeys, gettextCatalog, inform, U2fFactory, 142 webAuthnToken, versioningSuffixProvider, $location) { 143 144 hotkeys.bindTo($scope).add({ 145 combo: 'alt+e', 146 description: gettextCatalog.getString('Enroll a new token'), 147 callback: function (event, hotkey) { 148 event.preventDefault(); 149 $state.go('token.enroll'); 150 $scope.enrolledToken = null; 151 } 152 }); 153 hotkeys.bindTo($scope).add({ 154 combo: 'alt+r', 155 description: gettextCatalog.getString('Roll the token'), 156 callback: function () { 157 $scope.enrollToken(); 158 } 159 }); 160 161 $scope.qrCodeWidth = 250; 162 163 // Available SMS gateways. We do this here to avoid javascript loops 164 $scope.smsGateways = $scope.getRightsValue('sms_gateways', '').split(' '); 165 166 if ($state.includes('token.wizard') && !$scope.show_seed) { 167 $scope.qrCodeWidth = 300; 168 } 169 $scope.checkRight = AuthFactory.checkRight; 170 $scope.loggedInUser = AuthFactory.getUser(); 171 $scope.newUser = {}; 172 $scope.tempData = {}; 173 $scope.instanceUrl = instanceUrl; 174 $scope.click_wait = true; 175 $scope.U2FToken = {}; 176 $scope.webAuthnToken = {}; 177 // System default values for enrollment 178 $scope.systemDefault = {}; 179 // questions for questionnaire token 180 $scope.questions = []; 181 $scope.num_questions = 5; 182 $scope.fileVersionSuffix = versioningSuffixProvider.$get(); 183 // These are values that are also sent to the backend! 184 $scope.form = { 185 timeStep: 30, 186 otplen: 6, 187 genkey: true, 188 type: $scope.default_tokentype, 189 hashlib: "sha1",
190 'radius.system_settings': true, 191 container_serial: null, 192 }; 193 if ($state.includes('token.rollover')) { 194 $scope.form.serial = $stateParams.tokenSerial; 195 $scope.form.type = $stateParams.tokenType; 196 $scope.form.container_serial = $stateParams.containerSerial; 197 } 198 $scope.vasco = { 199 // Note: A primitive does not work in the ng-model of the checkbox! 200 useIt: false 201 }; 202 $scope.enrolling = false; 203 $scope.containerSerial = $stateParams.containerSerial; 204 205 $scope.formInit = { 206 tokenTypes: {}, // will be set later with response from server 207 timesteps: [30, 60], 208 otplens: [6, 8], 209 hashlibs: ["sha1", "sha256", "sha512"], 210 service_ids: {} 211 }; 212 213 $scope.loadAvailableServiceIDs = function () { 214 ConfigFactory.getServiceid("", function (data) { 215 let serviceids = data.result.value; 216 angular.forEach(serviceids, function (serviceid_data, name) { 217 $scope.formInit.service_ids[name] = name + ": " + serviceid_data.description; 218 }); 219 }) 220 } 221 222 $scope.setVascoSerial = function () { 223 if ($scope.form.otpkey.length === 496) { 224 //console.log('DEBUG: got 496 hexlify otpkey, check vasco serialnumber!'); 225 226 // convert hexlified input blob to ascii and use the serialnumber (first 10 chars) 227 const vasco_hex = $scope.form.otpkey.toString();//force conversion 228 let vasco_otpstr = ''; 229 for (let i = 0; i < vasco_hex.length; i += 2) 230 vasco_otpstr += String.fromCharCode(parseInt(vasco_hex.substr(i, 2), 16)); 231 const vasco_serial = vasco_otpstr.slice(0, 10); 232 //console.log(vasco_serial); 233 $scope.vascoSerial = vasco_serial; 234 if ($scope.vasco.useIt) { 235 $scope.form.serial = vasco_serial; 236 } else { 237 delete $scope.form.serial; 238 } 239 } else { 240 // If we do not have 496 characters this might be no correct vasco blob. 241 // So we reset the serial 242 $scope.vascoSerial = ""; 243 delete $scope.form.serial; 244 } 245 }; 246 247 // These token need to PIN 248 // TODO: This is also contained in the tokentype class! 249 $scope.changeTokenType = function () { 250 //debug: console.log("Token Type Changed."); 251 $scope.hidePin = ["sshkey", "certificate"].indexOf($scope.form.type) >= 0; 252 if ($scope.form.type === "hotp") { 253 // preset HOTP hashlib 254 $scope.form.hashlib = $scope.systemDefault['hotp.hashlib'] || 'sha1'; 255 } else if ($scope.form.type === "totp") { 256 // preset TOTP hashlib 257 $scope.form.hashlib = $scope.systemDefault['totp.hashlib'] || 'sha1'; 258 $scope.form.timeStep = parseInt($scope.systemDefault['totp.timeStep'] || '30'); 259 } else if ($scope.form.type === "daypassword") { 260 // preset DayPassword hashlib 261 $scope.form.hashlib = $scope.systemDefault['daypassword.hashlib'] || 'sha1'; 262 $scope.form.timeStep = parseInt($scope.systemDefault['daypassword.timeStep'] || '60'); 263 } 264 $scope.form.genkey = $scope.form.type !== "vasco"; 265 if ($scope.form.type === "applspec") { 266 $scope.loadAvailableServiceIDs(); 267 } 268 if ($scope.form.type === "yubikey") { 269 // save the original otp length 270 $scope.old_otplen = $scope.form.otplen; 271 // set the default otp length for yubikeys in AES mode to 44 272 // (12 characters (6 bytes) UID and 32 characters (16 bytes) OTP) 273 $scope.form.otplen = 44; 274 } else { 275 // restore old otp length if available 276 if (typeof $scope.old_otplen != "undefined") { 277 $scope.form.otplen = $scope.old_otplen; 278 delete $scope.old_otplen; 279 } 280 } 281 282 $scope.preset_indexedsecret(); 283 284 if ($scope.form.type === "radius") { 285 // only load RADIUS servers when the user actually tries to enroll a RADIUS token, 286 // because the user might not be allowed to list RADIUS servers 287 $scope.getRADIUSIdentifiers(); 288 } 289 if ($scope.form.type === "remote") { 290 // fetch the privacyIDEA servers 291 $scope.getPrivacyIDEAServers(); 292 } 293 if ($scope.form.type === "certificate") { 294 $scope.getCAConnectors(); 295 } 296 // preset twostep enrollment 297 $scope.setTwostepEnrollmentDefault(); 298 }; 299 300 // helper function for setting indexed secret attribute 301 $scope.preset_indexedsecret = function () { 302 if ($scope.form.type === "indexedsecret") { 303 // in case of indexedsecret we do never generate a key from the UI 304 $scope.form.genkey = false;
305 // Only fetch, if a preset_attribute is defined 306 if ($scope.tokensettings.indexedsecret.preset_attribute) { 307 // In case of a normal logged in user, an empty params is fine 308 let params = {}; 309 if (AuthFactory.getRole() === 'admin') { 310 params = { 311 realm: $scope.newUser.realm, 312 username: fixUser($scope.newUser.user) 313 }; 314 } 315 UserFactory.getUsers(params, 316 function (data) { 317 const userObject = data.result.value[0]; 318 // preset for indexedsecret token 319 $scope.form.otpkey = userObject[$scope.tokensettings.indexedsecret.preset_attribute]; 320 }); 321 } 322 } 323 }; 324 325 // Set the default value of the "2stepinit" field if twostep enrollment should be forced 326 $scope.setTwostepEnrollmentDefault = function () { 327 $scope.form["2stepinit"] = $scope.checkRight($scope.form.type + "_2step=force"); 328 }; 329 330 // Initially set the default value 331 $scope.setTwostepEnrollmentDefault(); 332 333 // A watch function to change the form data in case another user is selected 334 $scope.$watch(function (scope) { 335 return scope.newUser.email; 336 }, 337 function (newValue, oldValue) { 338 if (newValue !== '') { 339 $scope.form.email = newValue; 340 } 341 }); 342 $scope.$watch(function (scope) { 343 return scope.newUser.mobile; 344 }, 345 function (newValue, oldValue) { 346 if (newValue !== '') { 347 $scope.form.phone = newValue; 348 } 349 }); 350 $scope.$watch(function (scope) { 351 return fixUser(scope.newUser.user); 352 }, 353 function (newValue, oldValue) { 354 // The newUser was changed 355 $scope.preset_indexedsecret(); 356 }); 357 358 // Helper function to populate user information 359 $scope.get_user_infos = function (data) { 360 const userObject = data.result.value[0]; 361 $scope.form.email = userObject.email; 362 if (typeof userObject.mobile === "string") { 363 $scope.form.phone = userObject.mobile; 364 } else { 365 $scope.phone_list = userObject.mobile; 366 if ($scope.phone_list && $scope.phone_list.length === 1) { 367 $scope.form.phone = $scope.phone_list[0]; 368 } 369 } 370 return userObject; 371 } 372 373 // Get the realms and fill the realm dropdown box 374 if (AuthFactory.getRole() === 'admin') { 375 ConfigFactory.getRealms(function (data) { 376 $scope.realms = data.result.value; 377 // Set the default realm 378 angular.forEach($scope.realms, function (realm, realmname) { 379 // if there is a default realm, preset the default realm 380 if (realm.default && !$stateParams.realmname) { 381 $scope.newUser = {user: "", realm: realmname}; 382 } 383 }); 384 385 // init the user, if token.enroll was called from the user.details 386 if ($stateParams.realmname) { 387 $scope.newUser.realm = $stateParams.realmname; 388 } 389 if ($stateParams.username) { 390 $scope.newUser.user = $stateParams.username; 391 // preset the mobile and email for SMS or EMAIL token 392 UserFactory.getUsers({ 393 realm: $scope.newUser.realm, 394 username: $scope.newUser.user 395 }, 396 function (data) { 397 $scope.get_user_infos(data) 398 }); 399 } 400 }); 401 } else if (AuthFactory.getRole() === 'user') { 402 // init the user, if token.enroll was called as a normal user 403 $scope.newUser.user = AuthFactory.getUser().username; 404 $scope.newUser.realm = AuthFactory.getUser().realm; 405 if ($scope.checkRight('userlist')) { 406 UserFactory.getUserDetails({}, function (data) { 407 $scope.User = $scope.get_user_infos(data); 408 }); 409 } 410 } 411 412 // Read the tokentypes from the server 413 TokenFactory.getEnrollTokens(function (data) { 414 //console.log("getEnrollTokens"); 415 //console.log(data); 416 $scope.formInit["tokenTypes"] = data.result.value; 417 // set the default tokentype 418 if (!$scope.formInit.tokenTypes.hasOwnProperty( 419 $scope.default_tokentype)) { 420 // if HOTP does not exist, we set another default type 421 for (const tkey in $scope.formInit.tokenTypes) { 422 // set the first key to be the default tokentype 423 $scope.form.type = tkey; 424 // Set the 2step enrollment value 425 $scope.setTwostepEnrollmentDefault(); 426 // Initialize token specific settings 427 $scope.changeTokenType(); 428 break; 429 } 430 } 431 }); 432 433 $scope.CAConnectors = []; 434 $scope.CATemplates = {}; 435 $scope.radioCSR = 'csrgenerate'; 436 437 // default enrollment callback 438 $scope.callback = function (data) { 439 let blob; 440 $scope.U2FToken = {}; 441 $scope.webAuthnToken = {}; 442 $scope.enrolledToken = data.detail; 443 $scope.click_wait = false;
444 if ($scope.enrolledToken.otps) { 445 const otps_count = Object.keys($scope.enrolledToken.otps).length; 446 $scope.otp_row_count = parseInt(otps_count / 5 + 0.5); 447 $scope.otp_rows = Object.keys($scope.enrolledToken.otps).slice(0, $scope.otp_row_count); 448 } else { 449 $scope.otp_rows = null; 450 } 451 if ($scope.enrolledToken.certificate) { 452 blob = new Blob([$scope.enrolledToken.certificate], 453 {type: 'text/plain'}); 454 $scope.certificateBlob = (window.URL || window.webkitURL).createObjectURL(blob); 455 } 456 if ($scope.enrolledToken.pkcs12) { 457 const bytechars = atob($scope.enrolledToken.pkcs12); 458 const byteNumbers = new Array(bytechars.length); 459 for (let i = 0; i < bytechars.length; i++) { 460 byteNumbers[i] = bytechars.charCodeAt(i); 461 } 462 const byteArray = new Uint8Array(byteNumbers); 463 blob = new Blob([byteArray], {type: 'application/x-pkcs12'}); 464 $scope.pkcs12Blob = (window.URL || window.webkitURL).createObjectURL(blob); 465 } 466 if ($scope.enrolledToken.u2fRegisterRequest) { 467 // This is the first step of U2F registering, save serial. 468 $scope.serial = data.detail.serial; 469 470 $scope.register_fido($scope.enrolledToken.u2fRegisterRequest, U2fFactory, $scope.U2FToken); 471 } 472 if ($scope.enrolledToken.webAuthnRegisterRequest) { 473 // This is the first step of U2F registering, save serial. 474 $scope.serial = data.detail.serial; 475 476 $scope.register_fido($scope.enrolledToken.webAuthnRegisterRequest, webAuthnToken, $scope.webAuthnToken); 477 } 478 if ($scope.enrolledToken.rollout_state === "clientwait" && !$scope.form["2stepinit"]) { 479 $scope.pollTokenInfo(); 480 } 481 // Passkey 482 $scope.bytesToBase64 = function (bytes) { 483 const binString = Array.from(bytes, (byte) => 484 String.fromCodePoint(byte),).join(""); 485 return btoa(binString); 486 }; 487 $scope.base64URLToBytes = function (base64URLString) { 488 const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/'); 489 const padLength = (4 - (base64.length % 4)) % 4; 490 const padded = base64.padEnd(base64.length + padLength, '='); 491 const binary = atob(padded); 492 const buffer = new ArrayBuffer(binary.length); 493 const bytes = new Uint8Array(buffer); 494 for (let i = 0; i < binary.length; i++) { 495 bytes[i] = binary.charCodeAt(i); 496 } 497 return buffer; 498 } 499 500 if ($scope.enrolledToken.passkey_registration) { 501 $scope.click_wait = true; 502 //console.log($scope.enrolledToken.passkey_registration); 503 let options = $scope.enrolledToken.passkey_registration; 504 let excludedCredentials = []; 505 for (const cred of options.excludeCredentials) { 506 excludedCredentials.push({ 507 id: $scope.base64URLToBytes(cred.id), 508 type: cred.type, 509 }); 510 } 511 navigator.credentials.create({ 512 publicKey: { 513 rp: options.rp, 514 user: { 515 id: $scope.base64URLToBytes(options.user.id), 516 name: options.user.name, 517 displayName: options.user.displayName 518 }, 519 challenge: Uint8Array.from(options.challenge, c => c.charCodeAt(0)), 520 pubKeyCredParams: options.pubKeyCredParams, 521 excludeCredentials: excludedCredentials, 522 authenticatorSelection: options.authenticatorSelection, 523 timeout: options.timeout, 524 extensions: { 525 credProps: true, 526 }, 527 attestation: options.attestation 528 } 529 }).then(function (publicKeyCred) { 530 //console.log("Successfully registered passkey"); 531 //console.log(publicKeyCred); 532 let params = { 533 user: $scope.newUser.user, 534 realm: $scope.newUser.realm, 535 transaction_id: data.detail.transaction_id, 536 serial: data.detail.serial, 537 type: "passkey", 538 credential_id: publicKeyCred.id, 539 rawId: $scope.bytesToBase64(new Uint8Array(publicKeyCred.rawId)), 540 authenticatorAttachment: publicKeyCred.authenticatorAttachment, 541 attestationObject: $scope.bytesToBase64( 542 new Uint8Array(publicKeyCred.response.attestationObject)), 543 clientDataJSON: $scope.bytesToBase64(new Uint8Array(publicKeyCred.response.clientDataJSON)), 544 } 545 if (publicKeyCred.response.attestationObject) {
546 params.attestationObject = $scope.bytesToBase64( 547 new Uint8Array(publicKeyCred.response.attestationObject)); 548 } 549 const extResults = publicKeyCred.getClientExtensionResults(); 550 if (extResults.credProps) { 551 params.credProps = extResults.credProps; 552 } 553 TokenFactory.initToken(params, function (response) { 554 $scope.click_wait = false; 555 }); 556 }, function (error) { 557 console.log("Error while registering passkey"); 558 console.log(error); 559 inform.add("Error while registering passkey, the token will not be created!", 560 {type: "danger", ttl: 10000}); 561 if (AuthFactory.checkRight("delete")) { 562 TokenFactory.delete(data.detail.serial, function (response) { 563 $state.go('token.list'); 564 }); 565 } 566 }); 567 } 568 // End Passkey 569 $('html,body').scrollTop(0); 570 } 571 572 $scope.enrollToken = function () { 573 $scope.enrolling = true; 574 //debug: console.log($scope.newUser.user); 575 //debug: console.log($scope.newUser.realm); 576 //debug: console.log($scope.newUser.pin); 577 $scope.newUser.user = fixUser($scope.newUser.user); 578 // convert the date object to a string 579 $scope.form.validity_period_start = date_object_to_string($scope.form.validity_period_start); 580 $scope.form.validity_period_end = date_object_to_string($scope.form.validity_period_end); 581 582 if ($scope.containerSerial !== "createnew" && $scope.containerSerial !== "none") { 583 $scope.form.container_serial = $scope.containerSerial; 584 } else { 585 // Do not send the container_serial if it has no value 586 delete $scope.form.container_serial; 587 } 588 589 TokenFactory.enroll($scope.newUser, 590 $scope.form, $scope.callback, 591 function (data) { 592 $scope.enrolling = false; 593 } 594 ); 595 }; 596 597 $scope.pollTokenInfo = function () { 598 TokenFactory.getTokenForSerial($scope.enrolledToken.serial, function (data) { 599 if (data.result.value && data.result.value.tokens && data.result.value.tokens.length > 0) { 600 $scope.enrolledToken.rollout_state = data.result.value.tokens[0].rollout_state; 601 } 602 // Poll the data after 2.5 seconds again 603 if ($scope.enrolledToken.rollout_state === "clientwait" && $location.path().indexOf("/token/enroll") > -1) { 604 $timeout($scope.pollTokenInfo, 2500); 605 } 606 }) 607 }; 608 609 $scope.regenerateToken = function (serial) { 610 const params = $scope.form; 611 if (serial) { 612 params.serial = serial; 613 } else { 614 params.serial = $scope.enrolledToken.serial; 615 } 616 TokenFactory.enroll(null, params, $scope.callback); 617 }; 618 619 $scope.sendClientPart = function () { 620 const params = { 621 "otpkey": $scope.clientpart.replace(/ /g, ""), 622 "otpkeyformat": "base32check", 623 "serial": $scope.enrolledToken.serial, 624 "type": $scope.form.type, 625 // Send the rollover parameter as well to avoid a possible PIN check 626 "rollover": $scope.form.rollover 627 }; 628 TokenFactory.enroll($scope.newUser, params, function (data) { 629 $scope.clientpart = ""; 630 $scope.callback(data); 631 }); 632 }; 633 634 $scope.sendVerifyResponse = function () { 635 const params = { 636 "serial": $scope.enrolledToken.serial, 637 "verify": $scope.verifyResponse, 638 "type": $scope.form.type 639 }; 640 TokenFactory.enroll($scope.newUser, params, function (data) { 641 if (data.result.value === true) { 642 inform.add(gettextCatalog.getString("Token successfully verified"), 643 {type: "success", ttl: 10000}); 644 } 645 $scope.verifyResponse = ""; 646 $scope.callback(data); 647 }); 648 }; 649 650 // Special Token functions 651 $scope.sshkeyChanged = function () {
652 const keyArr = $scope.form.sshkey.split(" "); 653 $scope.form.description = keyArr.slice(2).join(" "); 654 }; 655 656 $scope.yubikeyGetLen = function () { 657 let yktestdatalen = $scope.tempData.yubikeyTest.trim().length; 658 if (yktestdatalen >= 32) { 659 $scope.form.otplen = yktestdatalen; 660 } 661 }; 662 663 // U2F and WebAuthn 664 $scope.register_fido = function (registerRequest, Factory, token) { 665 // We need to send the 2nd stage of the U2F enroll 666 Factory.register_request(registerRequest, function (params) { 667 params.serial = $scope.serial; 668 TokenFactory.enroll($scope.newUser, 669 params, function (response) { 670 $scope.click_wait = false; 671 token.subject 672 = (response.detail.u2fRegisterResponse || response.detail.webAuthnRegisterResponse).subject; 673 token.vendor = token.subject.split(" ")[0]; 674 //console.log(token); 675 }); 676 }, function (error) { 677 if (AuthFactory.checkRight("delete")) { 678 TokenFactory.delete($scope.serial, function (response) { 679 $state.go('token.list'); 680 }); 681 } 682 }); 683 $scope.click_wait = true; 684 }; 685 686 // get the list of configured RADIUS server identifiers 687 $scope.getRADIUSIdentifiers = function () { 688 ConfigFactory.getRadiusNames(function (data) { 689 $scope.radiusIdentifiers = data.result.value; 690 }); 691 }; 692 693 // get the list of configured privacyIDEA server identifiers 694 $scope.getPrivacyIDEAServers = function () { 695 ConfigFactory.getPrivacyidea(function (data) { 696 $scope.privacyIDEAServers = data.result.value; 697 }); 698 }; 699 700 // get the list of configured CA connectors 701 $scope.getCAConnectors = function () { 702 ConfigFactory.getCAConnectorNames(function (data) { 703 const CAConnectors = data.result.value; 704 angular.forEach(CAConnectors, function (value, key) { 705 $scope.CAConnectors.push(value.connectorname); 706 $scope.form.ca = value.connectorname; 707 $scope.CATemplates[value.connectorname] = value; 708 }); 709 //debug: console.log($scope.CAConnectors); 710 }); 711 }; 712 713 // If the user is admin, he can read the config. 714 ConfigFactory.loadSystemConfig(function (data) { 715 /* Default config values like 716 radius.server, radius.secret... 717 are stored in systemDefault and $scope.form 718 */ 719 $scope.systemDefault = data.result.value; 720 //debug: console.log("system default config"); 721 //debug: console.log(systemDefault); 722 // TODO: The entries should be handled automatically. 723 const entries = ["radius.server", "radius.secret", "remote.server", 724 "radius.identifier", "email.mailserver", 725 "email.mailfrom", "yubico.id", "tiqr.regServer"]; 726 entries.forEach(function (entry) { 727 if (!$scope.form[entry]) { 728 // preset the UI 729 $scope.form[entry] = $scope.systemDefault[entry]; 730 } 731 }); 732 // Default HOTP hashlib 733 $scope.form.hashlib = $scope.systemDefault["hotp.hashlib"] || 'sha1'; 734 // Now add the questions 735 angular.forEach($scope.systemDefault, function (value, key) { 736 if (key.indexOf("question.question.") === 0) { 737 $scope.questions.push(value); 738 } 739 }); 740 $scope.num_answers = $scope.systemDefault["question.num_answers"]; 741 //debug: console.log($scope.questions); 742 //debug: console.log($scope.form); 743 }); 744 745 // open the window to generate the key pair 746 $scope.openCertificateWindow = function () { 747 const params = { 748 authtoken: AuthFactory.getAuthToken(), 749 ca: $scope.form.ca 750 }; 751 const tabWindowId = window.open('about:blank', '_blank'); 752 $http.post(instanceUrl + '/certificate', params).then( 753 function (response) { 754 //debug: console.log(response); 755 tabWindowId.document.write(response.data); 756 //tabWindowId.location.href = response.headers('Location'); 757 }); 758 }; 759 760 // print the paper token 761 $scope.printOtp = function () { 762 const serial = $scope.enrolledToken.serial; 763 const myWindow = window.open('', 'otpPrintingWindow', 'height=400,width=600'); 764 const css = '<link' + 765 ' href="' + instanceUrl + 766 '/static/css/papertoken.css"' + 767 ' rel="stylesheet">';
768 myWindow.document.write('<html><head><title>' + serial + '</title>'); 769 myWindow.document.write(css); 770 myWindow.document.write('</head>' + 771 '<body onload="window.print(); window.close()">'); 772 myWindow.document.write($('#paperOtpTable').html()); 773 myWindow.document.write('</body></html>'); 774 myWindow.document.close(); // necessary for IE >= 10 775 myWindow.focus(); // necessary for IE >= 10 776 return true; 777 }; 778 779 $scope.copyPKCS12PasswordToClipboard = function (text) { 780 navigator.clipboard.writeText(text).then(function () { 781 inform.add(gettextCatalog.getString("PKCS12 Password copied to clipboard"), 782 {type: "info", ttl: 3000}) 783 }); 784 } 785 786 // =========================================================== 787 // =============== Date stuff =============================== 788 // =========================================================== 789 790 $scope.openDate = function ($event) { 791 $event.stopPropagation(); 792 return true; 793 }; 794 795 $scope.today = new Date(); 796 $scope.dateOptions = { 797 formatYear: 'yy', 798 startingDay: 1 799 }; 800 } 801]); 802 803myApp.controller("tokenImportController", ['$scope', 'Upload', 'AuthFactory', 'tokenUrl', 'ConfigFactory', 'inform', 804 'gettextCatalog', 805 function ($scope, Upload, AuthFactory, tokenUrl, ConfigFactory, inform, gettextCatalog) { 806 $scope.formInit = { 807 fileTypes: ["aladdin-xml", "OATH CSV", "Yubikey CSV", "pskc"] 808 }; 809 810 $scope.verify_pskc_opts = { 811 no_check: gettextCatalog.getString('Do not verify the authenticity'), 812 check_fail_soft: gettextCatalog.getString('Skip tokens that can not be verified'), 813 check_fail_hard: gettextCatalog.getString('Abort operation on unverifiable token'), 814 } 815 816 // These are values that are also sent to the backend! 817 $scope.form = { 818 type: "OATH CSV", 819 realm: "" 820 }; 821 822 // get Realms 823 ConfigFactory.getRealms(function (data) { 824 $scope.realms = data.result.value; 825 // Preset the default realm 826 angular.forEach($scope.realms, function (realm, realmname) { 827 if (realm.default) { 828 $scope.form.realm = realmname; 829 } 830 }); 831 }); 832 833 // get PGP keys 834 ConfigFactory.getPGPKeys(function (data) { 835 $scope.pgpkeys = data.result.value; 836 }); 837 838 $scope.upload = function (file) { 839 if (file) { 840 Upload.upload({ 841 url: tokenUrl + '/load/filename', 842 headers: {'PI-Authorization': AuthFactory.getAuthToken()}, 843 data: { 844 file: file, 845 type: $scope.form.type, 846 psk: $scope.form.psk, 847 pskcValidateMAC: $scope.form.validateMAC, 848 password: $scope.form.password, 849 tokenrealms: $scope.form.realm 850 }, 851 }).then(function (resp) { 852 $scope.uploadedFile = resp.config.data.file.name; 853 $scope.uploadedTokens = resp.data.result.value.n_imported; 854 $scope.notImportedTokens = resp.data.result.value.n_not_imported; 855 }, function (error) { 856 if (error.data.result.error.code === -401) { 857 $state.go('login'); 858 } else { 859 inform.add(error.data.result.error.message, 860 {type: "danger", ttl: 10000}); 861 } 862 }, function (evt) { 863 $scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total) 864 }); 865 } 866 }; 867 }]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.