From d8a0d9bb66eb7d656ba6220d182db9a2554998a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ca=C3=B1izares=20Est=C3=A9vez?= Date: Wed, 21 Dec 2016 11:30:11 +0100 Subject: [PATCH 01/10] BasketApi: Add authorizationHeader (pass bearer from Swagger Ui to authorize request...). MVC: Solve some design feedback (filter selector in catalog) CatalogApi: Move models from infrastructure to Model folder. --- add-firewall-docker.ps1 | 23 + docker-compose.override.yml | 25 +- .../Auth/Client/enable-token-client.js | 28 + .../Auth/Client/oidc-token-manager.js | 8896 +++++++++++++++++ .../Basket/Basket.API/Auth/Client/popup.html | 13 + ...orizationHeaderParameterOperationFilter.cs | 35 + .../Auth/Server/IdentitySecurityScheme.cs | 23 + src/Services/Basket/Basket.API/Startup.cs | 7 + .../Controllers/CatalogController.cs | 1 + .../Controllers/OrdersController.cs | 2 +- src/Services/Ordering/Ordering.API/Startup.cs | 5 +- src/Web/WebMVC/wwwroot/css/site.min.css | 2 +- 12 files changed, 9044 insertions(+), 16 deletions(-) create mode 100644 add-firewall-docker.ps1 create mode 100644 src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js create mode 100644 src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js create mode 100644 src/Services/Basket/Basket.API/Auth/Client/popup.html create mode 100644 src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs create mode 100644 src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs diff --git a/add-firewall-docker.ps1 b/add-firewall-docker.ps1 new file mode 100644 index 000000000..828a2246a --- /dev/null +++ b/add-firewall-docker.ps1 @@ -0,0 +1,23 @@ +param([switch]$Elevated) +function Check-Admin { +$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) +$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) +} +if ((Check-Admin) -eq $false) { +if ($elevated) +{ +# could not elevate, quit +} + +else { + +Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition)) +} +exit +} +$reglas = Get-NetFirewallRule -DisplayName 'EshopDocker' +if ($reglas.Length -gt 0) +{ + New-NetFirewallRule -DisplayName EshopDocker -Confirm -Description "Eshop on Containers" -LocalAddress Any -LocalPort Any -Protocol tcp -RemoteAddress Any -RemotePort 5100-5105 -Direction Inbound + New-NetFirewallRule -DisplayName EshopDocker -Confirm -Description "Eshop on Containers" -LocalAddress Any -LocalPort Any -Protocol tcp -RemoteAddress Any -RemotePort 5100-5105 -Direction Outbound +} \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 285194cd6..f8138e794 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -14,19 +14,22 @@ services: - CatalogUrl=http://catalog.api - OrderingUrl=http://ordering.api:5102 #- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105. - #- IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105. - - IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. + - IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105. + #- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. - BasketUrl=http://basket.api:5103 ports: - "5100:5100" + links: + - identity.service:10.0.75.1 webspa: environment: - CatalogUrl=http://catalog.api - OrderingUrl=http://ordering.api - #- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105. - - IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. - - BasketUrl=http://basket.api:5103 + #- IdentityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105. + #- IdentityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. + - IdentityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105. + - BasketUrl=http://basket.api:5103 ports: - "5104:80" @@ -34,7 +37,8 @@ services: environment: - ConnectionString=basket.data #- identityUrl=http://13.88.8.119:5105 #Remote - - identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. + #- identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. + - identityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105. ports: - "5103:5103" @@ -47,8 +51,9 @@ services: ordering.api: environment: - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - - identityUrl=http://identity.service:5105 #local - #- identityUrl=http://13.88.8.119:5105 #remote + #- identityUrl=http://13.88.8.119:5105 #Remote: VM Needs to have public access at 5105. + #- identityUrl=http://identity.service:5105 #Local: You need a entry in windows host file to run identity in local docker. + - identityUrl=http://10.0.75.1:5105 #Local: You need to open windows firewall at range 5100-5105. ports: - "5102:5102" @@ -56,9 +61,9 @@ services: environment: - SpaClient=http://localhost:5104 - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word - #- MvcClient=http://13.88.8.119:5100 #Remote: VM Needs to have public access at 5105. + #- MvcClient=http://13.88.8.119:5100 #Remote: VM Needs to have public access at 5105. - MvcClient=http://localhost:5100 #Local: You need a entry in windows host file to run identity in local docker. - #10.0.75.1:5105 CCE/TODO: try to avoid host entry. + - MvcClient=http://10.0.75.1:5100 #Local: You need to open windows firewall at range 5100-5105. ports: - "5105:5105" diff --git a/src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js b/src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js new file mode 100644 index 000000000..3c207e7ea --- /dev/null +++ b/src/Services/Basket/Basket.API/Auth/Client/enable-token-client.js @@ -0,0 +1,28 @@ +(function ($, swaggerUi) { + $(function () { + var settings = { + authority: 'https://localhost:5105', + client_id: 'js', + popup_redirect_uri: window.location.protocol + + '//' + + window.location.host + + '/tokenclient/popup.html', + + response_type: 'id_token token', + scope: 'openid profile basket', + + filter_protocol_claims: true + }, + manager = new OidcTokenManager(settings), + $inputApiKey = $('#input_apiKey'); + + $inputApiKey.on('dblclick', function () { + manager.openPopupForTokenAsync() + .then(function () { + $inputApiKey.val(manager.access_token).change(); + }, function (error) { + console.error(error); + }); + }); + }); +})(jQuery, window.swaggerUi); \ No newline at end of file diff --git a/src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js b/src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js new file mode 100644 index 000000000..a6f3f29e5 --- /dev/null +++ b/src/Services/Basket/Basket.API/Auth/Client/oidc-token-manager.js @@ -0,0 +1,8896 @@ +(function () { + + // globals + var _promiseFactory; + var _httpRequest; +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** + * CryptoJS core components. + */ +var CryptoJS = CryptoJS || (function (Math, undefined) { + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + function F() {} + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + F.prototype = this; + var subtype = new F(); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init')) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else if (thatWords.length > 0xffff) { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } else { + // Copy all words at once + thisWords.push.apply(thisWords, thatWords); + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + for (var i = 0; i < nBytes; i += 4) { + words.push((Math.random() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; +}(Math)); + +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Reusable object + var W = []; + + /** + * SHA-1 hash algorithm. + */ + var SHA1 = C_algo.SHA1 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476, + 0xc3d2e1f0 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + + // Computation + for (var i = 0; i < 80; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; + W[i] = (n << 1) | (n >>> 31); + } + + var t = ((a << 5) | (a >>> 27)) + e + W[i]; + if (i < 20) { + t += ((b & c) | (~b & d)) + 0x5a827999; + } else if (i < 40) { + t += (b ^ c ^ d) + 0x6ed9eba1; + } else if (i < 60) { + t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; + } else /* if (i < 80) */ { + t += (b ^ c ^ d) - 0x359d3e2a; + } + + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA1('message'); + * var hash = CryptoJS.SHA1(wordArray); + */ + C.SHA1 = Hasher._createHelper(SHA1); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA1(message, key); + */ + C.HmacSHA1 = Hasher._createHmacHelper(SHA1); +}()); + +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Initialization and round constants tables + var H = []; + var K = []; + + // Compute constants + (function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + if (nPrime < 8) { + H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); + } + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + + nPrime++; + } + + n++; + } + }()); + + // Reusable object + var W = []; + + /** + * SHA-256 hash algorithm. + */ + var SHA256 = C_algo.SHA256 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init(H.slice(0)); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA256('message'); + * var hash = CryptoJS.SHA256(wordArray); + */ + C.SHA256 = Hasher._createHelper(SHA256); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA256(message, key); + */ + C.HmacSHA256 = Hasher._createHmacHelper(SHA256); +}(Math)); + +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var X32WordArray = C_lib.WordArray; + + /** + * x64 namespace. + */ + var C_x64 = C.x64 = {}; + + /** + * A 64-bit word. + */ + var X64Word = C_x64.Word = Base.extend({ + /** + * Initializes a newly created 64-bit word. + * + * @param {number} high The high 32 bits. + * @param {number} low The low 32 bits. + * + * @example + * + * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); + */ + init: function (high, low) { + this.high = high; + this.low = low; + } + + /** + * Bitwise NOTs this word. + * + * @return {X64Word} A new x64-Word object after negating. + * + * @example + * + * var negated = x64Word.not(); + */ + // not: function () { + // var high = ~this.high; + // var low = ~this.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ANDs this word with the passed word. + * + * @param {X64Word} word The x64-Word to AND with this word. + * + * @return {X64Word} A new x64-Word object after ANDing. + * + * @example + * + * var anded = x64Word.and(anotherX64Word); + */ + // and: function (word) { + // var high = this.high & word.high; + // var low = this.low & word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to OR with this word. + * + * @return {X64Word} A new x64-Word object after ORing. + * + * @example + * + * var ored = x64Word.or(anotherX64Word); + */ + // or: function (word) { + // var high = this.high | word.high; + // var low = this.low | word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise XORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to XOR with this word. + * + * @return {X64Word} A new x64-Word object after XORing. + * + * @example + * + * var xored = x64Word.xor(anotherX64Word); + */ + // xor: function (word) { + // var high = this.high ^ word.high; + // var low = this.low ^ word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the left. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftL(25); + */ + // shiftL: function (n) { + // if (n < 32) { + // var high = (this.high << n) | (this.low >>> (32 - n)); + // var low = this.low << n; + // } else { + // var high = this.low << (n - 32); + // var low = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the right. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftR(7); + */ + // shiftR: function (n) { + // if (n < 32) { + // var low = (this.low >>> n) | (this.high << (32 - n)); + // var high = this.high >>> n; + // } else { + // var low = this.high >>> (n - 32); + // var high = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Rotates this word n bits to the left. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotL(25); + */ + // rotL: function (n) { + // return this.shiftL(n).or(this.shiftR(64 - n)); + // }, + + /** + * Rotates this word n bits to the right. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotR(7); + */ + // rotR: function (n) { + // return this.shiftR(n).or(this.shiftL(64 - n)); + // }, + + /** + * Adds this word with the passed word. + * + * @param {X64Word} word The x64-Word to add with this word. + * + * @return {X64Word} A new x64-Word object after adding. + * + * @example + * + * var added = x64Word.add(anotherX64Word); + */ + // add: function (word) { + // var low = (this.low + word.low) | 0; + // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; + // var high = (this.high + word.high + carry) | 0; + + // return X64Word.create(high, low); + // } + }); + + /** + * An array of 64-bit words. + * + * @property {Array} words The array of CryptoJS.x64.Word objects. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var X64WordArray = C_x64.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.x64.WordArray.create(); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ]); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ], 10); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 8; + } + }, + + /** + * Converts this 64-bit word array to a 32-bit word array. + * + * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. + * + * @example + * + * var x32WordArray = x64WordArray.toX32(); + */ + toX32: function () { + // Shortcuts + var x64Words = this.words; + var x64WordsLength = x64Words.length; + + // Convert + var x32Words = []; + for (var i = 0; i < x64WordsLength; i++) { + var x64Word = x64Words[i]; + x32Words.push(x64Word.high); + x32Words.push(x64Word.low); + } + + return X32WordArray.create(x32Words, this.sigBytes); + }, + + /** + * Creates a copy of this word array. + * + * @return {X64WordArray} The clone. + * + * @example + * + * var clone = x64WordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + + // Clone "words" array + var words = clone.words = this.words.slice(0); + + // Clone each X64Word object + var wordsLength = words.length; + for (var i = 0; i < wordsLength; i++) { + words[i] = words[i].clone(); + } + + return clone; + } + }); +}()); +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + + function X64Word_create() { + return X64Word.create.apply(X64Word, arguments); + } + + // Constants + var K = [ + X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), + X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), + X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), + X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), + X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), + X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), + X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), + X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), + X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), + X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), + X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), + X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), + X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), + X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), + X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), + X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), + X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), + X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), + X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), + X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), + X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), + X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), + X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), + X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), + X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), + X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), + X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), + X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), + X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), + X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), + X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), + X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), + X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), + X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), + X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), + X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), + X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), + X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), + X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), + X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817) + ]; + + // Reusable objects + var W = []; + (function () { + for (var i = 0; i < 80; i++) { + W[i] = X64Word_create(); + } + }()); + + /** + * SHA-512 hash algorithm. + */ + var SHA512 = C_algo.SHA512 = Hasher.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), + new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), + new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), + new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179) + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var H = this._hash.words; + + var H0 = H[0]; + var H1 = H[1]; + var H2 = H[2]; + var H3 = H[3]; + var H4 = H[4]; + var H5 = H[5]; + var H6 = H[6]; + var H7 = H[7]; + + var H0h = H0.high; + var H0l = H0.low; + var H1h = H1.high; + var H1l = H1.low; + var H2h = H2.high; + var H2l = H2.low; + var H3h = H3.high; + var H3l = H3.low; + var H4h = H4.high; + var H4l = H4.low; + var H5h = H5.high; + var H5l = H5.low; + var H6h = H6.high; + var H6l = H6.low; + var H7h = H7.high; + var H7l = H7.low; + + // Working variables + var ah = H0h; + var al = H0l; + var bh = H1h; + var bl = H1l; + var ch = H2h; + var cl = H2l; + var dh = H3h; + var dl = H3l; + var eh = H4h; + var el = H4l; + var fh = H5h; + var fl = H5l; + var gh = H6h; + var gl = H6l; + var hh = H7h; + var hl = H7l; + + // Rounds + for (var i = 0; i < 80; i++) { + // Shortcut + var Wi = W[i]; + + // Extend message + if (i < 16) { + var Wih = Wi.high = M[offset + i * 2] | 0; + var Wil = Wi.low = M[offset + i * 2 + 1] | 0; + } else { + // Gamma0 + var gamma0x = W[i - 15]; + var gamma0xh = gamma0x.high; + var gamma0xl = gamma0x.low; + var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); + var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); + + // Gamma1 + var gamma1x = W[i - 2]; + var gamma1xh = gamma1x.high; + var gamma1xl = gamma1x.low; + var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); + var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); + + // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] + var Wi7 = W[i - 7]; + var Wi7h = Wi7.high; + var Wi7l = Wi7.low; + + var Wi16 = W[i - 16]; + var Wi16h = Wi16.high; + var Wi16l = Wi16.low; + + var Wil = gamma0l + Wi7l; + var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); + var Wil = Wil + gamma1l; + var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); + var Wil = Wil + Wi16l; + var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); + + Wi.high = Wih; + Wi.low = Wil; + } + + var chh = (eh & fh) ^ (~eh & gh); + var chl = (el & fl) ^ (~el & gl); + var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); + var majl = (al & bl) ^ (al & cl) ^ (bl & cl); + + var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); + var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); + var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); + var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); + + // t1 = h + sigma1 + ch + K[i] + W[i] + var Ki = K[i]; + var Kih = Ki.high; + var Kil = Ki.low; + + var t1l = hl + sigma1l; + var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); + var t1l = t1l + chl; + var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); + var t1l = t1l + Kil; + var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); + var t1l = t1l + Wil; + var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); + + // t2 = sigma0 + maj + var t2l = sigma0l + majl; + var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); + + // Update working variables + hh = gh; + hl = gl; + gh = fh; + gl = fl; + fh = eh; + fl = el; + el = (dl + t1l) | 0; + eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; + dh = ch; + dl = cl; + ch = bh; + cl = bl; + bh = ah; + bl = al; + al = (t1l + t2l) | 0; + ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; + } + + // Intermediate hash value + H0l = H0.low = (H0l + al); + H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); + H1l = H1.low = (H1l + bl); + H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); + H2l = H2.low = (H2l + cl); + H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); + H3l = H3.low = (H3l + dl); + H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); + H4l = H4.low = (H4l + el); + H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); + H5l = H5.low = (H5l + fl); + H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); + H6l = H6.low = (H6l + gl); + H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); + H7l = H7.low = (H7l + hl); + H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Convert hash to 32-bit word array before returning + var hash = this._hash.toX32(); + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + }, + + blockSize: 1024/32 + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA512('message'); + * var hash = CryptoJS.SHA512(wordArray); + */ + C.SHA512 = Hasher._createHelper(SHA512); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA512(message, key); + */ + C.HmacSHA512 = Hasher._createHmacHelper(SHA512); +}()); + + +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +var b64pad="="; + +function hex2b64(h) { + var i; + var c; + var ret = ""; + for(i = 0; i+3 <= h.length; i+=3) { + c = parseInt(h.substring(i,i+3),16); + ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63); + } + if(i+1 == h.length) { + c = parseInt(h.substring(i,i+1),16); + ret += b64map.charAt(c << 2); + } + else if(i+2 == h.length) { + c = parseInt(h.substring(i,i+2),16); + ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4); + } + if (b64pad) while((ret.length & 3) > 0) ret += b64pad; + return ret; +} + +// convert a base64 string to hex +function b64tohex(s) { + var ret = "" + var i; + var k = 0; // b64 state, 0-3 + var slop; + var v; + for(i = 0; i < s.length; ++i) { + if(s.charAt(i) == b64pad) break; + v = b64map.indexOf(s.charAt(i)); + if(v < 0) continue; + if(k == 0) { + ret += int2char(v >> 2); + slop = v & 3; + k = 1; + } + else if(k == 1) { + ret += int2char((slop << 2) | (v >> 4)); + slop = v & 0xf; + k = 2; + } + else if(k == 2) { + ret += int2char(slop); + ret += int2char(v >> 2); + slop = v & 3; + k = 3; + } + else { + ret += int2char((slop << 2) | (v >> 4)); + ret += int2char(v & 0xf); + k = 0; + } + } + if(k == 1) + ret += int2char(slop << 2); + return ret; +} + +// convert a base64 string to a byte/number array +function b64toBA(s) { + //piggyback on b64tohex for now, optimize later + var h = b64tohex(s); + var i; + var a = new Array(); + for(i = 0; 2*i < h.length; ++i) { + a[i] = parseInt(h.substring(2*i,2*i+2),16); + } + return a; +} +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +// Copyright (c) 2005 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+this.DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return (this.s<0)?-r:r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +// Depends on jsbn.js and rng.js + +// Version 1.1: support utf-8 encoding in pkcs1pad2 + +// convert a (hex) string to a bignum object +function parseBigInt(str,r) { + return new BigInteger(str,r); +} + +function linebrk(s,n) { + var ret = ""; + var i = 0; + while(i + n < s.length) { + ret += s.substring(i,i+n) + "\n"; + i += n; + } + return ret + s.substring(i,s.length); +} + +function byte2Hex(b) { + if(b < 0x10) + return "0" + b.toString(16); + else + return b.toString(16); +} + +// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint +function pkcs1pad2(s,n) { + if(n < s.length + 11) { // TODO: fix for utf-8 + alert("Message too long for RSA"); + return null; + } + var ba = new Array(); + var i = s.length - 1; + while(i >= 0 && n > 0) { + var c = s.charCodeAt(i--); + if(c < 128) { // encode using utf-8 + ba[--n] = c; + } + else if((c > 127) && (c < 2048)) { + ba[--n] = (c & 63) | 128; + ba[--n] = (c >> 6) | 192; + } + else { + ba[--n] = (c & 63) | 128; + ba[--n] = ((c >> 6) & 63) | 128; + ba[--n] = (c >> 12) | 224; + } + } + ba[--n] = 0; + var rng = new SecureRandom(); + var x = new Array(); + while(n > 2) { // random non-zero pad + x[0] = 0; + while(x[0] == 0) rng.nextBytes(x); + ba[--n] = x[0]; + } + ba[--n] = 2; + ba[--n] = 0; + return new BigInteger(ba); +} + +// PKCS#1 (OAEP) mask generation function +function oaep_mgf1_arr(seed, len, hash) +{ + var mask = '', i = 0; + + while (mask.length < len) + { + mask += hash(String.fromCharCode.apply(String, seed.concat([ + (i & 0xff000000) >> 24, + (i & 0x00ff0000) >> 16, + (i & 0x0000ff00) >> 8, + i & 0x000000ff]))); + i += 1; + } + + return mask; +} + +var SHA1_SIZE = 20; + +// PKCS#1 (OAEP) pad input string s to n bytes, and return a bigint +function oaep_pad(s, n, hash) +{ + if (s.length + 2 * SHA1_SIZE + 2 > n) + { + throw "Message too long for RSA"; + } + + var PS = '', i; + + for (i = 0; i < n - s.length - 2 * SHA1_SIZE - 2; i += 1) + { + PS += '\x00'; + } + + var DB = rstr_sha1('') + PS + '\x01' + s; + var seed = new Array(SHA1_SIZE); + new SecureRandom().nextBytes(seed); + + var dbMask = oaep_mgf1_arr(seed, DB.length, hash || rstr_sha1); + var maskedDB = []; + + for (i = 0; i < DB.length; i += 1) + { + maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i); + } + + var seedMask = oaep_mgf1_arr(maskedDB, seed.length, rstr_sha1); + var maskedSeed = [0]; + + for (i = 0; i < seed.length; i += 1) + { + maskedSeed[i + 1] = seed[i] ^ seedMask.charCodeAt(i); + } + + return new BigInteger(maskedSeed.concat(maskedDB)); +} + +// "empty" RSA key constructor +function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; +} + +// Set the public key fields N and e from hex strings +function RSASetPublic(N,E) { + this.isPublic = true; + if (typeof N !== "string") + { + this.n = N; + this.e = E; + } + else if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + } + else + alert("Invalid RSA public key"); +} + +// Perform raw public operation on "x": return x^e (mod n) +function RSADoPublic(x) { + return x.modPowInt(this.e, this.n); +} + +// Return the PKCS#1 RSA encryption of "text" as an even-length hex string +function RSAEncrypt(text) { + var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); + if(m == null) return null; + var c = this.doPublic(m); + if(c == null) return null; + var h = c.toString(16); + if((h.length & 1) == 0) return h; else return "0" + h; +} + +// Return the PKCS#1 OAEP RSA encryption of "text" as an even-length hex string +function RSAEncryptOAEP(text, hash) { + var m = oaep_pad(text, (this.n.bitLength()+7)>>3, hash); + if(m == null) return null; + var c = this.doPublic(m); + if(c == null) return null; + var h = c.toString(16); + if((h.length & 1) == 0) return h; else return "0" + h; +} + +// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string +//function RSAEncryptB64(text) { +// var h = this.encrypt(text); +// if(h) return hex2b64(h); else return null; +//} + +// protected +RSAKey.prototype.doPublic = RSADoPublic; + +// public +RSAKey.prototype.setPublic = RSASetPublic; +RSAKey.prototype.encrypt = RSAEncrypt; +RSAKey.prototype.encryptOAEP = RSAEncryptOAEP; +//RSAKey.prototype.encrypt_b64 = RSAEncryptB64; + +RSAKey.prototype.type = "RSA"; +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +// Depends on rsa.js and jsbn2.js + +// Version 1.1: support utf-8 decoding in pkcs1unpad2 + +// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext +function pkcs1unpad2(d,n) { + var b = d.toByteArray(); + var i = 0; + while(i < b.length && b[i] == 0) ++i; + if(b.length-i != n-1 || b[i] != 2) + return null; + ++i; + while(b[i] != 0) + if(++i >= b.length) return null; + var ret = ""; + while(++i < b.length) { + var c = b[i] & 255; + if(c < 128) { // utf-8 decode + ret += String.fromCharCode(c); + } + else if((c > 191) && (c < 224)) { + ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63)); + ++i; + } + else { + ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63)); + i += 2; + } + } + return ret; +} + +// PKCS#1 (OAEP) mask generation function +function oaep_mgf1_str(seed, len, hash) +{ + var mask = '', i = 0; + + while (mask.length < len) + { + mask += hash(seed + String.fromCharCode.apply(String, [ + (i & 0xff000000) >> 24, + (i & 0x00ff0000) >> 16, + (i & 0x0000ff00) >> 8, + i & 0x000000ff])); + i += 1; + } + + return mask; +} + +var SHA1_SIZE = 20; + +// Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext +function oaep_unpad(d, n, hash) +{ + d = d.toByteArray(); + + var i; + + for (i = 0; i < d.length; i += 1) + { + d[i] &= 0xff; + } + + while (d.length < n) + { + d.unshift(0); + } + + d = String.fromCharCode.apply(String, d); + + if (d.length < 2 * SHA1_SIZE + 2) + { + throw "Cipher too short"; + } + + var maskedSeed = d.substr(1, SHA1_SIZE) + var maskedDB = d.substr(SHA1_SIZE + 1); + + var seedMask = oaep_mgf1_str(maskedDB, SHA1_SIZE, hash || rstr_sha1); + var seed = [], i; + + for (i = 0; i < maskedSeed.length; i += 1) + { + seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i); + } + + var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed), + d.length - SHA1_SIZE, rstr_sha1); + + var DB = []; + + for (i = 0; i < maskedDB.length; i += 1) + { + DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i); + } + + DB = String.fromCharCode.apply(String, DB); + + if (DB.substr(0, SHA1_SIZE) !== rstr_sha1('')) + { + throw "Hash mismatch"; + } + + DB = DB.substr(SHA1_SIZE); + + var first_one = DB.indexOf('\x01'); + var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1; + + if (last_zero + 1 != first_one) + { + throw "Malformed data"; + } + + return DB.substr(first_one + 1); +} + +// Set the private key fields N, e, and d from hex strings +function RSASetPrivate(N,E,D) { + this.isPrivate = true; + if (typeof N !== "string") + { + this.n = N; + this.e = E; + this.d = D; + } + else if(N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + } + else + alert("Invalid RSA private key"); +} + +// Set the private key fields N, e, d and CRT params from hex strings +function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { + this.isPrivate = true; + if (N == null) throw "RSASetPrivateEx N == null"; + if (E == null) throw "RSASetPrivateEx E == null"; + if (N.length == 0) throw "RSASetPrivateEx N.length == 0"; + if (E.length == 0) throw "RSASetPrivateEx E.length == 0"; + + if (N != null && E != null && N.length > 0 && E.length > 0) { + this.n = parseBigInt(N,16); + this.e = parseInt(E,16); + this.d = parseBigInt(D,16); + this.p = parseBigInt(P,16); + this.q = parseBigInt(Q,16); + this.dmp1 = parseBigInt(DP,16); + this.dmq1 = parseBigInt(DQ,16); + this.coeff = parseBigInt(C,16); + } else { + alert("Invalid RSA private key in RSASetPrivateEx"); + } +} + +// Generate a new random private key B bits long, using public expt E +function RSAGenerate(B,E) { + var rng = new SecureRandom(); + var qs = B>>1; + this.e = parseInt(E,16); + var ee = new BigInteger(E,16); + for(;;) { + for(;;) { + this.p = new BigInteger(B-qs,1,rng); + if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; + } + for(;;) { + this.q = new BigInteger(qs,1,rng); + if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; + } + if(this.p.compareTo(this.q) <= 0) { + var t = this.p; + this.p = this.q; + this.q = t; + } + var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1 + var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1 + var phi = p1.multiply(q1); + if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { + this.n = this.p.multiply(this.q); // this.n = p * q + this.d = ee.modInverse(phi); // this.d = + this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1) + this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1) + this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p + break; + } + } + this.isPrivate = true; +} + +// Perform raw private operation on "x": return x^d (mod n) +function RSADoPrivate(x) { + if(this.p == null || this.q == null) + return x.modPow(this.d, this.n); + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp? + var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq? + + while(xp.compareTo(xq) < 0) + xp = xp.add(this.p); + // NOTE: + // xp.subtract(xq) => cp -cq + // xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h + // xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is an even-length hex string and the output is a plain string. +function RSADecrypt(ctext) { + var c = parseBigInt(ctext, 16); + var m = this.doPrivate(c); + if(m == null) return null; + return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); +} + +// Return the PKCS#1 OAEP RSA decryption of "ctext". +// "ctext" is an even-length hex string and the output is a plain string. +function RSADecryptOAEP(ctext, hash) { + var c = parseBigInt(ctext, 16); + var m = this.doPrivate(c); + if(m == null) return null; + return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash); +} + +// Return the PKCS#1 RSA decryption of "ctext". +// "ctext" is a Base64-encoded string and the output is a plain string. +//function RSAB64Decrypt(ctext) { +// var h = b64tohex(ctext); +// if(h) return this.decrypt(h); else return null; +//} + +// protected +RSAKey.prototype.doPrivate = RSADoPrivate; + +// public +RSAKey.prototype.setPrivate = RSASetPrivate; +RSAKey.prototype.setPrivateEx = RSASetPrivateEx; +RSAKey.prototype.generate = RSAGenerate; +RSAKey.prototype.decrypt = RSADecrypt; +RSAKey.prototype.decryptOAEP = RSADecryptOAEP; +//RSAKey.prototype.b64_decrypt = RSAB64Decrypt; +/*! rsapem-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license + */ +// +// rsa-pem.js - adding function for reading/writing PKCS#1 PEM private key +// to RSAKey class. +// +// version: 1.1.1 (2013-Apr-12) +// +// Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com) +// +// This software is licensed under the terms of the MIT License. +// http://kjur.github.com/jsrsasign/license/ +// +// The above copyright and license notice shall be +// included in all copies or substantial portions of the Software. +// +// +// Depends on: +// +// +// +// _RSApem_pemToBase64(sPEM) +// +// removing PEM header, PEM footer and space characters including +// new lines from PEM formatted RSA private key string. +// + +/** + * @fileOverview + * @name rsapem-1.1.js + * @author Kenji Urushima kenji.urushima@gmail.com + * @version 1.1 + * @license MIT License + */ +function _rsapem_pemToBase64(sPEMPrivateKey) { + var s = sPEMPrivateKey; + s = s.replace("-----BEGIN RSA PRIVATE KEY-----", ""); + s = s.replace("-----END RSA PRIVATE KEY-----", ""); + s = s.replace(/[ \n]+/g, ""); + return s; +} + +function _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey) { + var a = new Array(); + var v1 = ASN1HEX.getStartPosOfV_AtObj(hPrivateKey, 0); + var n1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, v1); + var e1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, n1); + var d1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, e1); + var p1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, d1); + var q1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, p1); + var dp1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, q1); + var dq1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dp1); + var co1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dq1); + a.push(v1, n1, e1, d1, p1, q1, dp1, dq1, co1); + return a; +} + +function _rsapem_getHexValueArrayOfChildrenFromHex(hPrivateKey) { + var posArray = _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey); + var v = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[0]); + var n = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[1]); + var e = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[2]); + var d = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[3]); + var p = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[4]); + var q = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[5]); + var dp = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[6]); + var dq = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[7]); + var co = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[8]); + var a = new Array(); + a.push(v, n, e, d, p, q, dp, dq, co); + return a; +} + +/** + * read RSA private key from a ASN.1 hexadecimal string + * @name readPrivateKeyFromASN1HexString + * @memberOf RSAKey# + * @function + * @param {String} keyHex ASN.1 hexadecimal string of PKCS#1 private key. + * @since 1.1.1 + */ +function _rsapem_readPrivateKeyFromASN1HexString(keyHex) { + var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex); + this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]); +} + +/** + * read PKCS#1 private key from a string + * @name readPrivateKeyFromPEMString + * @memberOf RSAKey# + * @function + * @param {String} keyPEM string of PKCS#1 private key. + */ +function _rsapem_readPrivateKeyFromPEMString(keyPEM) { + var keyB64 = _rsapem_pemToBase64(keyPEM); + var keyHex = b64tohex(keyB64) // depends base64.js + var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex); + this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]); +} + +RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString; +RSAKey.prototype.readPrivateKeyFromASN1HexString = _rsapem_readPrivateKeyFromASN1HexString; +/*! rsasign-1.2.7.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license + */ +var _RE_HEXDECONLY=new RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]","gi");function _rsasign_getHexPaddedDigestInfoForString(d,e,a){var b=function(f){return KJUR.crypto.Util.hashString(f,a)};var c=b(d);return KJUR.crypto.Util.getPaddedDigestInfoHex(c,a,e)}function _zeroPaddingOfSignature(e,d){var c="";var a=d/4-e.length;for(var b=0;b>24,(d&16711680)>>16,(d&65280)>>8,d&255]))));d+=1}return b}function _rsasign_signStringPSS(e,a,d){var c=function(f){return KJUR.crypto.Util.hashHex(f,a)};var b=c(rstrtohex(e));if(d===undefined){d=-1}return this.signWithMessageHashPSS(b,a,d)}function _rsasign_signWithMessageHashPSS(l,a,k){var b=hextorstr(l);var g=b.length;var m=this.n.bitLength()-1;var c=Math.ceil(m/8);var d;var o=function(i){return KJUR.crypto.Util.hashHex(i,a)};if(k===-1||k===undefined){k=g}else{if(k===-2){k=c-g-2}else{if(k<-2){throw"invalid salt length"}}}if(c<(g+k+2)){throw"data too long"}var f="";if(k>0){f=new Array(k);new SecureRandom().nextBytes(f);f=String.fromCharCode.apply(String,f)}var n=hextorstr(o(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+b+f)));var j=[];for(d=0;d>(8*c-m))&255;q[0]&=~p;for(d=0;dthis.n.bitLength()){return 0}var i=this.doPublic(b);var e=i.toString(16).replace(/^1f+00/,"");var g=_rsasign_getAlgNameAndHashFromHexDisgestInfo(e);if(g.length==0){return false}var d=g[0];var h=g[1];var a=function(k){return KJUR.crypto.Util.hashString(k,d)};var c=a(f);return(h==c)}function _rsasign_verifyWithMessageHash(e,a){a=a.replace(_RE_HEXDECONLY,"");a=a.replace(/[ \n]+/g,"");var b=parseBigInt(a,16);if(b.bitLength()>this.n.bitLength()){return 0}var h=this.doPublic(b);var g=h.toString(16).replace(/^1f+00/,"");var c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(g);if(c.length==0){return false}var d=c[0];var f=c[1];return(f==e)}function _rsasign_verifyStringPSS(c,b,a,f){var e=function(g){return KJUR.crypto.Util.hashHex(g,a)};var d=e(rstrtohex(c));if(f===undefined){f=-1}return this.verifyWithMessageHashPSS(d,b,a,f)}function _rsasign_verifyWithMessageHashPSS(f,s,l,c){var k=new BigInteger(s,16);if(k.bitLength()>this.n.bitLength()){return false}var r=function(i){return KJUR.crypto.Util.hashHex(i,l)};var j=hextorstr(f);var h=j.length;var g=this.n.bitLength()-1;var m=Math.ceil(g/8);var q;if(c===-1||c===undefined){c=h}else{if(c===-2){c=m-h-2}else{if(c<-2){throw"invalid salt length"}}}if(m<(h+c+2)){throw"data too long"}var a=this.doPublic(k).toByteArray();for(q=0;q>(8*m-g))&255;if((d.charCodeAt(0)&p)!==0){throw"bits beyond keysize not zero"}var n=pss_mgf1_str(e,d.length,r);var o=[];for(q=0;qMIT License + */ + +/* + * MEMO: + * f('3082025b02...', 2) ... 82025b ... 3bytes + * f('020100', 2) ... 01 ... 1byte + * f('0203001...', 2) ... 03 ... 1byte + * f('02818003...', 2) ... 8180 ... 2bytes + * f('3080....0000', 2) ... 80 ... -1 + * + * Requirements: + * - ASN.1 type octet length MUST be 1. + * (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...) + */ + +/** + * ASN.1 DER encoded hexadecimal string utility class + * @name ASN1HEX + * @class ASN.1 DER encoded hexadecimal string utility class + * @since jsrsasign 1.1 + */ +var ASN1HEX = new function() { + /** + * get byte length for ASN.1 L(length) bytes + * @name getByteLengthOfL_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return byte length for ASN.1 L(length) bytes + */ + this.getByteLengthOfL_AtObj = function(s, pos) { + if (s.substring(pos + 2, pos + 3) != '8') return 1; + var i = parseInt(s.substring(pos + 3, pos + 4)); + if (i == 0) return -1; // length octet '80' indefinite length + if (0 < i && i < 10) return i + 1; // including '8?' octet; + return -2; // malformed format + }; + + /** + * get hexadecimal string for ASN.1 L(length) bytes + * @name getHexOfL_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return {String} hexadecimal string for ASN.1 L(length) bytes + */ + this.getHexOfL_AtObj = function(s, pos) { + var len = this.getByteLengthOfL_AtObj(s, pos); + if (len < 1) return ''; + return s.substring(pos + 2, pos + 2 + len * 2); + }; + + // getting ASN.1 length value at the position 'idx' of + // hexa decimal string 's'. + // + // f('3082025b02...', 0) ... 82025b ... ??? + // f('020100', 0) ... 01 ... 1 + // f('0203001...', 0) ... 03 ... 3 + // f('02818003...', 0) ... 8180 ... 128 + /** + * get integer value of ASN.1 length for ASN.1 data + * @name getIntOfL_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return ASN.1 L(length) integer value + */ + this.getIntOfL_AtObj = function(s, pos) { + var hLength = this.getHexOfL_AtObj(s, pos); + if (hLength == '') return -1; + var bi; + if (parseInt(hLength.substring(0, 1)) < 8) { + bi = new BigInteger(hLength, 16); + } else { + bi = new BigInteger(hLength.substring(2), 16); + } + return bi.intValue(); + }; + + /** + * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'. + * @name getStartPosOfV_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + */ + this.getStartPosOfV_AtObj = function(s, pos) { + var l_len = this.getByteLengthOfL_AtObj(s, pos); + if (l_len < 0) return l_len; + return pos + (l_len + 1) * 2; + }; + + /** + * get hexadecimal string of ASN.1 V(value) + * @name getHexOfV_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return {String} hexadecimal string of ASN.1 value. + */ + this.getHexOfV_AtObj = function(s, pos) { + var pos1 = this.getStartPosOfV_AtObj(s, pos); + var len = this.getIntOfL_AtObj(s, pos); + return s.substring(pos1, pos1 + len * 2); + }; + + /** + * get hexadecimal string of ASN.1 TLV at + * @name getHexOfTLV_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return {String} hexadecimal string of ASN.1 TLV. + * @since 1.1 + */ + this.getHexOfTLV_AtObj = function(s, pos) { + var hT = s.substr(pos, 2); + var hL = this.getHexOfL_AtObj(s, pos); + var hV = this.getHexOfV_AtObj(s, pos); + return hT + hL + hV; + }; + + /** + * get next sibling starting index for ASN.1 object string + * @name getPosOfNextSibling_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} pos string index + * @return next sibling starting index for ASN.1 object string + */ + this.getPosOfNextSibling_AtObj = function(s, pos) { + var pos1 = this.getStartPosOfV_AtObj(s, pos); + var len = this.getIntOfL_AtObj(s, pos); + return pos1 + len * 2; + }; + + /** + * get array of indexes of child ASN.1 objects + * @name getPosArrayOfChildren_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} s hexadecimal string of ASN.1 DER encoded data + * @param {Number} start string index of ASN.1 object + * @return {Array of Number} array of indexes for childen of ASN.1 objects + */ + this.getPosArrayOfChildren_AtObj = function(h, pos) { + var a = new Array(); + var p0 = this.getStartPosOfV_AtObj(h, pos); + a.push(p0); + + var len = this.getIntOfL_AtObj(h, pos); + var p = p0; + var k = 0; + while (1) { + var pNext = this.getPosOfNextSibling_AtObj(h, p); + if (pNext == null || (pNext - p0 >= (len * 2))) break; + if (k >= 200) break; + + a.push(pNext); + p = pNext; + + k++; + } + + return a; + }; + + /** + * get string index of nth child object of ASN.1 object refered by h, idx + * @name getNthChildIndex_AtObj + * @memberOf ASN1HEX + * @function + * @param {String} h hexadecimal string of ASN.1 DER encoded data + * @param {Number} idx start string index of ASN.1 object + * @param {Number} nth for child + * @return {Number} string index of nth child. + * @since 1.1 + */ + this.getNthChildIndex_AtObj = function(h, idx, nth) { + var a = this.getPosArrayOfChildren_AtObj(h, idx); + return a[nth]; + }; + + // ========== decendant methods ============================== + /** + * get string index of nth child object of ASN.1 object refered by h, idx + * @name getDecendantIndexByNthList + * @memberOf ASN1HEX + * @function + * @param {String} h hexadecimal string of ASN.1 DER encoded data + * @param {Number} currentIndex start string index of ASN.1 object + * @param {Array of Number} nthList array list of nth + * @return {Number} string index refered by nthList + * @since 1.1 + * @example + * The "nthList" is a index list of structured ASN.1 object + * reference. Here is a sample structure and "nthList"s which + * refers each objects. + * + * SQUENCE - + * SEQUENCE - [0] + * IA5STRING 000 - [0, 0] + * UTF8STRING 001 - [0, 1] + * SET - [1] + * IA5STRING 010 - [1, 0] + * UTF8STRING 011 - [1, 1] + */ + this.getDecendantIndexByNthList = function(h, currentIndex, nthList) { + if (nthList.length == 0) { + return currentIndex; + } + var firstNth = nthList.shift(); + var a = this.getPosArrayOfChildren_AtObj(h, currentIndex); + return this.getDecendantIndexByNthList(h, a[firstNth], nthList); + }; + + /** + * get hexadecimal string of ASN.1 TLV refered by current index and nth index list. + * @name getDecendantHexTLVByNthList + * @memberOf ASN1HEX + * @function + * @param {String} h hexadecimal string of ASN.1 DER encoded data + * @param {Number} currentIndex start string index of ASN.1 object + * @param {Array of Number} nthList array list of nth + * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList + * @since 1.1 + */ + this.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) { + var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); + return this.getHexOfTLV_AtObj(h, idx); + }; + + /** + * get hexadecimal string of ASN.1 V refered by current index and nth index list. + * @name getDecendantHexVByNthList + * @memberOf ASN1HEX + * @function + * @param {String} h hexadecimal string of ASN.1 DER encoded data + * @param {Number} currentIndex start string index of ASN.1 object + * @param {Array of Number} nthList array list of nth + * @return {Number} hexadecimal string of ASN.1 V refered by nthList + * @since 1.1 + */ + this.getDecendantHexVByNthList = function(h, currentIndex, nthList) { + var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); + return this.getHexOfV_AtObj(h, idx); + }; +}; + +/* + * @since asn1hex 1.1.4 + */ +ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) { + var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); + if (idx === undefined) { + throw "can't find nthList object"; + } + if (checkingTag !== undefined) { + if (h.substr(idx, 2) != checkingTag) { + throw "checking tag doesn't match: " + + h.substr(idx,2) + "!=" + checkingTag; + } + } + return this.getHexOfV_AtObj(h, idx); +}; + +/** + * get OID string from hexadecimal encoded value + * @name hextooidstr + * @memberOf ASN1HEX + * @function + * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value + * @return {String} OID string (ex. '1.2.3.4.567') + * @since asn1hex 1.1.5 + */ +ASN1HEX.hextooidstr = function(hex) { + var zeroPadding = function(s, len) { + if (s.length >= len) return s; + return new Array(len - s.length + 1).join('0') + s; + }; + + var a = []; + + // a[0], a[1] + var hex0 = hex.substr(0, 2); + var i0 = parseInt(hex0, 16); + a[0] = new String(Math.floor(i0 / 40)); + a[1] = new String(i0 % 40); + + // a[2]..a[n] + var hex1 = hex.substr(2); + var b = []; + for (var i = 0; i < hex1.length / 2; i++) { + b.push(parseInt(hex1.substr(i * 2, 2), 16)); + } + var c = []; + var cbin = ""; + for (var i = 0; i < b.length; i++) { + if (b[i] & 0x80) { + cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); + } else { + cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); + c.push(new String(parseInt(cbin, 2))); + cbin = ""; + } + } + + var s = a.join("."); + if (c.length > 0) s = s + "." + c.join("."); + return s; +}; + +/*! x509-1.1.3.js (c) 2012-2014 Kenji Urushima | kjur.github.com/jsrsasign/license + */ +/* + * x509.js - X509 class to read subject public key from certificate. + * + * Copyright (c) 2010-2014 Kenji Urushima (kenji.urushima@gmail.com) + * + * This software is licensed under the terms of the MIT License. + * http://kjur.github.com/jsrsasign/license + * + * The above copyright and license notice shall be + * included in all copies or substantial portions of the Software. + */ + +/** + * @fileOverview + * @name x509-1.1.js + * @author Kenji Urushima kenji.urushima@gmail.com + * @version x509 1.1.3 (2014-May-17) + * @since jsrsasign 1.x.x + * @license MIT License + */ + +/* + * Depends: + * base64.js + * rsa.js + * asn1hex.js + */ + +/** + * X.509 certificate class.
+ * @class X.509 certificate class + * @property {RSAKey} subjectPublicKeyRSA Tom Wu's RSAKey object + * @property {String} subjectPublicKeyRSA_hN hexadecimal string for modulus of RSA public key + * @property {String} subjectPublicKeyRSA_hE hexadecimal string for public exponent of RSA public key + * @property {String} hex hexacedimal string for X.509 certificate. + * @author Kenji Urushima + * @version 1.0.1 (08 May 2012) + * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/ + */ +function X509() { + this.subjectPublicKeyRSA = null; + this.subjectPublicKeyRSA_hN = null; + this.subjectPublicKeyRSA_hE = null; + this.hex = null; + + // ===== get basic fields from hex ===================================== + + /** + * get hexadecimal string of serialNumber field of certificate.
+ * @name getSerialNumberHex + * @memberOf X509# + * @function + */ + this.getSerialNumberHex = function() { + return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]); + }; + + /** + * get hexadecimal string of issuer field TLV of certificate.
+ * @name getIssuerHex + * @memberOf X509# + * @function + */ + this.getIssuerHex = function() { + return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]); + }; + + /** + * get string of issuer field of certificate.
+ * @name getIssuerString + * @memberOf X509# + * @function + */ + this.getIssuerString = function() { + return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3])); + }; + + /** + * get hexadecimal string of subject field of certificate.
+ * @name getSubjectHex + * @memberOf X509# + * @function + */ + this.getSubjectHex = function() { + return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]); + }; + + /** + * get string of subject field of certificate.
+ * @name getSubjectString + * @memberOf X509# + * @function + */ + this.getSubjectString = function() { + return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5])); + }; + + /** + * get notBefore field string of certificate.
+ * @name getNotBefore + * @memberOf X509# + * @function + */ + this.getNotBefore = function() { + var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]); + s = s.replace(/(..)/g, "%$1"); + s = decodeURIComponent(s); + return s; + }; + + /** + * get notAfter field string of certificate.
+ * @name getNotAfter + * @memberOf X509# + * @function + */ + this.getNotAfter = function() { + var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]); + s = s.replace(/(..)/g, "%$1"); + s = decodeURIComponent(s); + return s; + }; + + // ===== read certificate public key ========================== + + // ===== read certificate ===================================== + /** + * read PEM formatted X.509 certificate from string.
+ * @name readCertPEM + * @memberOf X509# + * @function + * @param {String} sCertPEM string for PEM formatted X.509 certificate + */ + this.readCertPEM = function(sCertPEM) { + var hCert = X509.pemToHex(sCertPEM); + var a = X509.getPublicKeyHexArrayFromCertHex(hCert); + var rsa = new RSAKey(); + rsa.setPublic(a[0], a[1]); + this.subjectPublicKeyRSA = rsa; + this.subjectPublicKeyRSA_hN = a[0]; + this.subjectPublicKeyRSA_hE = a[1]; + this.hex = hCert; + }; + + this.readCertPEMWithoutRSAInit = function(sCertPEM) { + var hCert = X509.pemToHex(sCertPEM); + var a = X509.getPublicKeyHexArrayFromCertHex(hCert); + this.subjectPublicKeyRSA.setPublic(a[0], a[1]); + this.subjectPublicKeyRSA_hN = a[0]; + this.subjectPublicKeyRSA_hE = a[1]; + this.hex = hCert; + }; +}; + +X509.pemToBase64 = function(sCertPEM) { + var s = sCertPEM; + s = s.replace("-----BEGIN CERTIFICATE-----", ""); + s = s.replace("-----END CERTIFICATE-----", ""); + s = s.replace(/[ \n]+/g, ""); + return s; +}; + +X509.pemToHex = function(sCertPEM) { + var b64Cert = X509.pemToBase64(sCertPEM); + var hCert = b64tohex(b64Cert); + return hCert; +}; + +// NOTE: Without BITSTRING encapsulation. +X509.getSubjectPublicKeyPosFromCertHex = function(hCert) { + var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert); + if (pInfo == -1) return -1; + var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo); + if (a.length != 2) return -1; + var pBitString = a[1]; + if (hCert.substring(pBitString, pBitString + 2) != '03') return -1; + var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString); + + if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1; + return pBitStringV + 2; +}; + +// NOTE: privateKeyUsagePeriod field of X509v2 not supported. +// NOTE: v1 and v3 supported +X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) { + var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); + var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert); + if (a.length < 1) return -1; + if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3 + if (a.length < 6) return -1; + return a[6]; + } else { + if (a.length < 5) return -1; + return a[5]; + } +}; + +X509.getPublicKeyHexArrayFromCertHex = function(hCert) { + var p = X509.getSubjectPublicKeyPosFromCertHex(hCert); + var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p); + if (a.length != 2) return []; + var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]); + var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]); + if (hN != null && hE != null) { + return [hN, hE]; + } else { + return []; + } +}; + +X509.getHexTbsCertificateFromCert = function(hCert) { + var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); + return pTbsCert; +}; + +X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) { + var hCert = X509.pemToHex(sCertPEM); + var a = X509.getPublicKeyHexArrayFromCertHex(hCert); + return a; +}; + +X509.hex2dn = function(hDN) { + var s = ""; + var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0); + for (var i = 0; i < a.length; i++) { + var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]); + s = s + "/" + X509.hex2rdn(hRDN); + } + return s; +}; + +X509.hex2rdn = function(hRDN) { + var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]); + var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]); + var type = ""; + try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; } + hValue = hValue.replace(/(..)/g, "%$1"); + var value = decodeURIComponent(hValue); + return type + "=" + value; +}; + +X509.DN_ATTRHEX = { + "0603550406": "C", + "060355040a": "O", + "060355040b": "OU", + "0603550403": "CN", + "0603550405": "SN", + "0603550408": "ST", + "0603550407": "L", +}; + +/** + * get RSAKey/ECDSA public key object from PEM certificate string + * @name getPublicKeyFromCertPEM + * @memberOf X509 + * @function + * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate + * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key + * @since x509 1.1.1 + * @description + * NOTE: DSA is also supported since x509 1.1.2. + */ +X509.getPublicKeyFromCertPEM = function(sCertPEM) { + var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM); + + if (info.algoid == "2a864886f70d010101") { // RSA + var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex); + var key = new RSAKey(); + key.setPublic(aRSA.n, aRSA.e); + return key; + } else if (info.algoid == "2a8648ce3d0201") { // ECC + var curveName = KJUR.crypto.OID.oidhex2name[info.algparam]; + var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex}); + key.setPublicKeyHex(info.keyhex); + return key; + } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1 + var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02"); + var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02"); + var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02"); + var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0); + y = y.substr(2); + var key = new KJUR.crypto.DSA(); + key.setPublic(new BigInteger(p, 16), + new BigInteger(q, 16), + new BigInteger(g, 16), + new BigInteger(y, 16)); + return key; + } else { + throw "unsupported key"; + } +}; + +/** + * get public key information from PEM certificate + * @name getPublicKeyInfoPropOfCertPEM + * @memberOf X509 + * @function + * @param {String} sCertPEM string of PEM formatted certificate + * @return {Hash} hash of information for public key + * @since x509 1.1.1 + * @description + * Resulted associative array has following properties: + *
    + *
  • algoid - hexadecimal string of OID of asymmetric key algorithm
  • + *
  • algparam - hexadecimal string of OID of ECC curve name or null
  • + *
  • keyhex - hexadecimal string of key in the certificate
  • + *
+ * @since x509 1.1.1 + */ +X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) { + var result = {}; + result.algparam = null; + var hCert = X509.pemToHex(sCertPEM); + + // 1. Certificate ASN.1 + var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0); + if (a1.length != 3) + throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert + + // 2. tbsCertificate + if (hCert.substr(a1[0], 2) != "30") + throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq + + var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]); + + // 3. subjectPublicKeyInfo + if (a2.length < 7) + throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo + + var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]); + + if (a3.length != 2) + throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey + + // 4. AlgId + var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]); + + if (a4.length != 2) + throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId + + result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]); + + if (hCert.substr(a4[1], 2) == "06") { // EC + result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]); + } else if (hCert.substr(a4[1], 2) == "30") { // DSA + result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]); + } + + // 5. Public Key Hex + if (hCert.substr(a3[1], 2) != "03") + throw "malformed X.509 certificate PEM (code:006)"; // not bitstring + + var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]); + result.keyhex = unusedBitAndKeyHex.substr(2); + + return result; +}; + +/* + X509.prototype.readCertPEM = _x509_readCertPEM; + X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit; + X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex; + X509.prototype.getIssuerHex = _x509_getIssuerHex; + X509.prototype.getSubjectHex = _x509_getSubjectHex; + X509.prototype.getIssuerString = _x509_getIssuerString; + X509.prototype.getSubjectString = _x509_getSubjectString; + X509.prototype.getNotBefore = _x509_getNotBefore; + X509.prototype.getNotAfter = _x509_getNotAfter; +*/ +/*! crypto-1.1.5.js (c) 2013 Kenji Urushima | kjur.github.com/jsrsasign/license + */ +/* + * crypto.js - Cryptographic Algorithm Provider class + * + * Copyright (c) 2013 Kenji Urushima (kenji.urushima@gmail.com) + * + * This software is licensed under the terms of the MIT License. + * http://kjur.github.com/jsrsasign/license + * + * The above copyright and license notice shall be + * included in all copies or substantial portions of the Software. + */ + +/** + * @fileOverview + * @name crypto-1.1.js + * @author Kenji Urushima kenji.urushima@gmail.com + * @version 1.1.5 (2013-Oct-06) + * @since jsrsasign 2.2 + * @license MIT License + */ + +/** + * kjur's class library name space + * @name KJUR + * @namespace kjur's class library name space + */ +if (typeof KJUR == "undefined" || !KJUR) KJUR = {}; +/** + * kjur's cryptographic algorithm provider library name space + *

+ * This namespace privides following crytpgrahic classes. + *

    + *
  • {@link KJUR.crypto.MessageDigest} - Java JCE(cryptograhic extension) style MessageDigest class
  • + *
  • {@link KJUR.crypto.Signature} - Java JCE(cryptograhic extension) style Signature class
  • + *
  • {@link KJUR.crypto.Util} - cryptographic utility functions and properties
  • + *
+ * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2. + *

+ * @name KJUR.crypto + * @namespace + */ +if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {}; + +/** + * static object for cryptographic function utilities + * @name KJUR.crypto.Util + * @class static object for cryptographic function utilities + * @property {Array} DIGESTINFOHEAD PKCS#1 DigestInfo heading hexadecimal bytes for each hash algorithms + * @property {Array} DEFAULTPROVIDER associative array of default provider name for each hash and signature algorithms + * @description + */ +KJUR.crypto.Util = new function() { + this.DIGESTINFOHEAD = { + 'sha1': "3021300906052b0e03021a05000414", + 'sha224': "302d300d06096086480165030402040500041c", + 'sha256': "3031300d060960864801650304020105000420", + 'sha384': "3041300d060960864801650304020205000430", + 'sha512': "3051300d060960864801650304020305000440", + 'md2': "3020300c06082a864886f70d020205000410", + 'md5': "3020300c06082a864886f70d020505000410", + 'ripemd160': "3021300906052b2403020105000414", + }; + + /* + * @since crypto 1.1.1 + */ + this.DEFAULTPROVIDER = { + 'md5': 'cryptojs', + 'sha1': 'cryptojs', + 'sha224': 'cryptojs', + 'sha256': 'cryptojs', + 'sha384': 'cryptojs', + 'sha512': 'cryptojs', + 'ripemd160': 'cryptojs', + 'hmacmd5': 'cryptojs', + 'hmacsha1': 'cryptojs', + 'hmacsha224': 'cryptojs', + 'hmacsha256': 'cryptojs', + 'hmacsha384': 'cryptojs', + 'hmacsha512': 'cryptojs', + 'hmacripemd160': 'cryptojs', + + 'MD5withRSA': 'cryptojs/jsrsa', + 'SHA1withRSA': 'cryptojs/jsrsa', + 'SHA224withRSA': 'cryptojs/jsrsa', + 'SHA256withRSA': 'cryptojs/jsrsa', + 'SHA384withRSA': 'cryptojs/jsrsa', + 'SHA512withRSA': 'cryptojs/jsrsa', + 'RIPEMD160withRSA': 'cryptojs/jsrsa', + + 'MD5withECDSA': 'cryptojs/jsrsa', + 'SHA1withECDSA': 'cryptojs/jsrsa', + 'SHA224withECDSA': 'cryptojs/jsrsa', + 'SHA256withECDSA': 'cryptojs/jsrsa', + 'SHA384withECDSA': 'cryptojs/jsrsa', + 'SHA512withECDSA': 'cryptojs/jsrsa', + 'RIPEMD160withECDSA': 'cryptojs/jsrsa', + + 'SHA1withDSA': 'cryptojs/jsrsa', + 'SHA224withDSA': 'cryptojs/jsrsa', + 'SHA256withDSA': 'cryptojs/jsrsa', + + 'MD5withRSAandMGF1': 'cryptojs/jsrsa', + 'SHA1withRSAandMGF1': 'cryptojs/jsrsa', + 'SHA224withRSAandMGF1': 'cryptojs/jsrsa', + 'SHA256withRSAandMGF1': 'cryptojs/jsrsa', + 'SHA384withRSAandMGF1': 'cryptojs/jsrsa', + 'SHA512withRSAandMGF1': 'cryptojs/jsrsa', + 'RIPEMD160withRSAandMGF1': 'cryptojs/jsrsa', + }; + + /* + * @since crypto 1.1.2 + */ + this.CRYPTOJSMESSAGEDIGESTNAME = { + 'md5': 'CryptoJS.algo.MD5', + 'sha1': 'CryptoJS.algo.SHA1', + 'sha224': 'CryptoJS.algo.SHA224', + 'sha256': 'CryptoJS.algo.SHA256', + 'sha384': 'CryptoJS.algo.SHA384', + 'sha512': 'CryptoJS.algo.SHA512', + 'ripemd160': 'CryptoJS.algo.RIPEMD160' + }; + + /** + * get hexadecimal DigestInfo + * @name getDigestInfoHex + * @memberOf KJUR.crypto.Util + * @function + * @param {String} hHash hexadecimal hash value + * @param {String} alg hash algorithm name (ex. 'sha1') + * @return {String} hexadecimal string DigestInfo ASN.1 structure + */ + this.getDigestInfoHex = function(hHash, alg) { + if (typeof this.DIGESTINFOHEAD[alg] == "undefined") + throw "alg not supported in Util.DIGESTINFOHEAD: " + alg; + return this.DIGESTINFOHEAD[alg] + hHash; + }; + + /** + * get PKCS#1 padded hexadecimal DigestInfo + * @name getPaddedDigestInfoHex + * @memberOf KJUR.crypto.Util + * @function + * @param {String} hHash hexadecimal hash value of message to be signed + * @param {String} alg hash algorithm name (ex. 'sha1') + * @param {Integer} keySize key bit length (ex. 1024) + * @return {String} hexadecimal string of PKCS#1 padded DigestInfo + */ + this.getPaddedDigestInfoHex = function(hHash, alg, keySize) { + var hDigestInfo = this.getDigestInfoHex(hHash, alg); + var pmStrLen = keySize / 4; // minimum PM length + + if (hDigestInfo.length + 22 > pmStrLen) // len(0001+ff(*8)+00+hDigestInfo)=22 + throw "key is too short for SigAlg: keylen=" + keySize + "," + alg; + + var hHead = "0001"; + var hTail = "00" + hDigestInfo; + var hMid = ""; + var fLen = pmStrLen - hHead.length - hTail.length; + for (var i = 0; i < fLen; i += 2) { + hMid += "ff"; + } + var hPaddedMessage = hHead + hMid + hTail; + return hPaddedMessage; + }; + + /** + * get hexadecimal hash of string with specified algorithm + * @name hashString + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @param {String} alg hash algorithm name + * @return {String} hexadecimal string of hash value + * @since 1.1.1 + */ + this.hashString = function(s, alg) { + var md = new KJUR.crypto.MessageDigest({'alg': alg}); + return md.digestString(s); + }; + + /** + * get hexadecimal hash of hexadecimal string with specified algorithm + * @name hashHex + * @memberOf KJUR.crypto.Util + * @function + * @param {String} sHex input hexadecimal string to be hashed + * @param {String} alg hash algorithm name + * @return {String} hexadecimal string of hash value + * @since 1.1.1 + */ + this.hashHex = function(sHex, alg) { + var md = new KJUR.crypto.MessageDigest({'alg': alg}); + return md.digestHex(sHex); + }; + + /** + * get hexadecimal SHA1 hash of string + * @name sha1 + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @return {String} hexadecimal string of hash value + * @since 1.0.3 + */ + this.sha1 = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'sha1', 'prov':'cryptojs'}); + return md.digestString(s); + }; + + /** + * get hexadecimal SHA256 hash of string + * @name sha256 + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @return {String} hexadecimal string of hash value + * @since 1.0.3 + */ + this.sha256 = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'}); + return md.digestString(s); + }; + + this.sha256Hex = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'}); + return md.digestHex(s); + }; + + /** + * get hexadecimal SHA512 hash of string + * @name sha512 + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @return {String} hexadecimal string of hash value + * @since 1.0.3 + */ + this.sha512 = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'}); + return md.digestString(s); + }; + + this.sha512Hex = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'}); + return md.digestHex(s); + }; + + /** + * get hexadecimal MD5 hash of string + * @name md5 + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @return {String} hexadecimal string of hash value + * @since 1.0.3 + */ + this.md5 = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'md5', 'prov':'cryptojs'}); + return md.digestString(s); + }; + + /** + * get hexadecimal RIPEMD160 hash of string + * @name ripemd160 + * @memberOf KJUR.crypto.Util + * @function + * @param {String} s input string to be hashed + * @return {String} hexadecimal string of hash value + * @since 1.0.3 + */ + this.ripemd160 = function(s) { + var md = new KJUR.crypto.MessageDigest({'alg':'ripemd160', 'prov':'cryptojs'}); + return md.digestString(s); + }; + + /* + * @since 1.1.2 + */ + this.getCryptoJSMDByName = function(s) { + + }; +}; + +/** + * MessageDigest class which is very similar to java.security.MessageDigest class + * @name KJUR.crypto.MessageDigest + * @class MessageDigest class which is very similar to java.security.MessageDigest class + * @param {Array} params parameters for constructor + * @description + *
+ * Currently this supports following algorithm and providers combination: + *
    + *
  • md5 - cryptojs
  • + *
  • sha1 - cryptojs
  • + *
  • sha224 - cryptojs
  • + *
  • sha256 - cryptojs
  • + *
  • sha384 - cryptojs
  • + *
  • sha512 - cryptojs
  • + *
  • ripemd160 - cryptojs
  • + *
  • sha256 - sjcl (NEW from crypto.js 1.0.4)
  • + *
+ * @example + * // CryptoJS provider sample + * <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/core.js"></script> + * <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/sha1.js"></script> + * <script src="crypto-1.0.js"></script> + * var md = new KJUR.crypto.MessageDigest({alg: "sha1", prov: "cryptojs"}); + * md.updateString('aaa') + * var mdHex = md.digest() + * + * // SJCL(Stanford JavaScript Crypto Library) provider sample + * <script src="http://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script> + * <script src="crypto-1.0.js"></script> + * var md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "sjcl"}); // sjcl supports sha256 only + * md.updateString('aaa') + * var mdHex = md.digest() + */ +KJUR.crypto.MessageDigest = function(params) { + var md = null; + var algName = null; + var provName = null; + + /** + * set hash algorithm and provider + * @name setAlgAndProvider + * @memberOf KJUR.crypto.MessageDigest + * @function + * @param {String} alg hash algorithm name + * @param {String} prov provider name + * @description + * @example + * // for SHA1 + * md.setAlgAndProvider('sha1', 'cryptojs'); + * // for RIPEMD160 + * md.setAlgAndProvider('ripemd160', 'cryptojs'); + */ + this.setAlgAndProvider = function(alg, prov) { + if (alg != null && prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg]; + + // for cryptojs + if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(alg) != -1 && + prov == 'cryptojs') { + try { + this.md = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[alg]).create(); + } catch (ex) { + throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex; + } + this.updateString = function(str) { + this.md.update(str); + }; + this.updateHex = function(hex) { + var wHex = CryptoJS.enc.Hex.parse(hex); + this.md.update(wHex); + }; + this.digest = function() { + var hash = this.md.finalize(); + return hash.toString(CryptoJS.enc.Hex); + }; + this.digestString = function(str) { + this.updateString(str); + return this.digest(); + }; + this.digestHex = function(hex) { + this.updateHex(hex); + return this.digest(); + }; + } + if (':sha256:'.indexOf(alg) != -1 && + prov == 'sjcl') { + try { + this.md = new sjcl.hash.sha256(); + } catch (ex) { + throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex; + } + this.updateString = function(str) { + this.md.update(str); + }; + this.updateHex = function(hex) { + var baHex = sjcl.codec.hex.toBits(hex); + this.md.update(baHex); + }; + this.digest = function() { + var hash = this.md.finalize(); + return sjcl.codec.hex.fromBits(hash); + }; + this.digestString = function(str) { + this.updateString(str); + return this.digest(); + }; + this.digestHex = function(hex) { + this.updateHex(hex); + return this.digest(); + }; + } + }; + + /** + * update digest by specified string + * @name updateString + * @memberOf KJUR.crypto.MessageDigest + * @function + * @param {String} str string to update + * @description + * @example + * md.updateString('New York'); + */ + this.updateString = function(str) { + throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; + }; + + /** + * update digest by specified hexadecimal string + * @name updateHex + * @memberOf KJUR.crypto.MessageDigest + * @function + * @param {String} hex hexadecimal string to update + * @description + * @example + * md.updateHex('0afe36'); + */ + this.updateHex = function(hex) { + throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; + }; + + /** + * completes hash calculation and returns hash result + * @name digest + * @memberOf KJUR.crypto.MessageDigest + * @function + * @description + * @example + * md.digest() + */ + this.digest = function() { + throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName; + }; + + /** + * performs final update on the digest using string, then completes the digest computation + * @name digestString + * @memberOf KJUR.crypto.MessageDigest + * @function + * @param {String} str string to final update + * @description + * @example + * md.digestString('aaa') + */ + this.digestString = function(str) { + throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName; + }; + + /** + * performs final update on the digest using hexadecimal string, then completes the digest computation + * @name digestHex + * @memberOf KJUR.crypto.MessageDigest + * @function + * @param {String} hex hexadecimal string to final update + * @description + * @example + * md.digestHex('0f2abd') + */ + this.digestHex = function(hex) { + throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName; + }; + + if (params !== undefined) { + if (params['alg'] !== undefined) { + this.algName = params['alg']; + if (params['prov'] === undefined) + this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; + this.setAlgAndProvider(this.algName, this.provName); + } + } +}; + +/** + * Mac(Message Authentication Code) class which is very similar to java.security.Mac class + * @name KJUR.crypto.Mac + * @class Mac class which is very similar to java.security.Mac class + * @param {Array} params parameters for constructor + * @description + *
+ * Currently this supports following algorithm and providers combination: + *
    + *
  • hmacmd5 - cryptojs
  • + *
  • hmacsha1 - cryptojs
  • + *
  • hmacsha224 - cryptojs
  • + *
  • hmacsha256 - cryptojs
  • + *
  • hmacsha384 - cryptojs
  • + *
  • hmacsha512 - cryptojs
  • + *
+ * NOTE: HmacSHA224 and HmacSHA384 issue was fixed since jsrsasign 4.1.4. + * Please use 'ext/cryptojs-312-core-fix*.js' instead of 'core.js' of original CryptoJS + * to avoid those issue. + * @example + * var mac = new KJUR.crypto.Mac({alg: "HmacSHA1", prov: "cryptojs", "pass": "pass"}); + * mac.updateString('aaa') + * var macHex = md.doFinal() + */ +KJUR.crypto.Mac = function(params) { + var mac = null; + var pass = null; + var algName = null; + var provName = null; + var algProv = null; + + this.setAlgAndProvider = function(alg, prov) { + if (alg == null) alg = "hmacsha1"; + + alg = alg.toLowerCase(); + if (alg.substr(0, 4) != "hmac") { + throw "setAlgAndProvider unsupported HMAC alg: " + alg; + } + + if (prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg]; + this.algProv = alg + "/" + prov; + + var hashAlg = alg.substr(4); + + // for cryptojs + if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(hashAlg) != -1 && + prov == 'cryptojs') { + try { + var mdObj = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[hashAlg]); + this.mac = CryptoJS.algo.HMAC.create(mdObj, this.pass); + } catch (ex) { + throw "setAlgAndProvider hash alg set fail hashAlg=" + hashAlg + "/" + ex; + } + this.updateString = function(str) { + this.mac.update(str); + }; + this.updateHex = function(hex) { + var wHex = CryptoJS.enc.Hex.parse(hex); + this.mac.update(wHex); + }; + this.doFinal = function() { + var hash = this.mac.finalize(); + return hash.toString(CryptoJS.enc.Hex); + }; + this.doFinalString = function(str) { + this.updateString(str); + return this.doFinal(); + }; + this.doFinalHex = function(hex) { + this.updateHex(hex); + return this.doFinal(); + }; + } + }; + + /** + * update digest by specified string + * @name updateString + * @memberOf KJUR.crypto.Mac + * @function + * @param {String} str string to update + * @description + * @example + * md.updateString('New York'); + */ + this.updateString = function(str) { + throw "updateString(str) not supported for this alg/prov: " + this.algProv; + }; + + /** + * update digest by specified hexadecimal string + * @name updateHex + * @memberOf KJUR.crypto.Mac + * @function + * @param {String} hex hexadecimal string to update + * @description + * @example + * md.updateHex('0afe36'); + */ + this.updateHex = function(hex) { + throw "updateHex(hex) not supported for this alg/prov: " + this.algProv; + }; + + /** + * completes hash calculation and returns hash result + * @name doFinal + * @memberOf KJUR.crypto.Mac + * @function + * @description + * @example + * md.digest() + */ + this.doFinal = function() { + throw "digest() not supported for this alg/prov: " + this.algProv; + }; + + /** + * performs final update on the digest using string, then completes the digest computation + * @name doFinalString + * @memberOf KJUR.crypto.Mac + * @function + * @param {String} str string to final update + * @description + * @example + * md.digestString('aaa') + */ + this.doFinalString = function(str) { + throw "digestString(str) not supported for this alg/prov: " + this.algProv; + }; + + /** + * performs final update on the digest using hexadecimal string, + * then completes the digest computation + * @name doFinalHex + * @memberOf KJUR.crypto.Mac + * @function + * @param {String} hex hexadecimal string to final update + * @description + * @example + * md.digestHex('0f2abd') + */ + this.doFinalHex = function(hex) { + throw "digestHex(hex) not supported for this alg/prov: " + this.algProv; + }; + + if (params !== undefined) { + if (params['pass'] !== undefined) { + this.pass = params['pass']; + } + if (params['alg'] !== undefined) { + this.algName = params['alg']; + if (params['prov'] === undefined) + this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; + this.setAlgAndProvider(this.algName, this.provName); + } + } +}; + +/** + * Signature class which is very similar to java.security.Signature class + * @name KJUR.crypto.Signature + * @class Signature class which is very similar to java.security.Signature class + * @param {Array} params parameters for constructor + * @property {String} state Current state of this signature object whether 'SIGN', 'VERIFY' or null + * @description + *
+ * As for params of constructor's argument, it can be specify following attributes: + *
    + *
  • alg - signature algorithm name (ex. {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}with{RSA,ECDSA,DSA})
  • + *
  • provider - currently 'cryptojs/jsrsa' only
  • + *
+ *

SUPPORTED ALGORITHMS AND PROVIDERS

+ * This Signature class supports following signature algorithm and provider names: + *
    + *
  • MD5withRSA - cryptojs/jsrsa
  • + *
  • SHA1withRSA - cryptojs/jsrsa
  • + *
  • SHA224withRSA - cryptojs/jsrsa
  • + *
  • SHA256withRSA - cryptojs/jsrsa
  • + *
  • SHA384withRSA - cryptojs/jsrsa
  • + *
  • SHA512withRSA - cryptojs/jsrsa
  • + *
  • RIPEMD160withRSA - cryptojs/jsrsa
  • + *
  • MD5withECDSA - cryptojs/jsrsa
  • + *
  • SHA1withECDSA - cryptojs/jsrsa
  • + *
  • SHA224withECDSA - cryptojs/jsrsa
  • + *
  • SHA256withECDSA - cryptojs/jsrsa
  • + *
  • SHA384withECDSA - cryptojs/jsrsa
  • + *
  • SHA512withECDSA - cryptojs/jsrsa
  • + *
  • RIPEMD160withECDSA - cryptojs/jsrsa
  • + *
  • MD5withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA1withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA224withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA256withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA384withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA512withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • RIPEMD160withRSAandMGF1 - cryptojs/jsrsa
  • + *
  • SHA1withDSA - cryptojs/jsrsa
  • + *
  • SHA224withDSA - cryptojs/jsrsa
  • + *
  • SHA256withDSA - cryptojs/jsrsa
  • + *
+ * Here are supported elliptic cryptographic curve names and their aliases for ECDSA: + *
    + *
  • secp256k1
  • + *
  • secp256r1, NIST P-256, P-256, prime256v1
  • + *
  • secp384r1, NIST P-384, P-384
  • + *
+ * NOTE1: DSA signing algorithm is also supported since crypto 1.1.5. + *

EXAMPLES

+ * @example + * // RSA signature generation + * var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA"}); + * sig.init(prvKeyPEM); + * sig.updateString('aaa'); + * var hSigVal = sig.sign(); + * + * // DSA signature validation + * var sig2 = new KJUR.crypto.Signature({"alg": "SHA1withDSA"}); + * sig2.init(certPEM); + * sig.updateString('aaa'); + * var isValid = sig2.verify(hSigVal); + * + * // ECDSA signing + * var sig = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'}); + * sig.init(prvKeyPEM); + * sig.updateString('aaa'); + * var sigValueHex = sig.sign(); + * + * // ECDSA verifying + * var sig2 = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'}); + * sig.init(certPEM); + * sig.updateString('aaa'); + * var isValid = sig.verify(sigValueHex); + */ +KJUR.crypto.Signature = function(params) { + var prvKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for signing + var pubKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for verifying + + var md = null; // KJUR.crypto.MessageDigest object + var sig = null; + var algName = null; + var provName = null; + var algProvName = null; + var mdAlgName = null; + var pubkeyAlgName = null; // rsa,ecdsa,rsaandmgf1(=rsapss) + var state = null; + var pssSaltLen = -1; + var initParams = null; + + var sHashHex = null; // hex hash value for hex + var hDigestInfo = null; + var hPaddedDigestInfo = null; + var hSign = null; + + this._setAlgNames = function() { + if (this.algName.match(/^(.+)with(.+)$/)) { + this.mdAlgName = RegExp.$1.toLowerCase(); + this.pubkeyAlgName = RegExp.$2.toLowerCase(); + } + }; + + this._zeroPaddingOfSignature = function(hex, bitLength) { + var s = ""; + var nZero = bitLength / 4 - hex.length; + for (var i = 0; i < nZero; i++) { + s = s + "0"; + } + return s + hex; + }; + + /** + * set signature algorithm and provider + * @name setAlgAndProvider + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} alg signature algorithm name + * @param {String} prov provider name + * @description + * @example + * md.setAlgAndProvider('SHA1withRSA', 'cryptojs/jsrsa'); + */ + this.setAlgAndProvider = function(alg, prov) { + this._setAlgNames(); + if (prov != 'cryptojs/jsrsa') + throw "provider not supported: " + prov; + + if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(this.mdAlgName) != -1) { + try { + this.md = new KJUR.crypto.MessageDigest({'alg':this.mdAlgName}); + } catch (ex) { + throw "setAlgAndProvider hash alg set fail alg=" + + this.mdAlgName + "/" + ex; + } + + this.init = function(keyparam, pass) { + var keyObj = null; + try { + if (pass === undefined) { + keyObj = KEYUTIL.getKey(keyparam); + } else { + keyObj = KEYUTIL.getKey(keyparam, pass); + } + } catch (ex) { + throw "init failed:" + ex; + } + + if (keyObj.isPrivate === true) { + this.prvKey = keyObj; + this.state = "SIGN"; + } else if (keyObj.isPublic === true) { + this.pubKey = keyObj; + this.state = "VERIFY"; + } else { + throw "init failed.:" + keyObj; + } + }; + + this.initSign = function(params) { + if (typeof params['ecprvhex'] == 'string' && + typeof params['eccurvename'] == 'string') { + this.ecprvhex = params['ecprvhex']; + this.eccurvename = params['eccurvename']; + } else { + this.prvKey = params; + } + this.state = "SIGN"; + }; + + this.initVerifyByPublicKey = function(params) { + if (typeof params['ecpubhex'] == 'string' && + typeof params['eccurvename'] == 'string') { + this.ecpubhex = params['ecpubhex']; + this.eccurvename = params['eccurvename']; + } else if (params instanceof KJUR.crypto.ECDSA) { + this.pubKey = params; + } else if (params instanceof RSAKey) { + this.pubKey = params; + } + this.state = "VERIFY"; + }; + + this.initVerifyByCertificatePEM = function(certPEM) { + var x509 = new X509(); + x509.readCertPEM(certPEM); + this.pubKey = x509.subjectPublicKeyRSA; + this.state = "VERIFY"; + }; + + this.updateString = function(str) { + this.md.updateString(str); + }; + this.updateHex = function(hex) { + this.md.updateHex(hex); + }; + + this.sign = function() { + this.sHashHex = this.md.digest(); + if (typeof this.ecprvhex != "undefined" && + typeof this.eccurvename != "undefined") { + var ec = new KJUR.crypto.ECDSA({'curve': this.eccurvename}); + this.hSign = ec.signHex(this.sHashHex, this.ecprvhex); + } else if (this.pubkeyAlgName == "rsaandmgf1") { + this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex, + this.mdAlgName, + this.pssSaltLen); + } else if (this.pubkeyAlgName == "rsa") { + this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, + this.mdAlgName); + } else if (this.prvKey instanceof KJUR.crypto.ECDSA) { + this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); + } else if (this.prvKey instanceof KJUR.crypto.DSA) { + this.hSign = this.prvKey.signWithMessageHash(this.sHashHex); + } else { + throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; + } + return this.hSign; + }; + this.signString = function(str) { + this.updateString(str); + return this.sign(); + }; + this.signHex = function(hex) { + this.updateHex(hex); + return this.sign(); + }; + this.verify = function(hSigVal) { + this.sHashHex = this.md.digest(); + if (typeof this.ecpubhex != "undefined" && + typeof this.eccurvename != "undefined") { + var ec = new KJUR.crypto.ECDSA({curve: this.eccurvename}); + return ec.verifyHex(this.sHashHex, hSigVal, this.ecpubhex); + } else if (this.pubkeyAlgName == "rsaandmgf1") { + return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, hSigVal, + this.mdAlgName, + this.pssSaltLen); + } else if (this.pubkeyAlgName == "rsa") { + return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); + } else if (this.pubKey instanceof KJUR.crypto.ECDSA) { + return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); + } else if (this.pubKey instanceof KJUR.crypto.DSA) { + return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal); + } else { + throw "Signature: unsupported public key alg: " + this.pubkeyAlgName; + } + }; + } + }; + + /** + * Initialize this object for signing or verifying depends on key + * @name init + * @memberOf KJUR.crypto.Signature + * @function + * @param {Object} key specifying public or private key as plain/encrypted PKCS#5/8 PEM file, certificate PEM or {@link RSAKey}, {@link KJUR.crypto.DSA} or {@link KJUR.crypto.ECDSA} object + * @param {String} pass (OPTION) passcode for encrypted private key + * @since crypto 1.1.3 + * @description + * This method is very useful initialize method for Signature class since + * you just specify key then this method will automatically initialize it + * using {@link KEYUTIL.getKey} method. + * As for 'key', following argument type are supported: + *
signing
+ *
    + *
  • PEM formatted PKCS#8 encrypted RSA/ECDSA private key concluding "BEGIN ENCRYPTED PRIVATE KEY"
  • + *
  • PEM formatted PKCS#5 encrypted RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" and ",ENCRYPTED"
  • + *
  • PEM formatted PKCS#8 plain RSA/ECDSA private key concluding "BEGIN PRIVATE KEY"
  • + *
  • PEM formatted PKCS#5 plain RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" without ",ENCRYPTED"
  • + *
  • RSAKey object of private key
  • + *
  • KJUR.crypto.ECDSA object of private key
  • + *
  • KJUR.crypto.DSA object of private key
  • + *
+ *
verification
+ *
    + *
  • PEM formatted PKCS#8 RSA/EC/DSA public key concluding "BEGIN PUBLIC KEY"
  • + *
  • PEM formatted X.509 certificate with RSA/EC/DSA public key concluding + * "BEGIN CERTIFICATE", "BEGIN X509 CERTIFICATE" or "BEGIN TRUSTED CERTIFICATE".
  • + *
  • RSAKey object of public key
  • + *
  • KJUR.crypto.ECDSA object of public key
  • + *
  • KJUR.crypto.DSA object of public key
  • + *
+ * @example + * sig.init(sCertPEM) + */ + this.init = function(key, pass) { + throw "init(key, pass) not supported for this alg:prov=" + + this.algProvName; + }; + + /** + * Initialize this object for verifying with a public key + * @name initVerifyByPublicKey + * @memberOf KJUR.crypto.Signature + * @function + * @param {Object} param RSAKey object of public key or associative array for ECDSA + * @since 1.0.2 + * @deprecated from crypto 1.1.5. please use init() method instead. + * @description + * Public key information will be provided as 'param' parameter and the value will be + * following: + *
    + *
  • {@link RSAKey} object for RSA verification
  • + *
  • associative array for ECDSA verification + * (ex. {'ecpubhex': '041f..', 'eccurvename': 'secp256r1'}) + *
  • + *
+ * @example + * sig.initVerifyByPublicKey(rsaPrvKey) + */ + this.initVerifyByPublicKey = function(rsaPubKey) { + throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" + + this.algProvName; + }; + + /** + * Initialize this object for verifying with a certficate + * @name initVerifyByCertificatePEM + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} certPEM PEM formatted string of certificate + * @since 1.0.2 + * @deprecated from crypto 1.1.5. please use init() method instead. + * @description + * @example + * sig.initVerifyByCertificatePEM(certPEM) + */ + this.initVerifyByCertificatePEM = function(certPEM) { + throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" + + this.algProvName; + }; + + /** + * Initialize this object for signing + * @name initSign + * @memberOf KJUR.crypto.Signature + * @function + * @param {Object} param RSAKey object of public key or associative array for ECDSA + * @deprecated from crypto 1.1.5. please use init() method instead. + * @description + * Private key information will be provided as 'param' parameter and the value will be + * following: + *
    + *
  • {@link RSAKey} object for RSA signing
  • + *
  • associative array for ECDSA signing + * (ex. {'ecprvhex': '1d3f..', 'eccurvename': 'secp256r1'})
  • + *
+ * @example + * sig.initSign(prvKey) + */ + this.initSign = function(prvKey) { + throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName; + }; + + /** + * Updates the data to be signed or verified by a string + * @name updateString + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} str string to use for the update + * @description + * @example + * sig.updateString('aaa') + */ + this.updateString = function(str) { + throw "updateString(str) not supported for this alg:prov=" + this.algProvName; + }; + + /** + * Updates the data to be signed or verified by a hexadecimal string + * @name updateHex + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} hex hexadecimal string to use for the update + * @description + * @example + * sig.updateHex('1f2f3f') + */ + this.updateHex = function(hex) { + throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName; + }; + + /** + * Returns the signature bytes of all data updates as a hexadecimal string + * @name sign + * @memberOf KJUR.crypto.Signature + * @function + * @return the signature bytes as a hexadecimal string + * @description + * @example + * var hSigValue = sig.sign() + */ + this.sign = function() { + throw "sign() not supported for this alg:prov=" + this.algProvName; + }; + + /** + * performs final update on the sign using string, then returns the signature bytes of all data updates as a hexadecimal string + * @name signString + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} str string to final update + * @return the signature bytes of a hexadecimal string + * @description + * @example + * var hSigValue = sig.signString('aaa') + */ + this.signString = function(str) { + throw "digestString(str) not supported for this alg:prov=" + this.algProvName; + }; + + /** + * performs final update on the sign using hexadecimal string, then returns the signature bytes of all data updates as a hexadecimal string + * @name signHex + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} hex hexadecimal string to final update + * @return the signature bytes of a hexadecimal string + * @description + * @example + * var hSigValue = sig.signHex('1fdc33') + */ + this.signHex = function(hex) { + throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName; + }; + + /** + * verifies the passed-in signature. + * @name verify + * @memberOf KJUR.crypto.Signature + * @function + * @param {String} str string to final update + * @return {Boolean} true if the signature was verified, otherwise false + * @description + * @example + * var isValid = sig.verify('1fbcefdca4823a7(snip)') + */ + this.verify = function(hSigVal) { + throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName; + }; + + this.initParams = params; + + if (params !== undefined) { + if (params['alg'] !== undefined) { + this.algName = params['alg']; + if (params['prov'] === undefined) { + this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]; + } else { + this.provName = params['prov']; + } + this.algProvName = this.algName + ":" + this.provName; + this.setAlgAndProvider(this.algName, this.provName); + this._setAlgNames(); + } + + if (params['psssaltlen'] !== undefined) this.pssSaltLen = params['psssaltlen']; + + if (params['prvkeypem'] !== undefined) { + if (params['prvkeypas'] !== undefined) { + throw "both prvkeypem and prvkeypas parameters not supported"; + } else { + try { + var prvKey = new RSAKey(); + prvKey.readPrivateKeyFromPEMString(params['prvkeypem']); + this.initSign(prvKey); + } catch (ex) { + throw "fatal error to load pem private key: " + ex; + } + } + } + } +}; + +/** + * static object for cryptographic function utilities + * @name KJUR.crypto.OID + * @class static object for cryptography related OIDs + * @property {Array} oidhex2name key value of hexadecimal OID and its name + * (ex. '2a8648ce3d030107' and 'secp256r1') + * @since crypto 1.1.3 + * @description + */ + + +KJUR.crypto.OID = new function() { + this.oidhex2name = { + '2a864886f70d010101': 'rsaEncryption', + '2a8648ce3d0201': 'ecPublicKey', + '2a8648ce380401': 'dsa', + '2a8648ce3d030107': 'secp256r1', + '2b8104001f': 'secp192k1', + '2b81040021': 'secp224r1', + '2b8104000a': 'secp256k1', + '2b81040023': 'secp521r1', + '2b81040022': 'secp384r1', + '2a8648ce380403': 'SHA1withDSA', // 1.2.840.10040.4.3 + '608648016503040301': 'SHA224withDSA', // 2.16.840.1.101.3.4.3.1 + '608648016503040302': 'SHA256withDSA', // 2.16.840.1.101.3.4.3.2 + }; +}; + +/*! base64x-1.1.3 (c) 2012-2014 Kenji Urushima | kjur.github.com/jsjws/license + */ +/* + * base64x.js - Base64url and supplementary functions for Tom Wu's base64.js library + * + * version: 1.1.3 (2014 May 25) + * + * Copyright (c) 2012-2014 Kenji Urushima (kenji.urushima@gmail.com) + * + * This software is licensed under the terms of the MIT License. + * http://kjur.github.com/jsjws/license/ + * + * The above copyright and license notice shall be + * included in all copies or substantial portions of the Software. + * + * DEPENDS ON: + * - base64.js - Tom Wu's Base64 library + */ + +/** + * Base64URL and supplementary functions for Tom Wu's base64.js library.
+ * This class is just provide information about global functions + * defined in 'base64x.js'. The 'base64x.js' script file provides + * global functions for converting following data each other. + *
    + *
  • (ASCII) String
  • + *
  • UTF8 String including CJK, Latin and other characters
  • + *
  • byte array
  • + *
  • hexadecimal encoded String
  • + *
  • Full URIComponent encoded String (such like "%69%94")
  • + *
  • Base64 encoded String
  • + *
  • Base64URL encoded String
  • + *
+ * All functions in 'base64x.js' are defined in {@link _global_} and not + * in this class. + * + * @class Base64URL and supplementary functions for Tom Wu's base64.js library + * @author Kenji Urushima + * @version 1.1 (07 May 2012) + * @requires base64.js + * @see 'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/ + * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/ + */ +function Base64x() { +} + +// ==== string / byte array ================================ +/** + * convert a string to an array of character codes + * @param {String} s + * @return {Array of Numbers} + */ +function stoBA(s) { + var a = new Array(); + for (var i = 0; i < s.length; i++) { + a[i] = s.charCodeAt(i); + } + return a; +} + +/** + * convert an array of character codes to a string + * @param {Array of Numbers} a array of character codes + * @return {String} s + */ +function BAtos(a) { + var s = ""; + for (var i = 0; i < a.length; i++) { + s = s + String.fromCharCode(a[i]); + } + return s; +} + +// ==== byte array / hex ================================ +/** + * convert an array of bytes(Number) to hexadecimal string.
+ * @param {Array of Numbers} a array of bytes + * @return {String} hexadecimal string + */ +function BAtohex(a) { + var s = ""; + for (var i = 0; i < a.length; i++) { + var hex1 = a[i].toString(16); + if (hex1.length == 1) hex1 = "0" + hex1; + s = s + hex1; + } + return s; +} + +// ==== string / hex ================================ +/** + * convert a ASCII string to a hexadecimal string of ASCII codes.
+ * NOTE: This can't be used for non ASCII characters. + * @param {s} s ASCII string + * @return {String} hexadecimal string + */ +function stohex(s) { + return BAtohex(stoBA(s)); +} + +// ==== string / base64 ================================ +/** + * convert a ASCII string to a Base64 encoded string.
+ * NOTE: This can't be used for non ASCII characters. + * @param {s} s ASCII string + * @return {String} Base64 encoded string + */ +function stob64(s) { + return hex2b64(stohex(s)); +} + +// ==== string / base64url ================================ +/** + * convert a ASCII string to a Base64URL encoded string.
+ * NOTE: This can't be used for non ASCII characters. + * @param {s} s ASCII string + * @return {String} Base64URL encoded string + */ +function stob64u(s) { + return b64tob64u(hex2b64(stohex(s))); +} + +/** + * convert a Base64URL encoded string to a ASCII string.
+ * NOTE: This can't be used for Base64URL encoded non ASCII characters. + * @param {s} s Base64URL encoded string + * @return {String} ASCII string + */ +function b64utos(s) { + return BAtos(b64toBA(b64utob64(s))); +} + +// ==== base64 / base64url ================================ +/** + * convert a Base64 encoded string to a Base64URL encoded string.
+ * Example: "ab+c3f/==" → "ab-c3f_" + * @param {String} s Base64 encoded string + * @return {String} Base64URL encoded string + */ +function b64tob64u(s) { + s = s.replace(/\=/g, ""); + s = s.replace(/\+/g, "-"); + s = s.replace(/\//g, "_"); + return s; +} + +/** + * convert a Base64URL encoded string to a Base64 encoded string.
+ * Example: "ab-c3f_" → "ab+c3f/==" + * @param {String} s Base64URL encoded string + * @return {String} Base64 encoded string + */ +function b64utob64(s) { + if (s.length % 4 == 2) s = s + "=="; + else if (s.length % 4 == 3) s = s + "="; + s = s.replace(/-/g, "+"); + s = s.replace(/_/g, "/"); + return s; +} + +// ==== hex / base64url ================================ +/** + * convert a hexadecimal string to a Base64URL encoded string.
+ * @param {String} s hexadecimal string + * @return {String} Base64URL encoded string + */ +function hextob64u(s) { + return b64tob64u(hex2b64(s)); +} + +/** + * convert a Base64URL encoded string to a hexadecimal string.
+ * @param {String} s Base64URL encoded string + * @return {String} hexadecimal string + */ +function b64utohex(s) { + return b64tohex(b64utob64(s)); +} + +var utf8tob64u, b64utoutf8; + +if (typeof Buffer === 'function') +{ + utf8tob64u = function (s) + { + return b64tob64u(new Buffer(s, 'utf8').toString('base64')); + }; + + b64utoutf8 = function (s) + { + return new Buffer(b64utob64(s), 'base64').toString('utf8'); + }; +} +else +{ +// ==== utf8 / base64url ================================ +/** + * convert a UTF-8 encoded string including CJK or Latin to a Base64URL encoded string.
+ * @param {String} s UTF-8 encoded string + * @return {String} Base64URL encoded string + * @since 1.1 + */ + utf8tob64u = function (s) + { + return hextob64u(uricmptohex(encodeURIComponentAll(s))); + }; + +/** + * convert a Base64URL encoded string to a UTF-8 encoded string including CJK or Latin.
+ * @param {String} s Base64URL encoded string + * @return {String} UTF-8 encoded string + * @since 1.1 + */ + b64utoutf8 = function (s) + { + return decodeURIComponent(hextouricmp(b64utohex(s))); + }; +} + +// ==== utf8 / base64url ================================ +/** + * convert a UTF-8 encoded string including CJK or Latin to a Base64 encoded string.
+ * @param {String} s UTF-8 encoded string + * @return {String} Base64 encoded string + * @since 1.1.1 + */ +function utf8tob64(s) { + return hex2b64(uricmptohex(encodeURIComponentAll(s))); +} + +/** + * convert a Base64 encoded string to a UTF-8 encoded string including CJK or Latin.
+ * @param {String} s Base64 encoded string + * @return {String} UTF-8 encoded string + * @since 1.1.1 + */ +function b64toutf8(s) { + return decodeURIComponent(hextouricmp(b64tohex(s))); +} + +// ==== utf8 / hex ================================ +/** + * convert a UTF-8 encoded string including CJK or Latin to a hexadecimal encoded string.
+ * @param {String} s UTF-8 encoded string + * @return {String} hexadecimal encoded string + * @since 1.1.1 + */ +function utf8tohex(s) { + return uricmptohex(encodeURIComponentAll(s)); +} + +/** + * convert a hexadecimal encoded string to a UTF-8 encoded string including CJK or Latin.
+ * Note that when input is improper hexadecimal string as UTF-8 string, this function returns + * 'null'. + * @param {String} s hexadecimal encoded string + * @return {String} UTF-8 encoded string or null + * @since 1.1.1 + */ +function hextoutf8(s) { + return decodeURIComponent(hextouricmp(s)); +} + +/** + * convert a hexadecimal encoded string to raw string including non printable characters.
+ * @param {String} s hexadecimal encoded string + * @return {String} raw string + * @since 1.1.2 + * @example + * hextorstr("610061") → "a\x00a" + */ +function hextorstr(sHex) { + var s = ""; + for (var i = 0; i < sHex.length - 1; i += 2) { + s += String.fromCharCode(parseInt(sHex.substr(i, 2), 16)); + } + return s; +} + +/** + * convert a raw string including non printable characters to hexadecimal encoded string.
+ * @param {String} s raw string + * @return {String} hexadecimal encoded string + * @since 1.1.2 + * @example + * rstrtohex("a\x00a") → "610061" + */ +function rstrtohex(s) { + var result = ""; + for (var i = 0; i < s.length; i++) { + result += ("0" + s.charCodeAt(i).toString(16)).slice(-2); + } + return result; +} + +// ==== hex / b64nl ======================================= + +/* + * since base64x 1.1.3 + */ +function hextob64(s) { + return hex2b64(s); +} + +/* + * since base64x 1.1.3 + */ +function hextob64nl(s) { + var b64 = hextob64(s); + var b64nl = b64.replace(/(.{64})/g, "$1\r\n"); + b64nl = b64nl.replace(/\r\n$/, ''); + return b64nl; +} + +/* + * since base64x 1.1.3 + */ +function b64nltohex(s) { + var b64 = s.replace(/[^0-9A-Za-z\/+=]*/g, ''); + var hex = b64tohex(b64); + return hex; +} + +// ==== URIComponent / hex ================================ +/** + * convert a URLComponent string such like "%67%68" to a hexadecimal string.
+ * @param {String} s URIComponent string such like "%67%68" + * @return {String} hexadecimal string + * @since 1.1 + */ +function uricmptohex(s) { + return s.replace(/%/g, ""); +} + +/** + * convert a hexadecimal string to a URLComponent string such like "%67%68".
+ * @param {String} s hexadecimal string + * @return {String} URIComponent string such like "%67%68" + * @since 1.1 + */ +function hextouricmp(s) { + return s.replace(/(..)/g, "%$1"); +} + +// ==== URIComponent ================================ +/** + * convert UTFa hexadecimal string to a URLComponent string such like "%67%68".
+ * Note that these "0-9A-Za-z!'()*-._~" characters will not + * converted to "%xx" format by builtin 'encodeURIComponent()' function. + * However this 'encodeURIComponentAll()' function will convert + * all of characters into "%xx" format. + * @param {String} s hexadecimal string + * @return {String} URIComponent string such like "%67%68" + * @since 1.1 + */ +function encodeURIComponentAll(u8) { + var s = encodeURIComponent(u8); + var s2 = ""; + for (var i = 0; i < s.length; i++) { + if (s[i] == "%") { + s2 = s2 + s.substr(i, 3); + i = i + 2; + } else { + s2 = s2 + "%" + stohex(s[i]); + } + } + return s2; +} + +// ==== new lines ================================ +/** + * convert all DOS new line("\r\n") to UNIX new line("\n") in + * a String "s". + * @param {String} s string + * @return {String} converted string + */ +function newline_toUnix(s) { + s = s.replace(/\r\n/mg, "\n"); + return s; +} + +/** + * convert all UNIX new line("\r\n") to DOS new line("\n") in + * a String "s". + * @param {String} s string + * @return {String} converted string + */ +function newline_toDos(s) { + s = s.replace(/\r\n/mg, "\n"); + s = s.replace(/\n/mg, "\r\n"); + return s; +} + +/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval + */ +// This source code is free for use in the public domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// http://code.google.com/p/json-sans-eval/ + +/** + * Parses a string of well-formed JSON text. + * + * If the input is not well-formed, then behavior is undefined, but it is + * deterministic and is guaranteed not to modify any object other than its + * return value. + * + * This does not use `eval` so is less likely to have obscure security bugs than + * json2.js. + * It is optimized for speed, so is much faster than json_parse.js. + * + * This library should be used whenever security is a concern (when JSON may + * come from an untrusted source), speed is a concern, and erroring on malformed + * JSON is *not* a concern. + * + * Pros Cons + * +-----------------------+-----------------------+ + * json_sans_eval.js | Fast, secure | Not validating | + * +-----------------------+-----------------------+ + * json_parse.js | Validating, secure | Slow | + * +-----------------------+-----------------------+ + * json2.js | Fast, some validation | Potentially insecure | + * +-----------------------+-----------------------+ + * + * json2.js is very fast, but potentially insecure since it calls `eval` to + * parse JSON data, so an attacker might be able to supply strange JS that + * looks like JSON, but that executes arbitrary javascript. + * If you do have to use json2.js with untrusted data, make sure you keep + * your version of json2.js up to date so that you get patches as they're + * released. + * + * @param {string} json per RFC 4627 + * @param {function (this:Object, string, *):*} opt_reviver optional function + * that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1. + * If supplied, the function is called with a string key, and a value. + * The value is the property of 'this'. The reviver should return + * the value to use in its place. So if dates were serialized as + * {@code { "type": "Date", "time": 1234 }}, then a reviver might look like + * {@code + * function (key, value) { + * if (value && typeof value === 'object' && 'Date' === value.type) { + * return new Date(value.time); + * } else { + * return value; + * } + * }}. + * If the reviver returns {@code undefined} then the property named by key + * will be deleted from its container. + * {@code this} is bound to the object containing the specified property. + * @return {Object|Array} + * @author Mike Samuel + */ +var jsonParse = (function () { + var number + = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)'; + var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]' + + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))'; + var string = '(?:\"' + oneChar + '*\")'; + + // Will match a value in a well-formed JSON file. + // If the input is not well-formed, may match strangely, but not in an unsafe + // way. + // Since this only matches value tokens, it does not match whitespace, colons, + // or commas. + var jsonToken = new RegExp( + '(?:false|true|null|[\\{\\}\\[\\]]' + + '|' + number + + '|' + string + + ')', 'g'); + + // Matches escape sequences in a string literal + var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g'); + + // Decodes escape sequences in object literals + var escapes = { + '"': '"', + '/': '/', + '\\': '\\', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t' + }; + function unescapeOne(_, ch, hex) { + return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16)); + } + + // A non-falsy value that coerces to the empty string when used as a key. + var EMPTY_STRING = new String(''); + var SLASH = '\\'; + + // Constructor to use based on an open token. + var firstTokenCtors = { '{': Object, '[': Array }; + + var hop = Object.hasOwnProperty; + + return function (json, opt_reviver) { + // Split into tokens + var toks = json.match(jsonToken); + // Construct the object to return + var result; + var tok = toks[0]; + var topLevelPrimitive = false; + if ('{' === tok) { + result = {}; + } else if ('[' === tok) { + result = []; + } else { + // The RFC only allows arrays or objects at the top level, but the JSON.parse + // defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null + // at the top level. + result = []; + topLevelPrimitive = true; + } + + // If undefined, the key in an object key/value record to use for the next + // value parsed. + var key; + // Loop over remaining tokens maintaining a stack of uncompleted objects and + // arrays. + var stack = [result]; + for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) { + tok = toks[i]; + + var cont; + switch (tok.charCodeAt(0)) { + default: // sign or digit + cont = stack[0]; + cont[key || cont.length] = +(tok); + key = void 0; + break; + case 0x22: // '"' + tok = tok.substring(1, tok.length - 1); + if (tok.indexOf(SLASH) !== -1) { + tok = tok.replace(escapeSequence, unescapeOne); + } + cont = stack[0]; + if (!key) { + if (cont instanceof Array) { + key = cont.length; + } else { + key = tok || EMPTY_STRING; // Use as key for next value seen. + break; + } + } + cont[key] = tok; + key = void 0; + break; + case 0x5b: // '[' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = []); + key = void 0; + break; + case 0x5d: // ']' + stack.shift(); + break; + case 0x66: // 'f' + cont = stack[0]; + cont[key || cont.length] = false; + key = void 0; + break; + case 0x6e: // 'n' + cont = stack[0]; + cont[key || cont.length] = null; + key = void 0; + break; + case 0x74: // 't' + cont = stack[0]; + cont[key || cont.length] = true; + key = void 0; + break; + case 0x7b: // '{' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = {}); + key = void 0; + break; + case 0x7d: // '}' + stack.shift(); + break; + } + } + // Fail if we've got an uncompleted object. + if (topLevelPrimitive) { + if (stack.length !== 1) { throw new Error(); } + result = result[0]; + } else { + if (stack.length) { throw new Error(); } + } + + if (opt_reviver) { + // Based on walk as implemented in http://www.json.org/json2.js + var walk = function (holder, key) { + var value = holder[key]; + if (value && typeof value === 'object') { + var toDelete = null; + for (var k in value) { + if (hop.call(value, k) && value !== holder) { + // Recurse to properties first. This has the effect of causing + // the reviver to be called on the object graph depth-first. + + // Since 'this' is bound to the holder of the property, the + // reviver can access sibling properties of k including ones + // that have not yet been revived. + + // The value returned by the reviver is used in place of the + // current value of property k. + // If it returns undefined then the property is deleted. + var v = walk(value, k); + if (v !== void 0) { + value[k] = v; + } else { + // Deleting properties inside the loop has vaguely defined + // semantics in ES3 and ES3.1. + if (!toDelete) { toDelete = []; } + toDelete.push(k); + } + } + } + if (toDelete) { + for (var i = toDelete.length; --i >= 0;) { + delete value[toDelete[i]]; + } + } + } + return opt_reviver.call(holder, key, value); + }; + result = walk({ '': result }, ''); + } + + return result; + }; +})(); + +/*! jws-3.0.2 (c) 2013 Kenji Urushima | kjur.github.com/jsjws/license + */ +/* + * jws.js - JSON Web Signature Class + * + * version: 3.0.2 (2013 Sep 24) + * + * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com) + * + * This software is licensed under the terms of the MIT License. + * http://kjur.github.com/jsjws/license/ + * + * The above copyright and license notice shall be + * included in all copies or substantial portions of the Software. + */ + +/** + * @fileOverview + * @name jws-3.0.js + * @author Kenji Urushima kenji.urushima@gmail.com + * @version 3.0.1 (2013-Sep-24) + * @since jsjws 1.0 + * @license MIT License + */ + +if (typeof KJUR == "undefined" || !KJUR) KJUR = {}; +if (typeof KJUR.jws == "undefined" || !KJUR.jws) KJUR.jws = {}; + +/** + * JSON Web Signature(JWS) class.
+ * @name KJUR.jws.JWS + * @class JSON Web Signature(JWS) class + * @property {Dictionary} parsedJWS This property is set after JWS signature verification.
+ * Following "parsedJWS_*" properties can be accessed as "parsedJWS.*" because of + * JsDoc restriction. + * @property {String} parsedJWS_headB64U string of Encrypted JWS Header + * @property {String} parsedJWS_payloadB64U string of Encrypted JWS Payload + * @property {String} parsedJWS_sigvalB64U string of Encrypted JWS signature value + * @property {String} parsedJWS_si string of Signature Input + * @property {String} parsedJWS_sigvalH hexadecimal string of JWS signature value + * @property {String} parsedJWS_sigvalBI BigInteger(defined in jsbn.js) object of JWS signature value + * @property {String} parsedJWS_headS string of decoded JWS Header + * @property {String} parsedJWS_headS string of decoded JWS Payload + * @requires base64x.js, json-sans-eval.js and jsrsasign library + * @see 'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/ + * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/ + * @see IETF I-D JSON Web Algorithms (JWA) + * @since jsjws 1.0 + * @description + *

Supported Algorithms

+ * Here is supported algorithm names for {@link KJUR.jws.JWS.sign} and {@link KJUR.jws.JWS.verify} + * methods. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
alg valuespec requirementjsjws support
HS256REQUIREDSUPPORTED
HS384OPTIONALSUPPORTED
HS512OPTIONALSUPPORTED
RS256RECOMMENDEDSUPPORTED
RS384OPTIONALSUPPORTED
RS512OPTIONALSUPPORTED
ES256RECOMMENDED+SUPPORTED
ES384OPTIONALSUPPORTED
ES512OPTIONAL-
PS256OPTIONALSUPPORTED
PS384OPTIONALSUPPORTED
PS512OPTIONALSUPPORTED
noneREQUIREDSUPPORTED
+ * NOTE: HS384 is supported since jsjws 3.0.2 with jsrsasign 4.1.4. + */ +KJUR.jws.JWS = function() { + + // === utility ============================================================= + + /** + * parse JWS string and set public property 'parsedJWS' dictionary.
+ * @name parseJWS + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sJWS JWS signature string to be parsed. + * @throws if sJWS is not comma separated string such like "Header.Payload.Signature". + * @throws if JWS Header is a malformed JSON string. + * @since jws 1.1 + */ + this.parseJWS = function(sJWS, sigValNotNeeded) { + if ((this.parsedJWS !== undefined) && + (sigValNotNeeded || (this.parsedJWS.sigvalH !== undefined))) { + return; + } + if (sJWS.match(/^([^.]+)\.([^.]+)\.([^.]+)$/) == null) { + throw "JWS signature is not a form of 'Head.Payload.SigValue'."; + } + var b6Head = RegExp.$1; + var b6Payload = RegExp.$2; + var b6SigVal = RegExp.$3; + var sSI = b6Head + "." + b6Payload; + this.parsedJWS = {}; + this.parsedJWS.headB64U = b6Head; + this.parsedJWS.payloadB64U = b6Payload; + this.parsedJWS.sigvalB64U = b6SigVal; + this.parsedJWS.si = sSI; + + if (!sigValNotNeeded) { + var hSigVal = b64utohex(b6SigVal); + var biSigVal = parseBigInt(hSigVal, 16); + this.parsedJWS.sigvalH = hSigVal; + this.parsedJWS.sigvalBI = biSigVal; + } + + var sHead = b64utoutf8(b6Head); + var sPayload = b64utoutf8(b6Payload); + this.parsedJWS.headS = sHead; + this.parsedJWS.payloadS = sPayload; + + if (!KJUR.jws.JWS.isSafeJSONString(sHead, this.parsedJWS, 'headP')) + throw "malformed JSON string for JWS Head: " + sHead; + }; + + // ==== JWS Validation ========================================================= + function _getSignatureInputByString(sHead, sPayload) { + return utf8tob64u(sHead) + "." + utf8tob64u(sPayload); + }; + + function _getHashBySignatureInput(sSignatureInput, sHashAlg) { + var hashfunc = function(s) { return KJUR.crypto.Util.hashString(s, sHashAlg); }; + if (hashfunc == null) throw "hash function not defined in jsrsasign: " + sHashAlg; + return hashfunc(sSignatureInput); + }; + + function _jws_verifySignature(sHead, sPayload, hSig, hN, hE) { + var sSignatureInput = _getSignatureInputByString(sHead, sPayload); + var biSig = parseBigInt(hSig, 16); + return _rsasign_verifySignatureWithArgs(sSignatureInput, biSig, hN, hE); + }; + + /** + * verify JWS signature with naked RSA public key.
+ * This only supports "RS256" and "RS512" algorithm. + * @name verifyJWSByNE + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sJWS JWS signature string to be verified + * @param {String} hN hexadecimal string for modulus of RSA public key + * @param {String} hE hexadecimal string for public exponent of RSA public key + * @return {String} returns 1 when JWS signature is valid, otherwise returns 0 + * @throws if sJWS is not comma separated string such like "Header.Payload.Signature". + * @throws if JWS Header is a malformed JSON string. + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify} + */ + this.verifyJWSByNE = function(sJWS, hN, hE) { + this.parseJWS(sJWS); + return _rsasign_verifySignatureWithArgs(this.parsedJWS.si, this.parsedJWS.sigvalBI, hN, hE); + }; + + /** + * verify JWS signature with RSA public key.
+ * This only supports "RS256", "RS512", "PS256" and "PS512" algorithms. + * @name verifyJWSByKey + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sJWS JWS signature string to be verified + * @param {RSAKey} key RSA public key + * @return {Boolean} returns true when JWS signature is valid, otherwise returns false + * @throws if sJWS is not comma separated string such like "Header.Payload.Signature". + * @throws if JWS Header is a malformed JSON string. + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify} + */ + this.verifyJWSByKey = function(sJWS, key) { + this.parseJWS(sJWS); + var hashAlg = _jws_getHashAlgFromParsedHead(this.parsedJWS.headP); + var isPSS = this.parsedJWS.headP['alg'].substr(0, 2) == "PS"; + + if (key.hashAndVerify) { + return key.hashAndVerify(hashAlg, + new Buffer(this.parsedJWS.si, 'utf8').toString('base64'), + b64utob64(this.parsedJWS.sigvalB64U), + 'base64', + isPSS); + } else if (isPSS) { + return key.verifyStringPSS(this.parsedJWS.si, + this.parsedJWS.sigvalH, hashAlg); + } else { + return key.verifyString(this.parsedJWS.si, + this.parsedJWS.sigvalH); + } + }; + + /** + * verify JWS signature by PEM formatted X.509 certificate.
+ * This only supports "RS256" and "RS512" algorithm. + * @name verifyJWSByPemX509Cert + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sJWS JWS signature string to be verified + * @param {String} sPemX509Cert string of PEM formatted X.509 certificate + * @return {String} returns 1 when JWS signature is valid, otherwise returns 0 + * @throws if sJWS is not comma separated string such like "Header.Payload.Signature". + * @throws if JWS Header is a malformed JSON string. + * @since 1.1 + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify} + */ + this.verifyJWSByPemX509Cert = function(sJWS, sPemX509Cert) { + this.parseJWS(sJWS); + var x509 = new X509(); + x509.readCertPEM(sPemX509Cert); + return x509.subjectPublicKeyRSA.verifyString(this.parsedJWS.si, this.parsedJWS.sigvalH); + }; + + // ==== JWS Generation ========================================================= + function _jws_getHashAlgFromParsedHead(head) { + var sigAlg = head["alg"]; + var hashAlg = ""; + + if (sigAlg != "RS256" && sigAlg != "RS512" && + sigAlg != "PS256" && sigAlg != "PS512") + throw "JWS signature algorithm not supported: " + sigAlg; + if (sigAlg.substr(2) == "256") hashAlg = "sha256"; + if (sigAlg.substr(2) == "512") hashAlg = "sha512"; + return hashAlg; + }; + + function _jws_getHashAlgFromHead(sHead) { + return _jws_getHashAlgFromParsedHead(jsonParse(sHead)); + }; + + function _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD) { + var rsa = new RSAKey(); + rsa.setPrivate(hN, hE, hD); + + var hashAlg = _jws_getHashAlgFromHead(sHead); + var sigValue = rsa.signString(sSI, hashAlg); + return sigValue; + }; + + function _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, head) { + var hashAlg = null; + if (typeof head == "undefined") { + hashAlg = _jws_getHashAlgFromHead(sHead); + } else { + hashAlg = _jws_getHashAlgFromParsedHead(head); + } + + var isPSS = head['alg'].substr(0, 2) == "PS"; + + if (key.hashAndSign) { + return b64tob64u(key.hashAndSign(hashAlg, sSI, 'binary', 'base64', isPSS)); + } else if (isPSS) { + return hextob64u(key.signStringPSS(sSI, hashAlg)); + } else { + return hextob64u(key.signString(sSI, hashAlg)); + } + }; + + function _jws_generateSignatureValueByNED(sHead, sPayload, hN, hE, hD) { + var sSI = _getSignatureInputByString(sHead, sPayload); + return _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD); + }; + + /** + * generate JWS signature by Header, Payload and a naked RSA private key.
+ * This only supports "RS256" and "RS512" algorithm. + * @name generateJWSByNED + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sHead string of JWS Header + * @param {String} sPayload string of JWS Payload + * @param {String} hN hexadecimal string for modulus of RSA public key + * @param {String} hE hexadecimal string for public exponent of RSA public key + * @param {String} hD hexadecimal string for private exponent of RSA private key + * @return {String} JWS signature string + * @throws if sHead is a malformed JSON string. + * @throws if supported signature algorithm was not specified in JSON Header. + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign} + */ + this.generateJWSByNED = function(sHead, sPayload, hN, hE, hD) { + if (!KJUR.jws.JWS.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead; + var sSI = _getSignatureInputByString(sHead, sPayload); + var hSigValue = _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD); + var b64SigValue = hextob64u(hSigValue); + + this.parsedJWS = {}; + this.parsedJWS.headB64U = sSI.split(".")[0]; + this.parsedJWS.payloadB64U = sSI.split(".")[1]; + this.parsedJWS.sigvalB64U = b64SigValue; + + return sSI + "." + b64SigValue; + }; + + /** + * generate JWS signature by Header, Payload and a RSA private key.
+ * This only supports "RS256", "RS512", "PS256" and "PS512" algorithms. + * @name generateJWSByKey + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sHead string of JWS Header + * @param {String} sPayload string of JWS Payload + * @param {RSAKey} RSA private key + * @return {String} JWS signature string + * @throws if sHead is a malformed JSON string. + * @throws if supported signature algorithm was not specified in JSON Header. + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign} + */ + this.generateJWSByKey = function(sHead, sPayload, key) { + var obj = {}; + if (!KJUR.jws.JWS.isSafeJSONString(sHead, obj, 'headP')) + throw "JWS Head is not safe JSON string: " + sHead; + var sSI = _getSignatureInputByString(sHead, sPayload); + var b64SigValue = _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, obj.headP); + + this.parsedJWS = {}; + this.parsedJWS.headB64U = sSI.split(".")[0]; + this.parsedJWS.payloadB64U = sSI.split(".")[1]; + this.parsedJWS.sigvalB64U = b64SigValue; + + return sSI + "." + b64SigValue; + }; + + // === sign with PKCS#1 RSA private key ===================================================== + function _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey) { + var rsa = new RSAKey(); + rsa.readPrivateKeyFromPEMString(sPemPrvKey); + var hashAlg = _jws_getHashAlgFromHead(sHead); + var sigValue = rsa.signString(sSI, hashAlg); + return sigValue; + }; + + /** + * generate JWS signature by Header, Payload and a PEM formatted PKCS#1 RSA private key.
+ * This only supports "RS256" and "RS512" algorithm. + * @name generateJWSByP1PrvKey + * @memberOf KJUR.jws.JWS + * @function + * @param {String} sHead string of JWS Header + * @param {String} sPayload string of JWS Payload + * @param {String} string for sPemPrvKey PEM formatted PKCS#1 RSA private key
+ * Heading and trailing space characters in PEM key will be ignored. + * @return {String} JWS signature string + * @throws if sHead is a malformed JSON string. + * @throws if supported signature algorithm was not specified in JSON Header. + * @since 1.1 + * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign} + */ + this.generateJWSByP1PrvKey = function(sHead, sPayload, sPemPrvKey) { + if (!KJUR.jws.JWS.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead; + var sSI = _getSignatureInputByString(sHead, sPayload); + var hSigValue = _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey); + var b64SigValue = hextob64u(hSigValue); + + this.parsedJWS = {}; + this.parsedJWS.headB64U = sSI.split(".")[0]; + this.parsedJWS.payloadB64U = sSI.split(".")[1]; + this.parsedJWS.sigvalB64U = b64SigValue; + + return sSI + "." + b64SigValue; + }; +}; + +// === major static method ======================================================== + +/** + * generate JWS signature by specified key
+ * @name sign + * @memberOf KJUR.jws.JWS + * @function + * @static + * @param {String} alg JWS algorithm name to sign and force set to sHead or null + * @param {String} sHead string of JWS Header + * @param {String} sPayload string of JWS Payload + * @param {String} key string of private key or key object to sign + * @param {String} pass (OPTION)passcode to use encrypted private key + * @return {String} JWS signature string + * @since jws 3.0.0 + * @see jsrsasign KJUR.crypto.Signature method + * @see jsrsasign KJUR.crypto.Mac method + * @description + * This method supports following algorithms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
alg valuespec requirementjsjws support
HS256REQUIREDSUPPORTED
HS384OPTIONAL-
HS512OPTIONALSUPPORTED
RS256RECOMMENDEDSUPPORTED
RS384OPTIONALSUPPORTED
RS512OPTIONALSUPPORTED
ES256RECOMMENDED+SUPPORTED
ES384OPTIONALSUPPORTED
ES512OPTIONAL-
PS256OPTIONALSUPPORTED
PS384OPTIONALSUPPORTED
PS512OPTIONALSUPPORTED
noneREQUIREDSUPPORTED
+ *
+ *
NOTE1: + *
salt length of RSAPSS signature is the same as the hash algorithm length + * because of IETF JOSE ML discussion. + *
NOTE2: + *
The reason of HS384 unsupport is + * CryptoJS HmacSHA384 bug. + *
+ */ +KJUR.jws.JWS.sign = function(alg, sHeader, sPayload, key, pass) { + var ns1 = KJUR.jws.JWS; + + if (! ns1.isSafeJSONString(sHeader)) + throw "JWS Head is not safe JSON string: " + sHead; + + var pHeader = ns1.readSafeJSONString(sHeader); + + // 1. use alg if defined in sHeader + if ((alg == '' || alg == null) && + pHeader['alg'] !== undefined) { + alg = pHeader['alg']; + } + + // 2. set alg in sHeader if undefined + if ((alg != '' && alg != null) && + pHeader['alg'] === undefined) { + pHeader['alg'] = alg; + sHeader = JSON.stringify(pHeader); + } + + // 3. set signature algorithm like SHA1withRSA + var sigAlg = null; + if (ns1.jwsalg2sigalg[alg] === undefined) { + throw "unsupported alg name: " + alg; + } else { + sigAlg = ns1.jwsalg2sigalg[alg]; + } + + var uHeader = utf8tob64u(sHeader); + var uPayload = utf8tob64u(sPayload); + var uSignatureInput = uHeader + "." + uPayload + + // 4. sign + var hSig = ""; + if (sigAlg.substr(0, 4) == "Hmac") { + if (key === undefined) + throw "hexadecimal key shall be specified for HMAC"; + var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'pass': hextorstr(key)}); + mac.updateString(uSignatureInput); + hSig = mac.doFinal(); + } else if (sigAlg.indexOf("withECDSA") != -1) { + var sig = new KJUR.crypto.Signature({'alg': sigAlg}); + sig.init(key, pass); + sig.updateString(uSignatureInput); + hASN1Sig = sig.sign(); + hSig = KJUR.crypto.ECDSA.asn1SigToConcatSig(hASN1Sig); + } else if (sigAlg != "none") { + var sig = new KJUR.crypto.Signature({'alg': sigAlg}); + sig.init(key, pass); + sig.updateString(uSignatureInput); + hSig = sig.sign(); + } + + var uSig = hextob64u(hSig); + return uSignatureInput + "." + uSig; +}; + +/** + * verify JWS signature by specified key or certificate
+ * @name verify + * @memberOf KJUR.jws.JWS + * @function + * @static + * @param {String} sJWS string of JWS signature to verify + * @param {String} key string of public key, certificate or key object to verify + * @return {Boolean} true if the signature is valid otherwise false + * @since jws 3.0.0 + * @see jsrsasign KJUR.crypto.Signature method + * @see jsrsasign KJUR.crypto.Mac method + */ +KJUR.jws.JWS.verify = function(sJWS, key) { + var jws = KJUR.jws.JWS; + var a = sJWS.split("."); + var uHeader = a[0]; + var uPayload = a[1]; + var uSignatureInput = uHeader + "." + uPayload; + var hSig = b64utohex(a[2]); + + var pHeader = jws.readSafeJSONString(b64utoutf8(a[0])); + var alg = null; + if (pHeader.alg === undefined) { + throw "algorithm not specified in header"; + } else { + alg = pHeader.alg; + } + + var sigAlg = null; + if (jws.jwsalg2sigalg[pHeader.alg] === undefined) { + throw "unsupported alg name: " + alg; + } else { + sigAlg = jws.jwsalg2sigalg[alg]; + } + + // x. verify + if (sigAlg == "none") { + return true; + } else if (sigAlg.substr(0, 4) == "Hmac") { + if (key === undefined) + throw "hexadecimal key shall be specified for HMAC"; + var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'pass': hextorstr(key)}); + mac.updateString(uSignatureInput); + hSig2 = mac.doFinal(); + return hSig == hSig2; + } else if (sigAlg.indexOf("withECDSA") != -1) { + var hASN1Sig = null; + try { + hASN1Sig = KJUR.crypto.ECDSA.concatSigToASN1Sig(hSig); + } catch (ex) { + return false; + } + var sig = new KJUR.crypto.Signature({'alg': sigAlg}); + sig.init(key) + sig.updateString(uSignatureInput); + return sig.verify(hASN1Sig); + } else { + var sig = new KJUR.crypto.Signature({'alg': sigAlg}); + sig.init(key) + sig.updateString(uSignatureInput); + return sig.verify(hSig); + } +}; + +/* + * @since jws 3.0.0 + */ +KJUR.jws.JWS.jwsalg2sigalg = { + "HS256": "HmacSHA256", + //"HS384": "HmacSHA384", // unsupported because of CryptoJS bug + "HS512": "HmacSHA512", + "RS256": "SHA256withRSA", + "RS384": "SHA384withRSA", + "RS512": "SHA512withRSA", + "ES256": "SHA256withECDSA", + "ES384": "SHA384withECDSA", + //"ES512": "SHA512withECDSA", // unsupported because of jsrsasign's bug + "PS256": "SHA256withRSAandMGF1", + "PS384": "SHA384withRSAandMGF1", + "PS512": "SHA512withRSAandMGF1", + "none": "none", +}; + +// === utility static method ====================================================== + +/** + * check whether a String "s" is a safe JSON string or not.
+ * If a String "s" is a malformed JSON string or an other object type + * this returns 0, otherwise this returns 1. + * @name isSafeJSONString + * @memberOf KJUR.jws.JWS + * @function + * @static + * @param {String} s JSON string + * @return {Number} 1 or 0 + */ +KJUR.jws.JWS.isSafeJSONString = function(s, h, p) { + var o = null; + try { + o = jsonParse(s); + if (typeof o != "object") return 0; + if (o.constructor === Array) return 0; + if (h) h[p] = o; + return 1; + } catch (ex) { + return 0; + } +}; + +/** + * read a String "s" as JSON object if it is safe.
+ * If a String "s" is a malformed JSON string or not JSON string, + * this returns null, otherwise returns JSON object. + * @name readSafeJSONString + * @memberOf KJUR.jws.JWS + * @function + * @static + * @param {String} s JSON string + * @return {Object} JSON object or null + * @since 1.1.1 + */ +KJUR.jws.JWS.readSafeJSONString = function(s) { + var o = null; + try { + o = jsonParse(s); + if (typeof o != "object") return null; + if (o.constructor === Array) return null; + return o; + } catch (ex) { + return null; + } +}; + +/** + * get Encoed Signature Value from JWS string.
+ * @name getEncodedSignatureValueFromJWS + * @memberOf KJUR.jws.JWS + * @function + * @static + * @param {String} sJWS JWS signature string to be verified + * @return {String} string of Encoded Signature Value + * @throws if sJWS is not comma separated string such like "Header.Payload.Signature". + */ +KJUR.jws.JWS.getEncodedSignatureValueFromJWS = function(sJWS) { + if (sJWS.match(/^[^.]+\.[^.]+\.([^.]+)$/) == null) { + throw "JWS signature is not a form of 'Head.Payload.SigValue'."; + } + return RegExp.$1; +}; + +/** + * IntDate class for time representation for JSON Web Token(JWT) + * @class KJUR.jws.IntDate class + * @name KJUR.jws.IntDate + * @since jws 3.0.1 + * @description + * Utility class for IntDate which is integer representation of UNIX origin time + * used in JSON Web Token(JWT). + */ +KJUR.jws.IntDate = function() { +}; + +/** + * @name get + * @memberOf KJUR.jws.IntDate + * @function + * @static + * @param {String} s string of time representation + * @return {Integer} UNIX origin time in seconds for argument 's' + * @since jws 3.0.1 + * @throws "unsupported format: s" when malformed format + * @description + * This method will accept following representation of time. + *
    + *
  • now - current time
  • + *
  • now + 1hour - after 1 hour from now
  • + *
  • now + 1day - after 1 day from now
  • + *
  • now + 1month - after 30 days from now
  • + *
  • now + 1year - after 365 days from now
  • + *
  • YYYYmmDDHHMMSSZ - UTC time (ex. 20130828235959Z)
  • + *
  • number - UNIX origin time (seconds from 1970-01-01 00:00:00) (ex. 1377714748)
  • + *
+ */ +KJUR.jws.IntDate.get = function(s) { + if (s == "now") { + return KJUR.jws.IntDate.getNow(); + } else if (s == "now + 1hour") { + return KJUR.jws.IntDate.getNow() + 60 * 60; + } else if (s == "now + 1day") { + return KJUR.jws.IntDate.getNow() + 60 * 60 * 24; + } else if (s == "now + 1month") { + return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 30; + } else if (s == "now + 1year") { + return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 365; + } else if (s.match(/Z$/)) { + return KJUR.jws.IntDate.getZulu(s); + } else if (s.match(/^[0-9]+$/)) { + return parseInt(s); + } + throw "unsupported format: " + s; +}; + +KJUR.jws.IntDate.getZulu = function(s) { + if (a = s.match(/(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z/)) { + var year = parseInt(RegExp.$1); + var month = parseInt(RegExp.$2) - 1; + var day = parseInt(RegExp.$3); + var hour = parseInt(RegExp.$4); + var min = parseInt(RegExp.$5); + var sec = parseInt(RegExp.$6); + var d = new Date(Date.UTC(year, month, day, hour, min, sec)); + return ~~(d / 1000); + } + throw "unsupported format: " + s; +}; + +/* + * @since jws 3.0.1 + */ +KJUR.jws.IntDate.getNow = function() { + var d = ~~(new Date() / 1000); + return d; +}; + +/* + * @since jws 3.0.1 + */ +KJUR.jws.IntDate.intDate2UTCString = function(intDate) { + var d = new Date(intDate * 1000); + return d.toUTCString(); +}; + +/* + * @since jws 3.0.1 + */ +KJUR.jws.IntDate.intDate2Zulu = function(intDate) { + var d = new Date(intDate * 1000); + var year = ("0000" + d.getUTCFullYear()).slice(-4); + var mon = ("00" + (d.getUTCMonth() + 1)).slice(-2); + var day = ("00" + d.getUTCDate()).slice(-2); + var hour = ("00" + d.getUTCHours()).slice(-2); + var min = ("00" + d.getUTCMinutes()).slice(-2); + var sec = ("00" + d.getUTCSeconds()).slice(-2); + return year + mon + day + hour + min + sec + "Z"; +}; + +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE + * @version 3.0.2 + */ + +(function() { + "use strict"; + function lib$es6$promise$utils$$objectOrFunction(x) { + return typeof x === 'function' || (typeof x === 'object' && x !== null); + } + + function lib$es6$promise$utils$$isFunction(x) { + return typeof x === 'function'; + } + + function lib$es6$promise$utils$$isMaybeThenable(x) { + return typeof x === 'object' && x !== null; + } + + var lib$es6$promise$utils$$_isArray; + if (!Array.isArray) { + lib$es6$promise$utils$$_isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; + } else { + lib$es6$promise$utils$$_isArray = Array.isArray; + } + + var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; + var lib$es6$promise$asap$$len = 0; + var lib$es6$promise$asap$$toString = {}.toString; + var lib$es6$promise$asap$$vertxNext; + var lib$es6$promise$asap$$customSchedulerFn; + + var lib$es6$promise$asap$$asap = function asap(callback, arg) { + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; + lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; + lib$es6$promise$asap$$len += 2; + if (lib$es6$promise$asap$$len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (lib$es6$promise$asap$$customSchedulerFn) { + lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); + } else { + lib$es6$promise$asap$$scheduleFlush(); + } + } + } + + function lib$es6$promise$asap$$setScheduler(scheduleFn) { + lib$es6$promise$asap$$customSchedulerFn = scheduleFn; + } + + function lib$es6$promise$asap$$setAsap(asapFn) { + lib$es6$promise$asap$$asap = asapFn; + } + + var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; + var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; + var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; + var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; + + // test for web worker but not in IE10 + var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && + typeof importScripts !== 'undefined' && + typeof MessageChannel !== 'undefined'; + + // node + function lib$es6$promise$asap$$useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function() { + process.nextTick(lib$es6$promise$asap$$flush); + }; + } + + // vertx + function lib$es6$promise$asap$$useVertxTimer() { + return function() { + lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); + }; + } + + function lib$es6$promise$asap$$useMutationObserver() { + var iterations = 0; + var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function() { + node.data = (iterations = ++iterations % 2); + }; + } + + // web worker + function lib$es6$promise$asap$$useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = lib$es6$promise$asap$$flush; + return function () { + channel.port2.postMessage(0); + }; + } + + function lib$es6$promise$asap$$useSetTimeout() { + return function() { + setTimeout(lib$es6$promise$asap$$flush, 1); + }; + } + + var lib$es6$promise$asap$$queue = new Array(1000); + function lib$es6$promise$asap$$flush() { + for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { + var callback = lib$es6$promise$asap$$queue[i]; + var arg = lib$es6$promise$asap$$queue[i+1]; + + callback(arg); + + lib$es6$promise$asap$$queue[i] = undefined; + lib$es6$promise$asap$$queue[i+1] = undefined; + } + + lib$es6$promise$asap$$len = 0; + } + + function lib$es6$promise$asap$$attemptVertx() { + try { + var r = require; + var vertx = r('vertx'); + lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; + return lib$es6$promise$asap$$useVertxTimer(); + } catch(e) { + return lib$es6$promise$asap$$useSetTimeout(); + } + } + + var lib$es6$promise$asap$$scheduleFlush; + // Decide what async method to use to triggering processing of queued callbacks: + if (lib$es6$promise$asap$$isNode) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); + } else if (lib$es6$promise$asap$$BrowserMutationObserver) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); + } else if (lib$es6$promise$asap$$isWorker) { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); + } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertx(); + } else { + lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); + } + + function lib$es6$promise$$internal$$noop() {} + + var lib$es6$promise$$internal$$PENDING = void 0; + var lib$es6$promise$$internal$$FULFILLED = 1; + var lib$es6$promise$$internal$$REJECTED = 2; + + var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); + } + + function lib$es6$promise$$internal$$cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); + } + + function lib$es6$promise$$internal$$getThen(promise) { + try { + return promise.then; + } catch(error) { + lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; + return lib$es6$promise$$internal$$GET_THEN_ERROR; + } + } + + function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch(e) { + return e; + } + } + + function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { + lib$es6$promise$asap$$asap(function(promise) { + var sealed = false; + var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { + if (sealed) { return; } + sealed = true; + if (thenable !== value) { + lib$es6$promise$$internal$$resolve(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + }, function(reason) { + if (sealed) { return; } + sealed = true; + + lib$es6$promise$$internal$$reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + lib$es6$promise$$internal$$reject(promise, error); + } + }, promise); + } + + function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { + if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, thenable._result); + } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, thenable._result); + } else { + lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { + lib$es6$promise$$internal$$resolve(promise, value); + }, function(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } + } + + function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) { + if (maybeThenable.constructor === promise.constructor) { + lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); + } else { + var then = lib$es6$promise$$internal$$getThen(maybeThenable); + + if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); + } else if (then === undefined) { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } else if (lib$es6$promise$utils$$isFunction(then)) { + lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); + } else { + lib$es6$promise$$internal$$fulfill(promise, maybeThenable); + } + } + } + + function lib$es6$promise$$internal$$resolve(promise, value) { + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFulfillment()); + } else if (lib$es6$promise$utils$$objectOrFunction(value)) { + lib$es6$promise$$internal$$handleMaybeThenable(promise, value); + } else { + lib$es6$promise$$internal$$fulfill(promise, value); + } + } + + function lib$es6$promise$$internal$$publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + lib$es6$promise$$internal$$publish(promise); + } + + function lib$es6$promise$$internal$$fulfill(promise, value) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + + promise._result = value; + promise._state = lib$es6$promise$$internal$$FULFILLED; + + if (promise._subscribers.length !== 0) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); + } + } + + function lib$es6$promise$$internal$$reject(promise, reason) { + if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } + promise._state = lib$es6$promise$$internal$$REJECTED; + promise._result = reason; + + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); + } + + function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + parent._onerror = null; + + subscribers[length] = child; + subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; + subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; + + if (length === 0 && parent._state) { + lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); + } + } + + function lib$es6$promise$$internal$$publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { return; } + + var child, callback, detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; + } + + function lib$es6$promise$$internal$$ErrorObject() { + this.error = null; + } + + var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); + + function lib$es6$promise$$internal$$tryCatch(callback, detail) { + try { + return callback(detail); + } catch(e) { + lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; + return lib$es6$promise$$internal$$TRY_CATCH_ERROR; + } + } + + function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { + var hasCallback = lib$es6$promise$utils$$isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + value = lib$es6$promise$$internal$$tryCatch(callback, detail); + + if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); + return; + } + + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== lib$es6$promise$$internal$$PENDING) { + // noop + } else if (hasCallback && succeeded) { + lib$es6$promise$$internal$$resolve(promise, value); + } else if (failed) { + lib$es6$promise$$internal$$reject(promise, error); + } else if (settled === lib$es6$promise$$internal$$FULFILLED) { + lib$es6$promise$$internal$$fulfill(promise, value); + } else if (settled === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } + } + + function lib$es6$promise$$internal$$initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value){ + lib$es6$promise$$internal$$resolve(promise, value); + }, function rejectPromise(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + }); + } catch(e) { + lib$es6$promise$$internal$$reject(promise, e); + } + } + + function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { + var enumerator = this; + + enumerator._instanceConstructor = Constructor; + enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (enumerator._validateInput(input)) { + enumerator._input = input; + enumerator.length = input.length; + enumerator._remaining = input.length; + + enumerator._init(); + + if (enumerator.length === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } else { + enumerator.length = enumerator.length || 0; + enumerator._enumerate(); + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); + } + } + } else { + lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); + } + } + + lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { + return lib$es6$promise$utils$$isArray(input); + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { + return new Error('Array Methods must be provided an Array'); + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { + this._result = new Array(this.length); + }; + + var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; + + lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { + var enumerator = this; + + var length = enumerator.length; + var promise = enumerator.promise; + var input = enumerator._input; + + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + enumerator._eachEntry(input[i], i); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { + var enumerator = this; + var c = enumerator._instanceConstructor; + + if (lib$es6$promise$utils$$isMaybeThenable(entry)) { + if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) { + entry._onerror = null; + enumerator._settledAt(entry._state, i, entry._result); + } else { + enumerator._willSettleAt(c.resolve(entry), i); + } + } else { + enumerator._remaining--; + enumerator._result[i] = entry; + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { + var enumerator = this; + var promise = enumerator.promise; + + if (promise._state === lib$es6$promise$$internal$$PENDING) { + enumerator._remaining--; + + if (state === lib$es6$promise$$internal$$REJECTED) { + lib$es6$promise$$internal$$reject(promise, value); + } else { + enumerator._result[i] = value; + } + } + + if (enumerator._remaining === 0) { + lib$es6$promise$$internal$$fulfill(promise, enumerator._result); + } + }; + + lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { + var enumerator = this; + + lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { + enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); + }, function(reason) { + enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); + }); + }; + function lib$es6$promise$promise$all$$all(entries) { + return new lib$es6$promise$enumerator$$default(this, entries).promise; + } + var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; + function lib$es6$promise$promise$race$$race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + + if (!lib$es6$promise$utils$$isArray(entries)) { + lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); + return promise; + } + + var length = entries.length; + + function onFulfillment(value) { + lib$es6$promise$$internal$$resolve(promise, value); + } + + function onRejection(reason) { + lib$es6$promise$$internal$$reject(promise, reason); + } + + for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { + lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); + } + + return promise; + } + var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; + function lib$es6$promise$promise$resolve$$resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$resolve(promise, object); + return promise; + } + var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; + function lib$es6$promise$promise$reject$$reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(lib$es6$promise$$internal$$noop); + lib$es6$promise$$internal$$reject(promise, reason); + return promise; + } + var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; + + var lib$es6$promise$promise$$counter = 0; + + function lib$es6$promise$promise$$needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } + + function lib$es6$promise$promise$$needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor + */ + function lib$es6$promise$promise$$Promise(resolver) { + this._id = lib$es6$promise$promise$$counter++; + this._state = undefined; + this._result = undefined; + this._subscribers = []; + + if (lib$es6$promise$$internal$$noop !== resolver) { + if (!lib$es6$promise$utils$$isFunction(resolver)) { + lib$es6$promise$promise$$needsResolver(); + } + + if (!(this instanceof lib$es6$promise$promise$$Promise)) { + lib$es6$promise$promise$$needsNew(); + } + + lib$es6$promise$$internal$$initializePromise(this, resolver); + } + } + + lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; + lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; + lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; + lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; + lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; + lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap; + lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap; + + lib$es6$promise$promise$$Promise.prototype = { + constructor: lib$es6$promise$promise$$Promise, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + var result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + var author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection) { + var parent = this; + var state = parent._state; + + if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { + return this; + } + + var child = new this.constructor(lib$es6$promise$$internal$$noop); + var result = parent._result; + + if (state) { + var callback = arguments[state - 1]; + lib$es6$promise$asap$$asap(function(){ + lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); + }); + } else { + lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); + } + + return child; + }, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection) { + return this.then(null, onRejection); + } + }; + function lib$es6$promise$polyfill$$polyfill() { + var local; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { + return; + } + + local.Promise = lib$es6$promise$promise$$default; + } + var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; + + var lib$es6$promise$umd$$ES6Promise = { + 'Promise': lib$es6$promise$promise$$default, + 'polyfill': lib$es6$promise$polyfill$$default + }; + + /* global define:true module:true window: true */ + if (typeof define === 'function' && define['amd']) { + define(function() { return lib$es6$promise$umd$$ES6Promise; }); + } else if (typeof module !== 'undefined' && module['exports']) { + module['exports'] = lib$es6$promise$umd$$ES6Promise; + } else if (typeof this !== 'undefined') { + this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; + } + + lib$es6$promise$polyfill$$default(); +}).call(this); + + +/** + * @constructor + */ +function DefaultHttpRequest() { + + /** + * @name _promiseFactory + * @type DefaultPromiseFactory + */ + + /** + * @param {XMLHttpRequest} xhr + * @param {object.} headers + */ + function setHeaders(xhr, headers) { + var keys = Object.keys(headers); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = headers[key]; + + xhr.setRequestHeader(key, value); + } + } + + /** + * @param {string} url + * @param {{ headers: object. }} [config] + * @returns {Promise} + */ + this.getJSON = function (url, config) { + return _promiseFactory.create(function (resolve, reject) { + + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.responseType = "json"; + + if (config) { + if (config.headers) { + setHeaders(xhr, config.headers); + } + } + + xhr.onload = function () { + try { + if (xhr.status === 200) { + var response = ""; + // To support IE9 get the response from xhr.responseText not xhr.response + if (window.XDomainRequest) { + response = xhr.responseText; + } else { + response = xhr.response; + } + if (typeof response === "string") { + response = JSON.parse(response); + } + resolve(response); + } + else { + reject(Error(xhr.statusText + "(" + xhr.status + ")")); + } + } + catch (err) { + reject(err); + } + }; + + xhr.onerror = function () { + reject(Error("Network error")); + }; + + xhr.send(); + } + catch (err) { + return reject(err); + } + }); + }; +} + +_httpRequest = new DefaultHttpRequest(); + +/** + * @constructor + * @param {Promise} promise + */ +function DefaultPromise(promise) { + + /** + * @param {function(*):*} successCallback + * @param {function(*):*} errorCallback + * @returns {DefaultPromise} + */ + this.then = function (successCallback, errorCallback) { + var childPromise = promise.then(successCallback, errorCallback); + + return new DefaultPromise(childPromise); + }; + + /** + * + * @param {function(*):*} errorCallback + * @returns {DefaultPromise} + */ + this.catch = function (errorCallback) { + var childPromise = promise.catch(errorCallback); + + return new DefaultPromise(childPromise); + }; +} + +/** + * @constructor + */ +function DefaultPromiseFactory() { + + this.resolve = function (value) { + return new DefaultPromise(Promise.resolve(value)); + }; + + this.reject = function (reason) { + return new DefaultPromise(Promise.reject(reason)); + }; + + /** + * @param {function(resolve:function, reject:function)} callback + * @returns {DefaultPromise} + */ + this.create = function (callback) { + return new DefaultPromise(new Promise(callback)); + }; +} + +_promiseFactory = new DefaultPromiseFactory(); +/* + * Copyright 2015 Dominick Baier, Brock Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function log() { + //var param = [].join.call(arguments); + //console.log(param); +} + +function copy(obj, target) { + target = target || {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + target[key] = obj[key]; + } + } + return target; +} + +function rand() { + return ((Date.now() + Math.random()) * Math.random()).toString().replace(".", ""); +} + +function resolve(param) { + return _promiseFactory.resolve(param); +} + +function error(message) { + return _promiseFactory.reject(Error(message)); +} + +function parseOidcResult(queryString) { + log("parseOidcResult"); + + queryString = queryString || location.hash; + + var idx = queryString.lastIndexOf("#"); + if (idx >= 0) { + queryString = queryString.substr(idx + 1); + } + + var params = {}, + regex = /([^&=]+)=([^&]*)/g, + m; + + var counter = 0; + while (m = regex.exec(queryString)) { + params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]); + if (counter++ > 50) { + return { + error: "Response exceeded expected number of parameters" + }; + } + } + + for (var prop in params) { + return params; + } +} + +function getJson(url, token) { + log("getJson", url); + + var config = {}; + + if (token) { + config.headers = {"Authorization": "Bearer " + token}; + } + + return _httpRequest.getJSON(url, config); +} + +function OidcClient(settings) { + this._settings = settings || {}; + + if (!this._settings.request_state_key) { + this._settings.request_state_key = "OidcClient.request_state"; + } + + if (!this._settings.request_state_store) { + this._settings.request_state_store = window.localStorage; + } + + if (typeof this._settings.load_user_profile === 'undefined') { + this._settings.load_user_profile = true; + } + + if (typeof this._settings.filter_protocol_claims === 'undefined') { + this._settings.filter_protocol_claims = true; + } + + if (this._settings.authority && this._settings.authority.indexOf('.well-known/openid-configuration') < 0) { + if (this._settings.authority[this._settings.authority.length - 1] !== '/') { + this._settings.authority += '/'; + } + this._settings.authority += '.well-known/openid-configuration'; + } + + if (!this._settings.response_type) { + this._settings.response_type = "id_token token"; + } + + Object.defineProperty(this, "isOidc", { + get: function () { + if (this._settings.response_type) { + var result = this._settings.response_type.split(/\s+/g).filter(function (item) { + return item === "id_token"; + }); + return !!(result[0]); + } + return false; + } + }); + + Object.defineProperty(this, "isOAuth", { + get: function () { + if (this._settings.response_type) { + var result = this._settings.response_type.split(/\s+/g).filter(function (item) { + return item === "token"; + }); + return !!(result[0]); + } + return false; + } + }); +} + +OidcClient.parseOidcResult = parseOidcResult; + +OidcClient.prototype.loadMetadataAsync = function () { + log("OidcClient.loadMetadataAsync"); + + var settings = this._settings; + + if (settings.metadata) { + return resolve(settings.metadata); + } + + if (!settings.authority) { + return error("No authority configured"); + } + + return getJson(settings.authority) + .then(function (metadata) { + settings.metadata = metadata; + return metadata; + }, function (err) { + return error("Failed to load metadata (" + err && err.message + ")"); + }); +}; + +OidcClient.prototype.loadX509SigningKeyAsync = function () { + log("OidcClient.loadX509SigningKeyAsync"); + + var settings = this._settings; + + function getKeyAsync(jwks) { + if (!jwks.keys || !jwks.keys.length) { + return error("Signing keys empty"); + } + + var key = jwks.keys[0]; + if (key.kty !== "RSA") { + return error("Signing key not RSA"); + } + + if (!key.x5c || !key.x5c.length) { + return error("RSA keys empty"); + } + + return resolve(key.x5c[0]); + } + + if (settings.jwks) { + return getKeyAsync(settings.jwks); + } + + return this.loadMetadataAsync().then(function (metadata) { + if (!metadata.jwks_uri) { + return error("Metadata does not contain jwks_uri"); + } + + return getJson(metadata.jwks_uri).then(function (jwks) { + settings.jwks = jwks; + return getKeyAsync(jwks); + }, function (err) { + return error("Failed to load signing keys (" + err && err.message + ")"); + }); + }); +}; + +OidcClient.prototype.loadUserProfile = function (access_token) { + log("OidcClient.loadUserProfile"); + + return this.loadMetadataAsync().then(function (metadata) { + + if (!metadata.userinfo_endpoint) { + return error("Metadata does not contain userinfo_endpoint"); + } + + return getJson(metadata.userinfo_endpoint, access_token); + }); +} + +OidcClient.prototype.loadAuthorizationEndpoint = function () { + log("OidcClient.loadAuthorizationEndpoint"); + + if (this._settings.authorization_endpoint) { + return resolve(this._settings.authorization_endpoint); + } + + if (!this._settings.authority) { + return error("No authorization_endpoint configured"); + } + + return this.loadMetadataAsync().then(function (metadata) { + if (!metadata.authorization_endpoint) { + return error("Metadata does not contain authorization_endpoint"); + } + + return metadata.authorization_endpoint; + }); +}; + +OidcClient.prototype.createTokenRequestAsync = function () { + log("OidcClient.createTokenRequestAsync"); + + var client = this; + var settings = client._settings; + + return client.loadAuthorizationEndpoint().then(function (authorization_endpoint) { + + var state = rand(); + var url = authorization_endpoint + "?state=" + encodeURIComponent(state); + + if (client.isOidc) { + var nonce = rand(); + url += "&nonce=" + encodeURIComponent(nonce); + } + + var required = ["client_id", "redirect_uri", "response_type", "scope"]; + required.forEach(function (key) { + var value = settings[key]; + if (value) { + url += "&" + key + "=" + encodeURIComponent(value); + } + }); + + var optional = ["prompt", "display", "max_age", "ui_locales", "id_token_hint", "login_hint", "acr_values"]; + optional.forEach(function (key) { + var value = settings[key]; + if (value) { + url += "&" + key + "=" + encodeURIComponent(value); + } + }); + + var request_state = { + oidc: client.isOidc, + oauth: client.isOAuth, + state: state + }; + + if (nonce) { + request_state["nonce"] = nonce; + } + + settings.request_state_store.setItem(settings.request_state_key, JSON.stringify(request_state)); + + return { + request_state: request_state, + url: url + }; + }); +} + +OidcClient.prototype.createLogoutRequestAsync = function (id_token_hint) { + log("OidcClient.createLogoutRequestAsync"); + + var settings = this._settings; + return this.loadMetadataAsync().then(function (metadata) { + if (!metadata.end_session_endpoint) { + return error("No end_session_endpoint in metadata"); + } + + var url = metadata.end_session_endpoint; + if (id_token_hint && settings.post_logout_redirect_uri) { + url += "?post_logout_redirect_uri=" + encodeURIComponent(settings.post_logout_redirect_uri); + url += "&id_token_hint=" + encodeURIComponent(id_token_hint); + } + return url; + }); +} + +OidcClient.prototype.validateIdTokenAsync = function (id_token, nonce, access_token) { + log("OidcClient.validateIdTokenAsync"); + + var client = this; + var settings = client._settings; + + return client.loadX509SigningKeyAsync().then(function (cert) { + + var jws = new KJUR.jws.JWS(); + if (jws.verifyJWSByPemX509Cert(id_token, cert)) { + var id_token_contents = JSON.parse(jws.parsedJWS.payloadS); + + if (nonce !== id_token_contents.nonce) { + return error("Invalid nonce"); + } + + return client.loadMetadataAsync().then(function (metadata) { + + if (id_token_contents.iss !== metadata.issuer) { + return error("Invalid issuer"); + } + + if (id_token_contents.aud !== settings.client_id) { + return error("Invalid audience"); + } + + var now = parseInt(Date.now() / 1000); + + // accept tokens issues up to 5 mins ago + var diff = now - id_token_contents.iat; + if (diff > (5 * 60)) { + return error("Token issued too long ago"); + } + + if (id_token_contents.exp < now) { + return error("Token expired"); + } + + if (access_token && settings.load_user_profile) { + // if we have an access token, then call user info endpoint + return client.loadUserProfile(access_token, id_token_contents).then(function (profile) { + return copy(profile, id_token_contents); + }); + } + else { + // no access token, so we have all our claims + return id_token_contents; + } + + }); + } + else { + return error("JWT failed to validate"); + } + + }); + +}; + +OidcClient.prototype.validateAccessTokenAsync = function (id_token_contents, access_token) { + log("OidcClient.validateAccessTokenAsync"); + + if (!id_token_contents.at_hash) { + return error("No at_hash in id_token"); + } + + var hash = KJUR.crypto.Util.sha256(access_token); + var left = hash.substr(0, hash.length / 2); + var left_b64u = hextob64u(left); + + if (left_b64u !== id_token_contents.at_hash) { + return error("at_hash failed to validate"); + } + + return resolve(); +}; + +OidcClient.prototype.validateIdTokenAndAccessTokenAsync = function (id_token, nonce, access_token) { + log("OidcClient.validateIdTokenAndAccessTokenAsync"); + + var client = this; + + return client.validateIdTokenAsync(id_token, nonce, access_token).then(function (id_token_contents) { + + return client.validateAccessTokenAsync(id_token_contents, access_token).then(function () { + + return id_token_contents; + + }); + + }); +} + +OidcClient.prototype.processResponseAsync = function (queryString) { + log("OidcClient.processResponseAsync"); + + var client = this; + var settings = client._settings; + + var request_state = settings.request_state_store.getItem(settings.request_state_key); + settings.request_state_store.removeItem(settings.request_state_key); + + if (!request_state) { + return error("No request state loaded"); + } + + request_state = JSON.parse(request_state); + if (!request_state) { + return error("No request state loaded"); + } + + if (!request_state.state) { + return error("No state loaded"); + } + + var result = parseOidcResult(queryString); + if (!result) { + return error("No OIDC response"); + } + + if (result.error) { + return error(result.error); + } + + if (result.state !== request_state.state) { + return error("Invalid state"); + } + + if (request_state.oidc) { + if (!result.id_token) { + return error("No identity token"); + } + + if (!request_state.nonce) { + return error("No nonce loaded"); + } + } + + if (request_state.oauth) { + if (!result.access_token) { + return error("No access token"); + } + + if (!result.token_type || result.token_type.toLowerCase() !== "bearer") { + return error("Invalid token type"); + } + + if (!result.expires_in) { + return error("No token expiration"); + } + } + + var promise = resolve(); + if (request_state.oidc && request_state.oauth) { + promise = client.validateIdTokenAndAccessTokenAsync(result.id_token, request_state.nonce, result.access_token); + } + else if (request_state.oidc) { + promise = client.validateIdTokenAsync(result.id_token, request_state.nonce); + } + + return promise.then(function (profile) { + if (profile && settings.filter_protocol_claims) { + var remove = ["nonce", "at_hash", "iat", "nbf", "exp", "aud", "iss"]; + remove.forEach(function (key) { + delete profile[key]; + }); + } + + return { + profile: profile, + id_token: result.id_token, + access_token: result.access_token, + expires_in: result.expires_in, + scope: result.scope, + session_state : result.session_state + }; + }); +} + + // exports + OidcClient._promiseFactory = _promiseFactory; + OidcClient._httpRequest = _httpRequest; + window.OidcClient = OidcClient; +})(); +(function () { + +/* +* Copyright 2014-2016 Dominick Baier, Brock Allen +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// globals +var _promiseFactory = OidcClient._promiseFactory; +var _httpRequest = OidcClient._httpRequest; + +function copy(obj, target) { + target = target || {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + target[key] = obj[key]; + } + } + return target; +} + +function Token(other) { + if (other) { + this.profile = other.profile; + this.id_token = other.id_token; + this.access_token = other.access_token; + if (other.access_token) { + this.expires_at = parseInt(other.expires_at); + } + else if (other.id_token) { + this.expires_at = other.profile.exp; + } + else { + throw Error("Either access_token or id_token required."); + } + this.scope = other.scope; + this.session_state = other.session_state; + } + else { + this.expires_at = 0; + } + + Object.defineProperty(this, "scopes", { + get: function () { + return (this.scope || "").split(" "); + } + }); + + Object.defineProperty(this, "expired", { + get: function () { + var now = parseInt(Date.now() / 1000); + return this.expires_at < now; + } + }); + + Object.defineProperty(this, "expires_in", { + get: function () { + var now = parseInt(Date.now() / 1000); + return this.expires_at - now; + } + }); +} + +Token.fromResponse = function (response) { + if (response.access_token) { + var now = parseInt(Date.now() / 1000); + response.expires_at = now + parseInt(response.expires_in); + } + return new Token(response); +} + +Token.fromJSON = function (json) { + if (json) { + try { + var obj = JSON.parse(json); + return new Token(obj); + } + catch (e) { + } + } + return new Token(null); +} + +Token.prototype.toJSON = function () { + return JSON.stringify({ + profile: this.profile, + id_token: this.id_token, + access_token: this.access_token, + expires_at: this.expires_at, + scope: this.scopes.join(" "), + session_state: this.session_state + }); +} + +function FrameLoader(url, config) { + this.url = url; + config = config || {}; + config.cancelDelay = config.cancelDelay || 5000; + this.config = config; +} + +FrameLoader.prototype.loadAsync = function (url) { + var self = this; + url = url || this.url; + + if (!url) { + return _promiseFactory.reject(Error("No url provided")); + } + + return _promiseFactory.create(function (resolve, reject) { + var frame = window.document.createElement("iframe"); + frame.style.display = "none"; + + function cleanup() { + window.removeEventListener("message", message, false); + if (handle) { + window.clearTimeout(handle); + } + handle = null; + window.document.body.removeChild(frame); + } + + function cancel(e) { + cleanup(); + reject(); + } + + function message(e) { + if (handle && e.origin === location.protocol + "//" + location.host && e.source == frame.contentWindow) { + cleanup(); + resolve(e.data); + } + } + + var handle = window.setTimeout(cancel, self.config.cancelDelay); + window.addEventListener("message", message, false); + window.document.body.appendChild(frame); + frame.src = url; + }); +} + +function loadToken(mgr) { + mgr._token = null; + if (mgr._settings.persist) { + var tokenJson = mgr._settings.store.getItem(mgr._settings.persistKey); + if (tokenJson) { + var token = Token.fromJSON(tokenJson); + if (!token.expired) { + mgr._token = token; + } + } + } +} + +function configureTokenExpiring(mgr) { + + function callback() { + handle = null; + mgr._callTokenExpiring(); + } + + var handle = null; + + function cancel() { + if (handle) { + window.clearTimeout(handle); + handle = null; + } + } + + function setup(duration) { + handle = window.setTimeout(callback, duration * 1000); + } + + function configure() { + cancel(); + + if (!mgr.expired) { + var duration = mgr.expires_in; + if (duration > 60) { + setup(duration - 60); + } + else { + callback(); + } + } + } + + configure(); + + mgr.addOnTokenObtained(configure); + mgr.addOnTokenRemoved(cancel); +} + +function configureAutoRenewToken(mgr) { + + if (mgr._settings.silent_redirect_uri && mgr._settings.silent_renew) { + + mgr.addOnTokenExpiring(function () { + mgr.renewTokenSilentAsync().catch(function (e) { + mgr._callSilentTokenRenewFailed(); + console.error(e && e.message || "Unknown error"); + }); + }); + + } +} + +function configureTokenExpired(mgr) { + + function callback() { + handle = null; + + if (mgr._token) { + mgr.saveToken(null); + } + + mgr._callTokenExpired(); + } + + var handle = null; + + function cancel() { + if (handle) { + window.clearTimeout(handle); + handle = null; + } + } + + function setup(duration) { + handle = window.setTimeout(callback, duration * 1000); + } + + function configure() { + cancel(); + if (mgr.expires_in > 0) { + // register 1 second beyond expiration so we don't get into edge conditions for expiration + setup(mgr.expires_in + 1); + } + } + + configure(); + + mgr.addOnTokenObtained(configure); + mgr.addOnTokenRemoved(cancel); +} + +function TokenManager(settings) { + this._settings = settings || {}; + + if (typeof this._settings.persist === 'undefined') { + this._settings.persist = true; + } + this._settings.store = this._settings.store || window.localStorage; + this._settings.persistKey = this._settings.persistKey || "TokenManager.token"; + + this.oidcClient = new OidcClient(this._settings); + + this._callbacks = { + tokenRemovedCallbacks: [], + tokenExpiringCallbacks: [], + tokenExpiredCallbacks: [], + tokenObtainedCallbacks: [], + silentTokenRenewFailedCallbacks: [] + }; + + Object.defineProperty(this, "profile", { + get: function () { + if (this._token) { + return this._token.profile; + } + } + }); + Object.defineProperty(this, "id_token", { + get: function () { + if (this._token) { + return this._token.id_token; + } + } + }); + Object.defineProperty(this, "access_token", { + get: function () { + if (this._token && !this._token.expired) { + return this._token.access_token; + } + } + }); + Object.defineProperty(this, "expired", { + get: function () { + if (this._token) { + return this._token.expired; + } + return true; + } + }); + Object.defineProperty(this, "expires_in", { + get: function () { + if (this._token) { + return this._token.expires_in; + } + return 0; + } + }); + Object.defineProperty(this, "expires_at", { + get: function () { + if (this._token) { + return this._token.expires_at; + } + return 0; + } + }); + Object.defineProperty(this, "scope", { + get: function () { + return this._token && this._token.scope; + } + }); + Object.defineProperty(this, "scopes", { + get: function () { + if (this._token) { + return [].concat(this._token.scopes); + } + return []; + } + }); + Object.defineProperty(this, "session_state", { + get: function () { + if (this._token) { + return this._token.session_state; + } + } + }); + + var mgr = this; + loadToken(mgr); + if (mgr._settings.store instanceof window.localStorage.constructor) { + window.addEventListener("storage", function (e) { + if (e.key === mgr._settings.persistKey) { + loadToken(mgr); + + if (mgr._token) { + mgr._callTokenObtained(); + } + else { + mgr._callTokenRemoved(); + } + } + }); + } + configureTokenExpired(mgr); + configureAutoRenewToken(mgr); + + // delay this so consuming apps can register for callbacks first + window.setTimeout(function () { + configureTokenExpiring(mgr); + }, 0); +} + +/** + * @param {{ create:function(successCallback:function(), errorCallback:function()):Promise, resolve:function(value:*):Promise, reject:function():Promise}} promiseFactory + */ +TokenManager.setPromiseFactory = function (promiseFactory) { + _promiseFactory = promiseFactory; +}; + +/** + * @param {{getJSON:function(url:string, config:{ headers: object. })}} httpRequest + */ +TokenManager.setHttpRequest = function (httpRequest) { + if ((typeof httpRequest !== 'object') || (typeof httpRequest.getJSON !== 'function')) { + throw Error('The provided value is not a valid http request.'); + } + + _httpRequest = httpRequest; +}; + +TokenManager.prototype._callTokenRemoved = function () { + this._callbacks.tokenRemovedCallbacks.forEach(function (cb) { + cb(); + }); +} + +TokenManager.prototype._callTokenExpiring = function () { + this._callbacks.tokenExpiringCallbacks.forEach(function (cb) { + cb(); + }); +} + +TokenManager.prototype._callTokenExpired = function () { + this._callbacks.tokenExpiredCallbacks.forEach(function (cb) { + cb(); + }); +} + +TokenManager.prototype._callTokenObtained = function () { + this._callbacks.tokenObtainedCallbacks.forEach(function (cb) { + cb(); + }); +} + +TokenManager.prototype._callSilentTokenRenewFailed = function () { + this._callbacks.silentTokenRenewFailedCallbacks.forEach(function (cb) { + cb(); + }); +} + +TokenManager.prototype.saveToken = function (token) { + if (token && !(token instanceof Token)) { + token = Token.fromResponse(token); + } + + this._token = token; + + if (this._settings.persist && !this.expired) { + this._settings.store.setItem(this._settings.persistKey, token.toJSON()); + } + else { + this._settings.store.removeItem(this._settings.persistKey); + } + + if (token) { + this._callTokenObtained(); + } + else { + this._callTokenRemoved(); + } +} + +TokenManager.prototype.addOnTokenRemoved = function (cb) { + this._callbacks.tokenRemovedCallbacks.push(cb); +} + +TokenManager.prototype.addOnTokenObtained = function (cb) { + this._callbacks.tokenObtainedCallbacks.push(cb); +} + +TokenManager.prototype.addOnTokenExpiring = function (cb) { + this._callbacks.tokenExpiringCallbacks.push(cb); +} + +TokenManager.prototype.addOnTokenExpired = function (cb) { + this._callbacks.tokenExpiredCallbacks.push(cb); +} + +TokenManager.prototype.addOnSilentTokenRenewFailed = function (cb) { + this._callbacks.silentTokenRenewFailedCallbacks.push(cb); +} + +TokenManager.prototype.removeToken = function () { + this.saveToken(null); +} + +TokenManager.prototype.redirectForToken = function () { + var oidc = this.oidcClient; + return oidc.createTokenRequestAsync().then(function (request) { + window.location = request.url; + }, function (err) { + console.error("TokenManager.redirectForToken error: " + (err && err.message || "Unknown error")); + return _promiseFactory.reject(err); + }); +} + +TokenManager.prototype.redirectForLogout = function () { + var mgr = this; + return mgr.oidcClient.createLogoutRequestAsync(mgr.id_token).then(function (url) { + mgr.removeToken(); + window.location = url; + }, function (err) { + console.error("TokenManager.redirectForLogout error: " + (err && err.message || "Unknown error")); + return _promiseFactory.reject(err); + }); +} + +TokenManager.prototype.processTokenCallbackAsync = function (queryString) { + var mgr = this; + return mgr.oidcClient.processResponseAsync(queryString).then(function (token) { + mgr.saveToken(token); + }); +} + +TokenManager.prototype.renewTokenSilentAsync = function () { + var mgr = this; + + if (!mgr._settings.silent_redirect_uri) { + return _promiseFactory.reject(Error("silent_redirect_uri not configured")); + } + + var settings = copy(mgr._settings); + settings.redirect_uri = settings.silent_redirect_uri; + if (!settings.prompt) { + settings.prompt = "none"; + } + + var oidc = new OidcClient(settings); + return oidc.createTokenRequestAsync().then(function (request) { + var frame = new FrameLoader(request.url, { cancelDelay: mgr._settings.silent_renew_timeout }); + return frame.loadAsync().then(function (hash) { + return oidc.processResponseAsync(hash).then(function (token) { + mgr.saveToken(token); + }); + }); + }); +} + +TokenManager.prototype.processTokenCallbackSilent = function (hash) { + if (window.parent && window !== window.parent) { + var hash = hash || window.location.hash; + if (hash) { + window.parent.postMessage(hash, location.protocol + "//" + location.host); + } + } +} + +TokenManager.prototype.openPopupForTokenAsync = function (popupSettings) { + popupSettings = popupSettings || {}; + popupSettings.features = popupSettings.features || "location=no,toolbar=no"; + popupSettings.target = popupSettings.target || "_blank"; + + var callback_prefix = "tokenmgr_callback_"; + + // this is a shared callback + if (!window.openPopupForTokenAsyncCallback) { + window.openPopupForTokenAsyncCallback = function (hash) { + var result = OidcClient.parseOidcResult(hash); + if (result && result.state && window[callback_prefix + result.state]) { + window[callback_prefix + result.state](hash); + } + } + } + + var mgr = this; + var settings = copy(mgr._settings); + settings.redirect_uri = settings.popup_redirect_uri || settings.redirect_uri; + + if (mgr._pendingPopup) { + return _promiseFactory.create(function (resolve, reject) { + reject(Error("Already a pending popup token request.")); + }); + } + + var popup = window.open(settings.redirect_uri, popupSettings.target, popupSettings.features); + if (!popup) { + return _promiseFactory.create(function (resolve, reject) { + reject(Error("Error opening popup.")); + }); + } + + mgr._pendingPopup = true; + + function cleanup(name) { + if (handle) { + window.clearInterval(handle); + } + popup.close(); + delete mgr._pendingPopup; + if (name) { + delete window[name]; + } + } + + var reject_popup; + function checkClosed() { + if (!popup.window) { + cleanup(); + reject_popup(Error("Popup closed")); + } + } + var handle = window.setInterval(checkClosed, 1000); + + return _promiseFactory.create(function (resolve, reject) { + reject_popup = reject; + + var oidc = new OidcClient(settings); + oidc.createTokenRequestAsync().then(function (request) { + + var callback_name = callback_prefix + request.request_state.state; + window[callback_name] = function (hash) { + cleanup(callback_name); + + oidc.processResponseAsync(hash).then(function (token) { + mgr.saveToken(token); + resolve(); + }, function (err) { + reject(err); + }); + }; + + // give the popup 5 seconds to ready itself, otherwise fail + var seconds_to_wait = 5; + var interval = 500; + var total_times = (seconds_to_wait*1000) / interval; + var count = 0; + function redirectPopup() { + if (popup.setUrl) { + popup.setUrl(request.url); + } + else if (count < total_times) { + count++; + window.setTimeout(redirectPopup, interval); + } + else { + cleanup(callback_name); + reject(Error("Timeout error on popup")); + } + } + redirectPopup(); + }, function (err) { + cleanup(); + reject(err); + }); + }); +} + +TokenManager.prototype.processTokenPopup = function (hash) { + hash = hash || window.location.hash; + + window.setUrl = function (url) { + window.location = url; + } + + if (hash) { + window.opener.openPopupForTokenAsyncCallback(hash); + } +} + + + // exports + window.OidcTokenManager = TokenManager; +})(); diff --git a/src/Services/Basket/Basket.API/Auth/Client/popup.html b/src/Services/Basket/Basket.API/Auth/Client/popup.html new file mode 100644 index 000000000..364f8d7dd --- /dev/null +++ b/src/Services/Basket/Basket.API/Auth/Client/popup.html @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs b/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs new file mode 100644 index 000000000..15e285a28 --- /dev/null +++ b/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc.Authorization; +using Swashbuckle.Swagger.Model; +using Swashbuckle.SwaggerGen.Generator; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server +{ + public class AuthorizationHeaderParameterOperationFilter : IOperationFilter + { + public void Apply(Operation operation, OperationFilterContext context) + { + var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; + var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); + var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); + + if (isAuthorized && !allowAnonymous) + { + if (operation.Parameters == null) + operation.Parameters = new List(); + + operation.Parameters.Add(new NonBodyParameter + { + Name = "Authorization", + In = "header", + Description = "access token", + Required = true, + Type = "string" + }); + } + } + } +} diff --git a/src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs b/src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs new file mode 100644 index 000000000..b58d880d2 --- /dev/null +++ b/src/Services/Basket/Basket.API/Auth/Server/IdentitySecurityScheme.cs @@ -0,0 +1,23 @@ +using Swashbuckle.Swagger.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server +{ + public class IdentitySecurityScheme:SecurityScheme + { + public IdentitySecurityScheme() + { + Type = "IdentitySecurityScheme"; + Description = "Security definition that provides to the user of Swagger a mechanism to obtain a token from the identity service that secures the api"; + Extensions.Add("authorizationUrl", "http://localhost:5103/Auth/Client/popup.html"); + Extensions.Add("flow", "implicit"); + Extensions.Add("scopes", new List + { + "basket" + }); + } + } +} diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index ee804948e..c9b73b556 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -11,6 +11,8 @@ using Microsoft.eShopOnContainers.Services.Basket.API.Model; using StackExchange.Redis; using Microsoft.Extensions.Options; using System.Net; +using Swashbuckle.Swagger.Model; +using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -48,8 +50,11 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API }); services.AddSwaggerGen(); + //var sch = new IdentitySecurityScheme(); services.ConfigureSwaggerGen(options => { + //options.AddSecurityDefinition("IdentityServer", sch); + options.OperationFilter(); options.DescribeAllEnumsAsStrings(); options.SingleApiVersion(new Swashbuckle.Swagger.Model.Info() { @@ -79,6 +84,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); + app.UseStaticFiles(); + // Use frameworks app.UseCors("CorsPolicy"); diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 64a7cd8eb..e64f656ed 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -4,6 +4,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; + using Model; using System; using System.Linq; using System.Threading.Tasks; diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index 3349f45ab..518332cf9 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; [Route("api/v1/[controller]")] - //[Authorize] + [Authorize] public class OrdersController : Controller { private readonly IMediator _mediator; diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 680a70f6e..8ab54aa23 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -5,7 +5,6 @@ using Infrastructure; using Infrastructure.AutofacModules; using Infrastructure.Filters; - using MediatR; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -15,8 +14,6 @@ using Ordering.Infrastructure; using System; using System.Reflection; - using System.Threading; - using System.Threading.Tasks; public class Startup { @@ -64,7 +61,7 @@ Title = "Ordering HTTP API", Version = "v1", Description = "The Ordering Service HTTP API", - TermsOfService = "Terms Of Service" + TermsOfService = "Terms Of Service" }); }); diff --git a/src/Web/WebMVC/wwwroot/css/site.min.css b/src/Web/WebMVC/wwwroot/css/site.min.css index 00d9a0a72..756086398 100644 --- a/src/Web/WebMVC/wwwroot/css/site.min.css +++ b/src/Web/WebMVC/wwwroot/css/site.min.css @@ -1 +1 @@ -@font-face{font-family:Montserrat;font-weight:400;src:url("/fonts/Montserrat-Regular.eot?") format("eot"),url("/fonts/Montserrat-Regular.woff") format("woff"),url("/fonts/Montserrat-Regular.ttf") format("truetype"),url("/fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("/fonts/Montserrat-Bold.eot?") format("eot"),url("/fonts/Montserrat-Bold.woff") format("woff"),url("/fonts/Montserrat-Bold.ttf") format("truetype"),url("/fonts/Montserrat-Bold.svg#Montserrat") format("svg")}body{padding-top:80px;font-family:Montserrat,sans-serif;min-width:480px}.mt-15{margin-top:15px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.select-filter{background-color:transparent;padding:10px;margin:10px;margin-right:20px;color:#fff;padding-top:20px;padding-bottom:3px;min-width:140px;border-color:#37c7ca;max-height:43px;-webkit-appearance:none}.select-filter option{background-color:#00a69c}select::-ms-expand{display:none}.select-filter-wrapper{z-index:0;display:inline-block;margin-left:-10px}.select-filter-wrapper::before{content:attr(data-name);opacity:.5;z-index:1;text-transform:uppercase;position:absolute;font-size:10px;margin-top:15px;margin-left:21px;color:#fff}.select-filter-arrow{position:absolute;margin-left:130px;margin-top:40px}.btn-brand-small-filter{margin-top:10px;position:absolute;margin-left:15px}.carousel-caption p{font-size:20px;line-height:1.4}.layout-cart-image{height:36px;margin-top:5px}.layout-cart-badge{position:absolute;margin-top:2px;margin-left:14px;background-color:#83d01b;padding:1px;color:#fff;border-radius:50%;width:18px;height:18px;font-size:12px;cursor:pointer}.btn-bracketed:hover:before{display:inline-block;content:"[";padding-right:.5em;color:#7fff00}.btn-bracketed:hover:after{display:inline-block;content:"]";padding-left:.5em;color:#7fff00}.btn-brand{background-color:#83d01b;color:#fff;padding:10px 20px 10px 20px;border-radius:0;border:none;width:255px;display:inline-block;text-align:center;text-transform:uppercase;height:45px;font-size:16px;font-weight:normal}.btn-brand::before{content:'['}.btn-brand::after{content:']'}.btn-brand:hover:before{padding-right:5px}.btn-brand:hover:after{padding-left:5px}.btn-brand-big{width:360px;margin-top:20px}.btn-brand-small{width:45px}.btn-brand-small::before{content:''}.btn-brand-small::after{content:''}.btn-brand-small:hover:before{content:'';padding:0}.btn-brand-small:hover:after{content:'';padding:0}.btn-brand-dark{background-color:#00a69c}.btn-brand:hover{color:#fff;background-color:#83d01b;text-decoration:none}.btn-brand-dark:hover{background-color:#00a69c}.btn-cart{float:right;margin-top:40px;margin-bottom:40px}.btn-catalog-apply{padding:0}.form-label{text-transform:uppercase;font-weight:normal!important;text-align:left;margin-bottom:10px !important;color:#404040}.form-input{border-radius:0;padding:10px;height:45px}.form-input-small{max-width:100px!important}.form-select{border-radius:0;padding:10px;height:45px;width:150px}.carousel-inner .item img[src$=".svg"]{width:100%}.navbar-inverse{background-color:#fff;border-color:#fff}.btn-login{border:1px solid #00a69c;height:36px!important;margin-right:10px;margin-top:10px;background-color:#fff;color:#00a69c;text-transform:uppercase;max-width:140px;width:140px;padding-top:8px!important}.btn-login{font-weight:normal!important}.btn-login::before{content:'['}.btn-login::after{content:']'}.btn-login:hover:before{content:'[ '}.btn-login:hover:after{content:' ]'}.navbar-inverse li a{height:30px;padding:5px 20px;color:#00a69c !important}.navbar-brand{margin-top:20px;background-image:url(../images/brand.PNG);width:201px;height:44px;margin-left:0 !important}.nav>li>a{color:#fff}.nav>li>a:hover,.nav>li>a:focus{background-color:#00a69c;font-weight:bolder}.container-fluid{padding-left:0;padding-right:0}.home-banner{width:100%;margin-right:0;margin-left:0;background-image:url(../images/main_banner.png);background-size:cover;height:258px;background-position:center}.home-banner-text{margin-top:70px}.home-catalog-container{min-height:400px;margin-bottom:20px}.home-catalog-filter-container{background-color:#00a69c;height:63px}.home-catalog-filter-container li a{padding-top:5px !important}.home-catalog-filter-brands::before{content:'BRAND';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-filter-types::before{content:'TYPES';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-item{margin-top:10px;margin-bottom:10px}.home-catalog-item-image{width:100%;object-fit:cover;text-align:center}.home-catalog-item-image-addCart{background-color:#83d01b;color:#fff;display:inline-block;height:43px;padding:10px 20px 10px 20px;font-weight:bold;text-align:center;margin-top:10px;margin-left:60px;margin-right:60px;font-size:16px;font-weight:normal}.home-catalog-item-image-addCart:hover{color:#fff;text-decoration:none}.home-catalog-item-image:hover:after{cursor:pointer}.home-catalog-item-title{text-align:center;text-transform:uppercase;font-weight:300;font-size:16px;margin-top:20px}.home-catalog-item-price{text-align:center;font-weight:900;font-size:28px}.home-catalog-item-price::before{content:'$'}.home-catalog-noResults{text-align:center;margin-top:100px}.container .nav .navbar-nav .col-sm-6 ::before{content:'BRAND'}.validation-summary-errors li{list-style:none}footer{background-color:#000;height:150px;vertical-align:middle}footer .brand{margin-top:25px;background-image:url(../images/brand_dark.PNG);max-width:231px;height:52px;margin-left:0 !important}footer .text{text-align:right;width:100%;height:100%;color:#83d01b;margin-top:10px}.text{color:#83d01b}.text:hover{color:#83d01b}form .col-md-4{text-align:right}.brand-header-block{background-color:#00a69c;height:63px}.brand-header-block li{list-style:none;display:inline;opacity:.5;margin-top:25px;margin-left:10px;float:right;cursor:pointer;color:#fff}.brand-header-block li a{color:#fff}.brand-header-block li a:hover{text-decoration:none}.brand-header-block .active{opacity:1}.brand-header-block .active::before{content:'[ ';color:#adff2f}.brand-header-block .active::after{content:' ]';color:#adff2f}.brand-header-back{float:left!important;margin-top:20px!important;text-transform:uppercase}.account-login-container{min-height:70vh;text-align:center;padding-top:40px}.account-register-container{min-height:70vh;text-align:center !important;align-content:center}.cart-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px;min-width:992px}.register-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px}.order-create-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px;min-width:995px}.cart-product-column{max-width:120px;text-transform:uppercase;vertical-align:middle!important}.order-create-container .cart-product-column{max-width:130px}.cart-product-column-name{width:220px}.cart-subtotal-label{font-size:12px;color:#404040;margin-top:10px}.cart-subtotal-value{font-size:20px;color:#00a69c}.cart-total-label{font-size:14px;color:#404040;margin-top:10px}.cart-total-value{font-size:28px;color:#00a69c;text-align:left}.cart-product-image{max-width:210px}.cart-section-total{margin-bottom:5px;margin-left:175px;text-align:left}.cart-product-column input{width:70px;text-align:center}.cart-refresh-button{margin-top:0;background-image:url('../images/refresh.svg');color:#fff;font-size:8px;width:40px;height:40px;background-color:transparent;border:none;margin-top:25px;margin-left:15px}.cart-refresh-button:hover{background-color:transparent}.cart-totals{border-bottom:none!important}.input-validation-error{border:1px solid #fb0d0d}.text-danger{color:#fb0d0d;font-size:12px}.cart{border:none !important}.form-horizontal h4{margin-top:30px}.form-horizontal .form-group{margin-right:0!important}.form-control:focus{border-color:#83d01b}.form-input-center{margin:auto}.order-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-index-container .table tbody tr{border-bottom:none}.order-index-container .table tbody tr td{border-top:none;padding-top:10px;padding-bottom:10px}.order-index-container .table tbody tr:nth-child(even){background-color:#f5f5f5}.order-create-section-title{margin-left:-15px;text-transform:uppercase}.order-create-section-items{margin-left:-45px;width:102%}.order-detail-button a{color:#83d01b}.order-detail-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-detail-container .table tbody tr:first-child td{border-top:none}.order-detail-container .table tr{border-bottom:none}.order-detail-section{margin-top:50px}.order-detail-container .table{margin-left:-7px}.order-section-total{margin-bottom:5px;margin-left:20px;text-align:left}.fr{float:right!important}.down-arrow{background-image:url('../images/arrow-down.png');height:7px;width:10px;display:inline-block;margin-left:20px}.logout-icon{background-image:url('../images/logout.PNG');display:inline-block;height:19px;width:19px;margin-left:15px}.myorders-icon{background-image:url('../images/my_orders.PNG');display:inline-block;height:20px;width:20px;margin-left:15px}.login-user{position:absolute!important;top:30px;right:65px;cursor:pointer}.login-user-dropdown{position:relative;display:inline-block}.login-user-dropdown-content{display:none;position:absolute;background-color:#fff;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);right:0}.login-user-dropdown-content a{color:#000;padding:12px 16px;text-decoration:none;display:block;text-align:right;text-transform:uppercase}.login-user:hover .login-user-dropdown-content{display:block}.down-arrow:hover>.login-user-dropdown-content{display:block}.login-user-dropdown-content a:hover{color:#83d01b}.es-header{min-height:80px!important}.es-pager-bottom{margin-top:40px}.es-pager-top{margin-bottom:20px;margin-top:20px}.es-pager-top ul{list-style:none}.es-pager-bottom ul{list-style:none}.page-item{cursor:pointer}.next{position:absolute;right:15px;top:0}.previous{position:absolute;left:0;top:0}.is-disabled{cursor:not-allowed;opacity:.5;pointer-events:none}.table tr{border-bottom:1px solid #ddd}.table th{text-transform:uppercase}.navbar-nav{margin-top:10px;margin-bottom:7.5px;margin-right:-10px;float:right}@media screen and (max-width:1195px){.cart-product-column-name{display:none}}@media screen and (max-width:767px){.carousel-caption{display:none}footer .text{text-align:left;margin-top:-15px}.cart-product-column-brand{display:none}}@media screen and (min-width:992px){.form-input{width:360px;max-width:360px}} \ No newline at end of file +@font-face{font-family:Montserrat;font-weight:400;src:url("/fonts/Montserrat-Regular.eot?") format("eot"),url("/fonts/Montserrat-Regular.woff") format("woff"),url("/fonts/Montserrat-Regular.ttf") format("truetype"),url("/fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("/fonts/Montserrat-Bold.eot?") format("eot"),url("/fonts/Montserrat-Bold.woff") format("woff"),url("/fonts/Montserrat-Bold.ttf") format("truetype"),url("/fonts/Montserrat-Bold.svg#Montserrat") format("svg")}body{padding-top:80px;font-family:Montserrat,sans-serif;min-width:480px}.mt-15{margin-top:15px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.select-filter{background-color:#00a69c;padding:10px;margin:10px;margin-right:20px;color:#fff;padding-top:20px;padding-bottom:3px;min-width:140px;border-color:#37c7ca;max-height:43px;-webkit-appearance:none}.select-filter option{background-color:#00a69c}select::-ms-expand{display:none}.select-filter-wrapper{z-index:0;display:inline-block;margin-left:-10px;position:relative}.select-filter-wrapper::before{content:attr(data-name);opacity:.5;z-index:1;text-transform:uppercase;position:absolute;font-size:10px;margin-top:15px;margin-left:21px;color:#fff}.select-filter-arrow-container{position:absolute;top:15px;right:25px}.btn-brand-small-filter{margin-top:10px;position:absolute;margin-left:15px}.carousel-caption p{font-size:20px;line-height:1.4}.layout-cart-image{height:36px;margin-top:5px}.layout-cart-badge{position:absolute;margin-top:2px;margin-left:14px;background-color:#83d01b;padding:1px;color:#fff;border-radius:50%;width:18px;height:18px;font-size:12px;cursor:pointer}.btn-bracketed:hover:before{display:inline-block;content:"[";padding-right:.5em;color:#7fff00}.btn-bracketed:hover:after{display:inline-block;content:"]";padding-left:.5em;color:#7fff00}.btn-brand{background-color:#83d01b;color:#fff;padding:10px 20px 10px 20px;border-radius:0;border:none;width:255px;display:inline-block;text-align:center;text-transform:uppercase;height:45px;font-size:16px;font-weight:normal}.btn-brand::before{content:'['}.btn-brand::after{content:']'}.btn-brand:hover:before{padding-right:5px}.btn-brand:hover:after{padding-left:5px}.btn-brand-big{width:360px;margin-top:20px}.btn-brand-small{width:45px}.btn-brand-small::before{content:''}.btn-brand-small::after{content:''}.btn-brand-small:hover:before{content:'';padding:0}.btn-brand-small:hover:after{content:'';padding:0}.btn-brand-dark{background-color:#00a69c}.btn-brand:hover{color:#fff;background-color:#83d01b;text-decoration:none}.btn-brand-dark:hover{background-color:#00a69c}.btn-cart{float:right;margin-top:40px;margin-bottom:40px}.btn-catalog-apply{padding:0}.form-label{text-transform:uppercase;font-weight:normal!important;text-align:left;margin-bottom:10px !important;color:#404040}.form-input{border-radius:0;padding:10px;height:45px}.form-input-small{max-width:100px!important}.form-select{border-radius:0;padding:10px;height:45px;width:150px}.carousel-inner .item img[src$=".svg"]{width:100%}.navbar-inverse{background-color:#fff;border-color:#fff}.btn-login{border:1px solid #00a69c;height:36px!important;margin-right:10px;margin-top:10px;background-color:#fff;color:#00a69c;text-transform:uppercase;max-width:140px;width:140px;padding-top:8px!important}.btn-login{font-weight:normal!important}.btn-login::before{content:'['}.btn-login::after{content:']'}.btn-login:hover:before{content:'[ '}.btn-login:hover:after{content:' ]'}.navbar-inverse li a{height:30px;padding:5px 20px;color:#00a69c !important}.navbar-brand{margin-top:20px;background-image:url(../images/brand.PNG);width:201px;height:44px;margin-left:0 !important}.nav>li>a{color:#fff}.nav>li>a:hover,.nav>li>a:focus{background-color:#00a69c;font-weight:bolder}.container-fluid{padding-left:0;padding-right:0}.home-banner{width:100%;margin-right:0;margin-left:0;background-image:url(../images/main_banner.png);background-size:cover;height:258px;background-position:center}.home-banner-text{margin-top:70px}.home-catalog-container{min-height:400px;margin-bottom:20px}.home-catalog-filter-container{background-color:#00a69c;height:63px}.home-catalog-filter-container li a{padding-top:5px !important}.home-catalog-filter-brands::before{content:'BRAND';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-filter-types::before{content:'TYPES';color:#fff;font-size:x-small;opacity:.5;margin:10px 0 0 15px}.home-catalog-item{margin-top:10px;margin-bottom:10px}.home-catalog-item-image{width:100%;object-fit:cover;text-align:center}.home-catalog-item-image-addCart{background-color:#83d01b;color:#fff;display:inline-block;height:43px;padding:10px 20px 10px 20px;font-weight:bold;text-align:center;margin-top:10px;margin-left:60px;margin-right:60px;font-size:16px;font-weight:normal}.home-catalog-item-image-addCart:hover{color:#fff;text-decoration:none}.home-catalog-item-image:hover:after{cursor:pointer}.home-catalog-item-title{text-align:center;text-transform:uppercase;font-weight:300;font-size:16px;margin-top:20px}.home-catalog-item-price{text-align:center;font-weight:900;font-size:28px}.home-catalog-item-price::before{content:'$'}.home-catalog-noResults{text-align:center;margin-top:100px}.container .nav .navbar-nav .col-sm-6 ::before{content:'BRAND'}.validation-summary-errors li{list-style:none}footer{background-color:#000;height:150px;vertical-align:middle}footer .brand{margin-top:25px;background-image:url(../images/brand_dark.PNG);max-width:231px;height:52px;margin-left:0 !important}footer .text{text-align:right;width:100%;height:100%;color:#83d01b;margin-top:10px}.text{color:#83d01b}.text:hover{color:#83d01b}form .col-md-4{text-align:right}.brand-header-block{background-color:#00a69c;height:63px}.brand-header-block li{list-style:none;display:inline;opacity:.5;margin-top:25px;margin-left:10px;float:right;cursor:pointer;color:#fff}.brand-header-block li a{color:#fff}.brand-header-block li a:hover{text-decoration:none}.brand-header-block .active{opacity:1}.brand-header-block .active::before{content:'[ ';color:#adff2f}.brand-header-block .active::after{content:' ]';color:#adff2f}.brand-header-back{float:left!important;margin-top:20px!important;text-transform:uppercase}.account-login-container{min-height:70vh;text-align:center;padding-top:40px}.account-register-container{min-height:70vh;text-align:center !important;align-content:center}.cart-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px;min-width:992px}.register-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px}.order-create-container{min-height:70vh;padding-top:40px;margin-bottom:30px;padding-left:30px;min-width:995px}.cart-product-column{max-width:120px;text-transform:uppercase;vertical-align:middle!important}.order-create-container .cart-product-column{max-width:130px}.cart-product-column-name{width:220px}.cart-subtotal-label{font-size:12px;color:#404040;margin-top:10px}.cart-subtotal-value{font-size:20px;color:#00a69c}.cart-total-label{font-size:14px;color:#404040;margin-top:10px}.cart-total-value{font-size:28px;color:#00a69c;text-align:left}.cart-product-image{max-width:210px}.cart-section-total{margin-bottom:5px;margin-left:175px;text-align:left}.cart-product-column input{width:70px;text-align:center}.cart-refresh-button{margin-top:0;background-image:url('../images/refresh.svg');color:#fff;font-size:8px;width:40px;height:40px;background-color:transparent;border:none;margin-top:25px;margin-left:15px}.cart-refresh-button:hover{background-color:transparent}.cart-totals{border-bottom:none!important}.input-validation-error{border:1px solid #fb0d0d}.text-danger{color:#fb0d0d;font-size:12px}.cart{border:none !important}.form-horizontal h4{margin-top:30px}.form-horizontal .form-group{margin-right:0!important}.form-control:focus{border-color:#83d01b}.form-input-center{margin:auto}.order-index-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-index-container .table tbody tr{border-bottom:none}.order-index-container .table tbody tr td{border-top:none;padding-top:10px;padding-bottom:10px}.order-index-container .table tbody tr:nth-child(even){background-color:#f5f5f5}.order-create-section-title{margin-left:-15px;text-transform:uppercase}.order-create-section-items{margin-left:-45px;width:102%}.order-detail-button a{color:#83d01b}.order-detail-container{min-height:70vh;padding-top:40px;margin-bottom:30px}.order-detail-container .table tbody tr:first-child td{border-top:none}.order-detail-container .table tr{border-bottom:none}.order-detail-section{margin-top:50px}.order-detail-container .table{margin-left:-7px}.order-section-total{margin-bottom:5px;margin-left:20px;text-align:left}.fr{float:right!important}.down-arrow{background-image:url('../images/arrow-down.png');height:7px;width:10px;display:inline-block;margin-left:20px}.logout-icon{background-image:url('../images/logout.PNG');display:inline-block;height:19px;width:19px;margin-left:15px}.myorders-icon{background-image:url('../images/my_orders.PNG');display:inline-block;height:20px;width:20px;margin-left:15px}.login-user{position:absolute!important;top:30px;right:65px;cursor:pointer}.login-user-dropdown{position:relative;display:inline-block}.login-user-dropdown-content{display:none;position:absolute;background-color:#fff;min-width:160px;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);right:0}.login-user-dropdown-content a{color:#000;padding:12px 16px;text-decoration:none;display:block;text-align:right;text-transform:uppercase}.login-user:hover .login-user-dropdown-content{display:block}.down-arrow:hover>.login-user-dropdown-content{display:block}.login-user-dropdown-content a:hover{color:#83d01b}.es-header{min-height:80px!important}.es-pager-bottom{margin-top:40px}.es-pager-top{margin-bottom:20px;margin-top:20px}.es-pager-top ul{list-style:none}.es-pager-bottom ul{list-style:none}.page-item{cursor:pointer}.next{position:absolute;right:15px;top:0}.previous{position:absolute;left:0;top:0}.is-disabled{cursor:not-allowed;opacity:.5;pointer-events:none}.table tr{border-bottom:1px solid #ddd}.table th{text-transform:uppercase}.navbar-nav{margin-top:10px;margin-bottom:7.5px;margin-right:-10px;float:right}@media screen and (max-width:1195px){.cart-product-column-name{display:none}}@media screen and (max-width:767px){.carousel-caption{display:none}footer .text{text-align:left;margin-top:-15px}.cart-product-column-brand{display:none}}@media screen and (min-width:992px){.form-input{width:360px;max-width:360px}} \ No newline at end of file From ac146f44759ba08d9a2b2bfa0834719df09bfcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ca=C3=B1izares=20Est=C3=A9vez?= Date: Thu, 22 Dec 2016 13:20:12 +0100 Subject: [PATCH 02/10] Added Unit Test Project for services --- docker-compose.override.yml | 2 +- eShopOnContainers.sln | 100 ++++++------ .../Configuration/Config.cs | 3 +- .../Controllers/OrdersController.cs | 25 ++- .../Services/IIdentityService.cs | 12 ++ .../Services/IdentityService.cs | 29 ++++ src/Services/Ordering/Ordering.API/Startup.cs | 5 + .../Queries/OrderQueries.cs | 29 +++- src/Web/WebMVC/Startup.cs | 8 +- .../UnitTest/Catalog/CatalogControllertest.cs | 34 ++++ .../Application/NewOrderCommandHandlerTest.cs | 92 +++++++++++ .../Controllers/OrderControllerTest.cs | 152 ++++++++++++++++++ .../UnitTest/Properties/AssemblyInfo.cs | 19 +++ test/Services/UnitTest/UnitTest.xproj | 22 +++ test/Services/UnitTest/project.json | 24 +++ .../UnitTests/Ordering/OrderControllerTest.cs | 50 ++++++ .../UnitTests/Properties/AssemblyInfo.cs | 36 +++++ .../UnitTests__/UnitTests/UnitTests.csproj | 117 ++++++++++++++ .../UnitTests__/UnitTests/packages.config | 12 ++ 19 files changed, 696 insertions(+), 75 deletions(-) create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs create mode 100644 test/Services/UnitTest/Catalog/CatalogControllertest.cs create mode 100644 test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs create mode 100644 test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs create mode 100644 test/Services/UnitTest/Properties/AssemblyInfo.cs create mode 100644 test/Services/UnitTest/UnitTest.xproj create mode 100644 test/Services/UnitTest/project.json create mode 100644 test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs create mode 100644 test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs create mode 100644 test/Services/UnitTests__/UnitTests/UnitTests.csproj create mode 100644 test/Services/UnitTests__/UnitTests/packages.config diff --git a/docker-compose.override.yml b/docker-compose.override.yml index f8138e794..5cd817bb8 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -62,7 +62,7 @@ services: - SpaClient=http://localhost:5104 - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word #- MvcClient=http://13.88.8.119:5100 #Remote: VM Needs to have public access at 5105. - - MvcClient=http://localhost:5100 #Local: You need a entry in windows host file to run identity in local docker. + #- MvcClient=http://localhost:5100 #Local: You need a entry in windows host file to run identity in local docker. - MvcClient=http://10.0.75.1:5100 #Local: You need to open windows firewall at range 5100-5105. ports: - "5105:5105" diff --git a/eShopOnContainers.sln b/eShopOnContainers.sln index f23ec5b12..53013c1cb 100644 --- a/eShopOnContainers.sln +++ b/eShopOnContainers.sln @@ -73,7 +73,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.UITests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eShopOnContainers.Core", "src\Mobile\eShopOnContainers\eShopOnContainers.Core\eShopOnContainers.Core.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ordering.Application", "src\Services\Ordering\Ordering.Application\Ordering.Application.xproj", "{4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}" +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "UnitTest", "test\Services\UnitTest\UnitTest.xproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -908,54 +908,54 @@ Global {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x64.Build.0 = Release|Any CPU {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.ActiveCfg = Release|Any CPU {67F9D3A8-F71E-4428-913F-C37AE82CDB24}.Release|x86.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|ARM.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x64.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x64.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x86.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.AppStore|x86.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|ARM.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|ARM.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhone.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x64.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x64.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x86.ActiveCfg = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Debug|x86.Build.0 = Debug|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|Any CPU.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|ARM.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|ARM.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhone.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhone.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x64.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x64.Build.0 = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x86.ActiveCfg = Release|Any CPU - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7}.Release|x86.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|ARM.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhone.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x64.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.AppStore|x86.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|ARM.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhone.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x64.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.ActiveCfg = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Debug|x86.Build.0 = Debug|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|Any CPU.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|ARM.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhone.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x64.Build.0 = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.ActiveCfg = Release|Any CPU + {7796F5D8-31FC-45A4-B673-19DE5BA194CF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -990,6 +990,6 @@ Global {621E7211-58D0-45FD-9600-1CB490BD930E} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {E3B18084-842C-4B80-8E4A-A7E588EC3137} = {B7B1D395-4E06-4036-BE86-C216756B9367} {67F9D3A8-F71E-4428-913F-C37AE82CDB24} = {778289CA-31F7-4464-8C2A-612EE846F8A7} - {4193CAA3-A1C3-4818-A06F-A2D85FDE77E7} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} + {7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870} EndGlobalSection EndGlobal diff --git a/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs b/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs index 5a5989b56..c8a434215 100644 --- a/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs +++ b/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs @@ -82,7 +82,8 @@ namespace eShopOnContainers.Identity.Configuration RedirectUris = new List { $"{clientsUrl["Mvc"]}/signin-oidc", - "http://104.40.62.65:5100/signin-oidc" + "http://104.40.62.65:5100/signin-oidc", + "http://localhost:5100" }, PostLogoutRedirectUris = new List { diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index 518332cf9..7ca26f9ba 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -3,6 +3,7 @@ using Api.Application.Commands; using Api.Application.Queries; using AspNetCore.Authorization; + using Infrastructure.Services; using MediatR; using Microsoft.AspNetCore.Mvc; using Models; @@ -16,8 +17,9 @@ { private readonly IMediator _mediator; private readonly IOrderQueries _orderQueries; + private readonly IIdentityService _identityService; - public OrdersController(IMediator mediator, IOrderQueries orderQueries) + public OrdersController(IMediator mediator, IOrderQueries orderQueries, IIdentityService identityService) { if (mediator == null) { @@ -29,21 +31,24 @@ throw new ArgumentNullException(nameof(orderQueries)); } + if (identityService == null) + { + throw new ArgumentException(nameof(identityService)); + } + _mediator = mediator; _orderQueries = orderQueries; + _identityService = identityService; } [Route("new")] [HttpPost] public async Task AddOrder([FromBody]NewOrderRequest order) { - if (order.CardExpiration == DateTime.MinValue) - order.CardExpiration = DateTime.Now.AddYears(5); - if (order.CardTypeId == 0) order.CardTypeId = 1; - order.Buyer = GetUserName(); + order.Buyer = _identityService.GetUserIdentity(); var added = await _mediator.SendAsync(order); if (added) @@ -86,17 +91,7 @@ return Ok(cardTypes); } - - /// - /// Returns the GUID corresponding to the Id of the authenticated user. - /// - /// GUID (string) - string GetUserName() - { - return HttpContext.User.FindFirst("sub").Value; - } } - } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs new file mode 100644 index 000000000..4864234dc --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services +{ + public interface IIdentityService + { + string GetUserIdentity(); + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs new file mode 100644 index 000000000..e897b61c7 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs @@ -0,0 +1,29 @@ + +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services +{ + public class IdentityService : IIdentityService + { + private IHttpContextAccessor _context; + + public IdentityService(IHttpContextAccessor context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + _context = context; + } + + public string GetUserIdentity() + { + return _context.HttpContext.User.FindFirst("sub").Value; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 8ab54aa23..a9883c764 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -1,10 +1,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API { + using AspNetCore.Http; using Autofac; using Autofac.Extensions.DependencyInjection; using Infrastructure; using Infrastructure.AutofacModules; using Infrastructure.Filters; + using Infrastructure.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -74,7 +76,10 @@ .AllowCredentials()); }); + // Add application services. + services.AddSingleton(); services.AddSingleton(this.Configuration); + services.AddTransient(); services.AddOptions(); diff --git a/src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs b/src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs index db877cf31..80cf57d68 100644 --- a/src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs +++ b/src/Services/Ordering/Ordering.Application/Queries/OrderQueries.cs @@ -7,9 +7,10 @@ using System; using System.Dynamic; using System.Collections.Generic; + using System.Linq; public class OrderQueries - :IOrderQueries + : IOrderQueries { private string _connectionString = string.Empty; @@ -97,5 +98,31 @@ return order; } + + //TODO/CCE: try to use this method instead actual. + //private object MapOrderItems(dynamic result) + //{ + // IEnumerable items = (result as System.Collections.IEnumerable).Cast(); + // var order = new + // { + // ordernumber = result[0].ordernumbe, + // date = result[0].date, + // status = result[0].status, + // street = result[0].street, + // city = result[0].city, + // zipcode = result[0].zipcode, + // country = result[0].country, + // total = items.Select(r => (int)r.units * (int)r.unitprice).Sum(), + // orderItems = items.Select(r => new + // { + // productname = r.productname, + // units = r.units, + // unitprice = r.unitprice, + // pictureurl = r.pictureurl + // }) + // }; + + // return order; + //} } } diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 408cad542..49106f017 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -33,9 +33,6 @@ namespace Microsoft.eShopOnContainers.WebMVC // builder.AddUserSecrets(); //} - //builder.AddJsonFile("appsettings.override.json"); - //builder.AddEnvironmentVariables(); - Configuration = builder.Build(); } @@ -51,7 +48,7 @@ namespace Microsoft.eShopOnContainers.WebMVC services.AddSingleton(); services.AddTransient(); - services.AddSingleton(); //CCE: Once services are integrated, a singleton is not needed we can left transient. + services.AddTransient(); services.AddTransient(); services.AddTransient, IdentityParser>(); } @@ -86,9 +83,6 @@ namespace Microsoft.eShopOnContainers.WebMVC var callBackUrl = Configuration.GetValue("CallBackUrl"); var log = loggerFactory.CreateLogger("identity"); - log.LogDebug(identityUrl.ToString()); - log.LogDebug(callBackUrl.ToString()); - var oidcOptions = new OpenIdConnectOptions { AuthenticationScheme = "oidc", diff --git a/test/Services/UnitTest/Catalog/CatalogControllertest.cs b/test/Services/UnitTest/Catalog/CatalogControllertest.cs new file mode 100644 index 000000000..e3ca383e0 --- /dev/null +++ b/test/Services/UnitTest/Catalog/CatalogControllertest.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopOnContainers.Services.Catalog.API.Controllers; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace UnitTest.Catalog +{ + public class CatalogControllerTest + { + private readonly Mock _mockContext; + private readonly Mock> _mockItems; + public CatalogControllerTest() + { + _mockContext = new Mock(); + _mockItems = new Mock>(); + } + + [Fact] + public async Task Items_ReturnsOKObject_WhenItemsFound() + { + //CCE: TODO + Assert.True(true); + } + + } +} diff --git a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs new file mode 100644 index 000000000..c728ed70f --- /dev/null +++ b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs @@ -0,0 +1,92 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Commands; +using Microsoft.eShopOnContainers.Services.Ordering.Domain; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.Repositories; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace UnitTest.Ordering.Application +{ + public class NewOrderRequestHandlerTest + { + private readonly Mock _buyerRepositoryMock; + private readonly Mock _orderRepositoryMock; + + public NewOrderRequestHandlerTest() + { + //Mocks; + _buyerRepositoryMock = new Mock(); + _orderRepositoryMock = new Mock(); + } + + [Fact] + public async Task Handle_ReturnsTrue_WhenOrderIsPersistedSuccesfully() + { + // Arrange + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().Buyer)) + .Returns(Task.FromResult(FakeBuyer())); + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + .Returns(Task.FromResult(1)); + + _orderRepositoryMock.Setup(or => or.Add(FakeOrder())).Returns(FakeOrder()); + _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))).Returns(Task.FromResult(1)); + + //Act + var handler = new NewOrderRequestHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object); + var result = await handler.Handle(FakeOrderRequestWithBuyer()); + + //Assert + Assert.True(result); + } + + [Fact] + public async Task Handle_ReturnsFalse_WhenOrderIsNotPersisted() + { + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(FakeOrderRequestWithBuyer().Buyer)) + .Returns(Task.FromResult(FakeBuyer())); + _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + .Returns(Task.FromResult(1)); + + _orderRepositoryMock.Setup(or => or.Add(FakeOrder())).Returns(FakeOrder()); + _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))).Returns(Task.FromResult(0)); + + //Act + var handler = new NewOrderRequestHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object); + var result = await handler.Handle(FakeOrderRequestWithBuyer()); + + //Assert + Assert.False(result); + } + + private Buyer FakeBuyer() + { + return new Buyer(Guid.NewGuid().ToString()); + } + + private Order FakeOrder() + { + return new Order(1, 1) + { + + }; + } + + private NewOrderRequest FakeOrderRequestWithBuyer() + { + return new NewOrderRequest + { + Buyer = "1234", + CardNumber = "1234", + CardExpiration = DateTime.Now.AddYears(1), + CardSecurityNumber = "123", + CardHolderName = "XXX" + }; + } + } +} diff --git a/test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs b/test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs new file mode 100644 index 000000000..055bd0730 --- /dev/null +++ b/test/Services/UnitTest/Ordering/Controllers/OrderControllerTest.cs @@ -0,0 +1,152 @@ +using System; +using Xunit; +using System.Threading.Tasks; +using Moq; +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; +using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Commands; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +using Microsoft.eShopOnContainers.Services.Ordering.Api.Application.Queries; +using System.Collections.Generic; + +namespace UnitTest.Ordering.Controllers +{ + public class OrderControllerTest + { + private readonly Mock _mediatorMock; + private readonly Mock _identityMock; + private readonly Mock _queriesMock; + + public OrderControllerTest() + { + //Mocks; + _mediatorMock = new Mock(); + _identityMock = new Mock(); + _queriesMock = new Mock(); + } + + [Fact] + public async Task AddOrder_ReturnsBadRequestResult_WhenPersitenceOperationFails() + { + // Arrange + var orderRequest = new object() as IAsyncRequest; + + _mediatorMock.Setup(mediator => mediator.SendAsync(OrderFakeNotExpired())) + .Returns(Task.FromResult(false)); + + _identityMock.Setup(identity => identity.GetUserIdentity()) + .Returns(Guid.NewGuid().ToString()); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var badRequestResult = await controller.AddOrder(OrderFakeNotExpired()); + + // Assert + Assert.IsType(badRequestResult); + } + + [Fact] + public async Task AddOrder_ReturnsOK_WhenPersistenceOperationSucceed() + { + // Arrange + _mediatorMock.Setup(mediator => mediator.SendAsync(OrderFakeNotExpired())) + .Returns(Task.FromResult(true)); + + _identityMock.Setup(identity => identity.GetUserIdentity()) + .Returns(Guid.NewGuid().ToString()); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var badRequestResult = await controller.AddOrder(OrderFakeNotExpired()); + + // Assert + Assert.IsType(badRequestResult); + } + + [Fact] + public async Task GetOrder_ReturnsNotFound_WhenItemNotFound() + { + // Arrange + _queriesMock.Setup(queries => queries.GetOrder(1)) + .Throws(new KeyNotFoundException()); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var notFoundResult = await controller.GetOrder(1); + + // Assert + Assert.IsType(notFoundResult); + } + + [Fact] + public async Task GetOrder_ReturnsOkObjecResult_WheItemFound() + { + // Arrange + _queriesMock.Setup(queries => queries.GetOrder(1)) + .Returns(Task.FromResult(new object())); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var OkObjectResult = await controller.GetOrder(1); + + // Assert + Assert.IsType(OkObjectResult); + } + + [Fact] + public async Task GetOrders_ReturnsOkObjectResult() + { + // Arrange + _queriesMock.Setup(queries => queries.GetOrders()) + .Returns(Task.FromResult(new object())); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var OkObjectResult = await controller.GetOrders(); + + // Assert + Assert.IsType(OkObjectResult); + } + + [Fact] + public async Task GetCardTypes() + { + // Arrange + _queriesMock.Setup(queries => queries.GetCardTypes()) + .Returns(Task.FromResult(new object())); + + var controller = new OrdersController(_mediatorMock.Object, _queriesMock.Object, _identityMock.Object); + + // Act + var OkObjectResult = await controller.GetCardTypes(); + + // Assert + Assert.IsType(OkObjectResult); + } + + //Fakes + private NewOrderRequest OrderFakeNotExpired() + { + return new NewOrderRequest() + { + CardTypeId = 1, + CardExpiration = DateTime.Now.AddYears(1) + }; + } + + private NewOrderRequest OrderFakeExpired() + { + return new NewOrderRequest() + { + CardTypeId = 1, + CardExpiration = DateTime.Now.AddYears(-1) + }; + } + } +} diff --git a/test/Services/UnitTest/Properties/AssemblyInfo.cs b/test/Services/UnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0d5dcb370 --- /dev/null +++ b/test/Services/UnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTest")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7796f5d8-31fc-45a4-b673-19de5ba194cf")] diff --git a/test/Services/UnitTest/UnitTest.xproj b/test/Services/UnitTest/UnitTest.xproj new file mode 100644 index 000000000..1d3c09c84 --- /dev/null +++ b/test/Services/UnitTest/UnitTest.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 7796f5d8-31fc-45a4-b673-19de5ba194cf + UnitTest + .\obj + .\bin\ + v4.5.2 + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Services/UnitTest/project.json b/test/Services/UnitTest/project.json new file mode 100644 index 000000000..0e54e4c4f --- /dev/null +++ b/test/Services/UnitTest/project.json @@ -0,0 +1,24 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "MediatR": "2.1.0", + "Moq": "4.6.38-alpha", + "Microsoft.NETCore.App": "1.1.0", + "xunit": "2.2.0-beta4-build3444", + "Ordering.API": "1.0.0-*", + "Catalog.API": "1.0.0-*", + "Microsoft.AspNetCore.TestHost": "1.1.0", + "dotnet-test-xunit": "2.2.0-preview2-build1029" + }, + "testRunner": "xunit", + "runtimes": { + "win10-x64": {} + }, + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + } + } + } +} diff --git a/test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs b/test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs new file mode 100644 index 000000000..17dcbdc55 --- /dev/null +++ b/test/Services/UnitTests__/UnitTests/Ordering/OrderControllerTest.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; +using System.Threading.Tasks; +using Moq; +using MediatR; + +namespace UnitTests +{ + public class OrderControllerTest + { + private readonly Mock _mock; + + public OrderControllerTest() + { + //config mock; + _mock = new Mock(); + + + } + + [Fact] + public async Task AddOrder_ReturnsBadRequestResult_WhenPersitenceOperationFails() + { + //Add order: + var orderRequest = new object() as IAsyncRequest; + _mock.Setup(mediator => mediator.SendAsync(orderRequest)) + .Returns(Task.FromResult(false)); + + // Arrange + var controller = new OrdersController(mockRepo.Object); + controller.ModelState.AddModelError("SessionName", "Required"); + var newSession = new HomeController.NewSessionModel(); + + // Act + var result = await controller.Index(newSession); + + // Assert + var badRequestResult = Assert.IsType(result); + Assert.IsType(badRequestResult.Value); + } + + + // Implement Fake method for mock. + private MediatorMockForAddOrder() + { + + } + } +} diff --git a/test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs b/test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..78acac4b0 --- /dev/null +++ b/test/Services/UnitTests__/UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ecbb8dc1-22ea-42d2-a45a-4ae800c73356")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/Services/UnitTests__/UnitTests/UnitTests.csproj b/test/Services/UnitTests__/UnitTests/UnitTests.csproj new file mode 100644 index 000000000..b7b2e5e47 --- /dev/null +++ b/test/Services/UnitTests__/UnitTests/UnitTests.csproj @@ -0,0 +1,117 @@ + + + + Debug + AnyCPU + {ECBB8DC1-22EA-42D2-A45A-4AE800C73356} + Library + Properties + UnitTests + UnitTests + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll + True + + + ..\..\..\..\packages\MediatR.2.1.0\lib\net45\MediatR.dll + True + + + ..\..\..\..\packages\Moq.4.6.38-alpha\lib\net45\Moq.dll + True + + + + ..\..\..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + True + + + ..\..\..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll + True + + + ..\..\..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll + True + + + ..\..\..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/test/Services/UnitTests__/UnitTests/packages.config b/test/Services/UnitTests__/UnitTests/packages.config new file mode 100644 index 000000000..ee7e708c6 --- /dev/null +++ b/test/Services/UnitTests__/UnitTests/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file From 7266f08048f5d1d7dbd6c3dd2b24c579ba38174f Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Thu, 22 Dec 2016 16:45:02 +0100 Subject: [PATCH 03/10] Fix fonts on webpack --- .../Client/modules/app.component.html | 62 +++++++++---------- .../Client/modules/app.component.scss | 7 +-- .../config/webpack.config.js | 6 +- .../eShopOnContainers.WebSPA/package.json | 6 +- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html index 8bf2ffeb1..eceebb0c5 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html @@ -1,41 +1,41 @@ - + - - + + -
-
-
-
-
- -
- - + + \ No newline at end of file diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss index f3436abe6..ef869deff 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss @@ -12,14 +12,9 @@ src: url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg") } -body { - padding-top: 80px; - /*padding-bottom: 20px;*/ +.app { font-family: Montserrat,sans-serif; - min-width:480px; -} -.app { &-footer { padding-top: 40px; padding-bottom: 40px; diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js b/src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js index 5fae32287..78ef65c21 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/config/webpack.config.js @@ -6,6 +6,7 @@ var extractCSS = new ExtractTextPlugin('styles.css'); var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin; var devConfig = require('./webpack.config.dev'); var prodConfig = require('./webpack.config.prod'); +var CopyWebpackPlugin = require('copy-webpack-plugin'); var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Development'; console.log("==========Dev Mode = " + isDevelopment + " ============" ) @@ -74,6 +75,9 @@ module.exports = merge({ 'process.env': { 'ENV': JSON.stringify(process.env.ASPNETCORE_ENVIRONMENT) } - }) + }), + new CopyWebpackPlugin([ + { from: 'Client/fonts', to: 'fonts' } + ]) ] }, isDevelopment ? devConfig : prodConfig); diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/package.json b/src/Web/WebSPA/eShopOnContainers.WebSPA/package.json index 0f445172c..3103d578e 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/package.json +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/package.json @@ -8,7 +8,7 @@ "angular2", "webpack2", "typescript2", - "bootstrap4", + "bootstrap4", "docker" ], "author": { @@ -71,11 +71,11 @@ "@types/source-map": "0.1.28", "@types/uglify-js": "2.6.28", "@types/webpack": "1.12.35", - "angular2-template-loader": "0.6.0", "angular2-router-loader": "0.3.4", + "angular2-template-loader": "0.6.0", "awesome-typescript-loader": "2.2.4", "codelyzer": "1.0.0-beta.3", - "copy-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^4.0.1", "css": "2.2.1", "css-loader": "0.25.0", "es6-promise": "3.2.1", From d1abd9da368a6f89da69f21c165d105807873fa7 Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Thu, 22 Dec 2016 17:16:59 +0100 Subject: [PATCH 04/10] Fix fonts loader --- .../Client/modules/app.component.scss | 4 ++-- .../Client/modules/basket/basket.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss index ef869deff..7433920b1 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.scss @@ -3,13 +3,13 @@ @font-face { font-family: Montserrat; font-weight: 400; - src: url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg") + src: url("../dist/fonts/Montserrat-Regular.eot?") format("eot"),url("../dist/fonts/Montserrat-Regular.woff") format("woff"),url("../dist/fonts/Montserrat-Regular.ttf") format("truetype"),url("../dist/fonts/Montserrat-Regular.svg#Montserrat") format("svg") } @font-face { font-family: Montserrat; font-weight: 700; - src: url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg") + src: url("../dist/fonts/Montserrat-Bold.eot?") format("eot"),url("../dist/fonts/Montserrat-Bold.woff") format("woff"),url("../dist/fonts/Montserrat-Bold.ttf") format("truetype"),url("../dist/fonts/Montserrat-Bold.svg#Montserrat") format("svg") } .app { diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts index 9377bede3..41818fb2b 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.component.ts @@ -5,7 +5,7 @@ import { IBasket } from '../shared/models/basket.model'; import { IBasketItem } from '../shared/models/basketItem.model'; @Component({ - selector: 'esh-basket', + selector: 'esh-basket .esh-basket', styleUrls: ['./basket.component.scss'], templateUrl: './basket.component.html' }) From 5466f58252d72e6e29fd7a9d13a4aca472c88dbc Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Thu, 22 Dec 2016 17:18:39 +0100 Subject: [PATCH 05/10] add catalog class on component --- .../Client/modules/catalog/catalog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts index c953d23ed..e3e328aea 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.component.ts @@ -8,7 +8,7 @@ import { IPager } from '../shared/models/pager.model'; import { BasketWrapperService} from '../shared/services/basket.wrapper.service'; @Component({ - selector: 'esh-catalog', + selector: 'esh-catalog .catalog', styleUrls: ['./catalog.component.scss'], templateUrl: './catalog.component.html' }) From c0c7d735e8dfe62580c03aab035cef7b2233cd7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ca=C3=B1izares=20Est=C3=A9vez?= Date: Thu, 22 Dec 2016 18:34:57 +0100 Subject: [PATCH 06/10] SPA: basket component, authentication service, basket list --- docker-compose.override.yml | 2 - docker-compose.yml | 2 + .../Configuration/Config.cs | 7 +- .../Client/modules/app.component.html | 3 +- .../Client/modules/app.component.ts | 28 +- .../basket-status/basket-status.component.ts | 11 +- .../Client/modules/basket/basket.service.ts | 12 +- .../shared/components/identity/identity.html | 1 + .../shared/components/identity/identity.scss | 3 + .../shared/components/identity/identity.ts | 38 +++ .../modules/shared/models/identity.model.ts | 3 + .../modules/shared/services/data.service.ts | 29 +- .../shared/services/security.service.ts | 247 ++++++++++++++++++ .../Client/modules/shared/shared.module.ts | 12 +- .../eShopOnContainers.WebSPA/Program.cs | 2 +- 15 files changed, 360 insertions(+), 40 deletions(-) create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 5cd817bb8..048784fb9 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -19,8 +19,6 @@ services: - BasketUrl=http://basket.api:5103 ports: - "5100:5100" - links: - - identity.service:10.0.75.1 webspa: environment: diff --git a/docker-compose.yml b/docker-compose.yml index 030db7df0..bbfd7b671 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,3 +47,5 @@ services: basket.data: image: redis + ports: + - "6379:6379" diff --git a/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs b/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs index c8a434215..01a3d0d21 100644 --- a/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs +++ b/src/Services/Identity/eShopOnContainers.Identity/Configuration/Config.cs @@ -41,8 +41,8 @@ namespace eShopOnContainers.Identity.Configuration ClientName = "eShop SPA OpenId Client", AllowedGrantTypes = GrantTypes.Implicit, AllowAccessTokensViaBrowser = true, - RedirectUris = { $"{clientsUrl["Spa"]}/callback.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/index.html" }, + RedirectUris = { $"{clientsUrl["Spa"]}/" }, + PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/" }, AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" }, AllowedScopes = { @@ -83,7 +83,8 @@ namespace eShopOnContainers.Identity.Configuration { $"{clientsUrl["Mvc"]}/signin-oidc", "http://104.40.62.65:5100/signin-oidc", - "http://localhost:5100" + "http://localhost:5100/signin-oidc", + "http://13.88.8.119:5100/signin-oidc" }, PostLogoutRedirectUris = new List { diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html index 8bf2ffeb1..eeae6d0cd 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html @@ -7,7 +7,8 @@
- + +
diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts index b32e8929b..08d64256a 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.ts @@ -1,8 +1,10 @@ import { Title } from '@angular/platform-browser'; import { Component, ViewEncapsulation, OnInit } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { Subscription } from 'rxjs/Subscription'; import { DataService } from './shared/services/data.service'; +import { SecurityService } from './shared/services/security.service'; /* * App Component @@ -10,22 +12,24 @@ import { DataService } from './shared/services/data.service'; */ @Component({ - selector: 'appc-app', - styleUrls: ['./app.component.scss'], - templateUrl: './app.component.html' + selector: 'appc-app', + styleUrls: ['./app.component.scss'], + templateUrl: './app.component.html' }) export class AppComponent implements OnInit { + private Authenticated: boolean = false; + subscription: Subscription; + constructor(private titleService: Title, private securityService: SecurityService) { - constructor(private titleService: Title) { + } - } + ngOnInit() { + console.log('app on init'); + this.subscription = this.securityService.authenticationChallenge$.subscribe(res => this.Authenticated = res); + } - ngOnInit() { - - } - - public setTitle(newTitle: string) { - this.titleService.setTitle('eShopOnContainers'); - } + public setTitle(newTitle: string) { + this.titleService.setTitle('eShopOnContainers'); + } } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts index 868cd9cc2..06751547b 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket-status/basket-status.component.ts @@ -19,10 +19,15 @@ export class BasketStatusComponent implements OnInit { ngOnInit() { this.subscription = this.basketEvents.addItemToBasket$.subscribe( item => { + console.log('element received in basket'); console.log(item); - this.service.setBasket(item); - this.service.getBasket().subscribe(basket => { - this.badge = basket.items.length; + this.service.setBasket(item).subscribe(res => { + console.log(res); + this.service.getBasket().subscribe(basket => { + this.badge = basket.items.length; + console.log('response from basket api'); + console.log(basket.items.length); + }); }); }); } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts index a294f8c87..c8018554e 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; +import { Response, Headers } from '@angular/http'; import { DataService } from '../shared/services/data.service'; +import { SecurityService } from '../shared/services/security.service'; import { IBasket } from '../shared/models/basket.model'; import { IBasketItem } from '../shared/models/basketItem.model'; @@ -19,13 +20,16 @@ export class BasketService { items: [] }; - constructor(private service: DataService) { + constructor(private service: DataService, private authService: SecurityService) { this.basket.items = []; } - setBasket(item) { + setBasket(item): Observable { + console.log('set basket'); this.basket.items.push(item); - this.service.post(this.basket.buyerId, this.basket.items); + return this.service.post(this.basketUrl + '/', this.basket).map((response: Response) => { + return true; + }); } getBasket(): Observable { diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html new file mode 100644 index 000000000..f1cfa47b9 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html @@ -0,0 +1 @@ + diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss new file mode 100644 index 000000000..76ac3a08e --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.scss @@ -0,0 +1,3 @@ +.identity { + +} diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts new file mode 100644 index 000000000..1e5fee902 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts @@ -0,0 +1,38 @@ +import { Component, OnInit, OnChanges, Output, Input, EventEmitter } from '@angular/core'; + +import { IIdentity } from '../../models/identity.model'; +import { SecurityService } from '../../services/security.service'; + +@Component({ + selector: 'esh-identity', + templateUrl: './identity.html', + styleUrls: ['./identity.scss'] +}) +export class Identity implements OnInit { + constructor(private service: SecurityService) { + } + + @Output() + changed: EventEmitter = new EventEmitter(); + + @Input() + model: IIdentity; + + ngOnInit() { + console.log("ngOnInit _securityService.AuthorizedCallback"); + + if (window.location.hash) { + this.service.AuthorizedCallback(); + console.log('isAutorized?'); + console.log(this.service.IsAuthorized); + } + } + + login() { + this.service.Authorize(); + } + + logout() { + this.service.Logoff(); + } +} diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts new file mode 100644 index 000000000..957dbba36 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/identity.model.ts @@ -0,0 +1,3 @@ +export interface IIdentity { + +} \ No newline at end of file diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts index d1e609e9e..20f62a015 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/data.service.ts @@ -8,15 +8,19 @@ import { Observer } from 'rxjs/Observer'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; +import { SecurityService } from './security.service'; + @Injectable() export class DataService { - - constructor(private http: Http) { } + constructor(private http: Http, private securityService: SecurityService) { } get(url: string, params?: any): Observable { let options: RequestOptionsArgs = {}; - options.headers = new Headers(); - this.addCors(options); + + if (this.securityService) { + options.headers = new Headers(); + options.headers.append("Authorization", "Bearer " + this.securityService.GetToken()); + } return this.http.get(url, options).map( (res: Response) => { @@ -24,13 +28,18 @@ export class DataService { }).catch(this.handleError); } - post(url: string, data: any, params?: any) { - return this.http.post(url, data, params); - } + post(url: string, data: any, params?: any): Observable { + let options: RequestOptionsArgs = {}; + + if (this.securityService) { + options.headers = new Headers(); + options.headers.append("Authorization", "Bearer " + this.securityService.GetToken()); + } - private addCors(options: RequestOptionsArgs): RequestOptionsArgs { - options.headers.append('Access-Control-Allow-Origin', '*'); - return options; + return this.http.post(url, data, options).map( + (res: Response) => { + return res; + }).catch(this.handleError); } private handleError(error: any) { diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts new file mode 100644 index 000000000..8f4776cb6 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts @@ -0,0 +1,247 @@ +import { Injectable } from '@angular/core'; +import { Http, Response, Headers } from '@angular/http'; +import 'rxjs/add/operator/map'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +//import { Configuration } from '../app.constants'; +import { Router } from '@angular/router'; + +@Injectable() +export class SecurityService { + + private actionUrl: string; + private headers: Headers; + private storage: any; + private authenticationSource = new Subject(); + authenticationChallenge$ = this.authenticationSource.asObservable(); + + constructor(private _http: Http, private _router: Router) { + + //this.actionUrl = _configuration.Server + 'api/DataEventRecords/'; + + this.headers = new Headers(); + this.headers.append('Content-Type', 'application/json'); + this.headers.append('Accept', 'application/json'); + this.storage = sessionStorage; //localStorage; + + if (this.retrieve("IsAuthorized") !== "") { + //this.HasAdminRole = this.retrieve("HasAdminRole"); + this.IsAuthorized = this.retrieve("IsAuthorized"); + } + } + + public IsAuthorized: boolean; + //public HasAdminRole: boolean; + + public GetToken(): any { + return this.retrieve("authorizationData"); + } + + public ResetAuthorizationData() { + this.store("authorizationData", ""); + this.store("authorizationDataIdToken", ""); + + this.IsAuthorized = false; + //this.HasAdminRole = false; + this.store("HasAdminRole", false); + this.store("IsAuthorized", false); + } + + public UserData: any; + public SetAuthorizationData(token: any, id_token:any) { + if (this.retrieve("authorizationData") !== "") { + this.store("authorizationData", ""); + } + + this.store("authorizationData", token); + this.store("authorizationDataIdToken", id_token); + this.IsAuthorized = true; + this.store("IsAuthorized", true); + //emit observable + this.authenticationSource.next(true); + + this.getUserData() + .subscribe(data => this.UserData = data, + error => this.HandleError(error), + () => { + console.log(this.UserData); + }); + } + + public Authorize() { + this.ResetAuthorizationData(); + + console.log("BEGIN Authorize, no auth data"); + + var authorizationUrl = 'http://localhost:5105/connect/authorize'; + var client_id = 'js'; + var redirect_uri = 'http://localhost:5104/'; + var response_type = "id_token token"; + var scope = "openid profile orders basket"; + var nonce = "N" + Math.random() + "" + Date.now(); + var state = Date.now() + "" + Math.random(); + + this.store("authStateControl", state); + this.store("authNonce", nonce); + console.log("AuthorizedController created. adding myautostate: " + this.retrieve("authStateControl")); + + var url = + authorizationUrl + "?" + + "response_type=" + encodeURI(response_type) + "&" + + "client_id=" + encodeURI(client_id) + "&" + + "redirect_uri=" + encodeURI(redirect_uri) + "&" + + "scope=" + encodeURI(scope) + "&" + + "nonce=" + encodeURI(nonce) + "&" + + "state=" + encodeURI(state); + + window.location.href = url; + } + + public AuthorizedCallback() { + console.log("BEGIN AuthorizedCallback, no auth data"); + this.ResetAuthorizationData(); + + var hash = window.location.hash.substr(1); + + var result: any = hash.split('&').reduce(function (result : any, item: string) { + var parts = item.split('='); + result[parts[0]] = parts[1]; + return result; + }, {}); + + console.log(result); + console.log("AuthorizedCallback created, begin token validation"); + + var token = ""; + var id_token = ""; + var authResponseIsValid = false; + if (!result.error) { + + if (result.state !== this.retrieve("authStateControl")) { + console.log("AuthorizedCallback incorrect state"); + } else { + + token = result.access_token; + id_token = result.id_token + + var dataIdToken: any = this.getDataFromToken(id_token); + console.log(dataIdToken); + + // validate nonce + if (dataIdToken.nonce !== this.retrieve("authNonce")) { + console.log("AuthorizedCallback incorrect nonce"); + } else { + this.store("authNonce", ""); + this.store("authStateControl", ""); + + authResponseIsValid = true; + console.log("AuthorizedCallback state and nonce validated, returning access token"); + } + } + } + + if (authResponseIsValid) { + this.SetAuthorizationData(token, id_token); + console.log(this.retrieve("authorizationData")); + + // router navigate to DataEventRecordsList + this._router.navigate(['/dataeventrecords/list']); + } + else { + this.ResetAuthorizationData(); + this._router.navigate(['/Unauthorized']); + } + } + + public Logoff() { + // /connect/endsession?id_token_hint=...&post_logout_redirect_uri=https://myapp.com + console.log("BEGIN Authorize, no auth data"); + + var authorizationUrl = 'http://localhost:5105/connect/endsession'; + console.log(this.retrieve("authorizationDataIdToken")); + var id_token_hint = this.retrieve("authorizationDataIdToken"); + var post_logout_redirect_uri = 'http://localhost:5104/'; + + var url = + authorizationUrl + "?" + + "id_token_hint=" + encodeURI(id_token_hint) + "&" + + "post_logout_redirect_uri=" + encodeURI(post_logout_redirect_uri); + + this.ResetAuthorizationData(); + + window.location.href = url; + } + + public HandleError(error: any) { + console.log(error); + if (error.status == 403) { + this._router.navigate(['/Forbidden']) + } + else if (error.status == 401) { + this.ResetAuthorizationData(); + this._router.navigate(['/Unauthorized']) + } + } + + private urlBase64Decode(str: string) { + var output = str.replace('-', '+').replace('_', '/'); + switch (output.length % 4) { + case 0: + break; + case 2: + output += '=='; + break; + case 3: + output += '='; + break; + default: + throw 'Illegal base64url string!'; + } + + return window.atob(output); + } + + private getDataFromToken(token: any) { + var data = {}; + if (typeof token !== 'undefined') { + var encoded = token.split('.')[1]; + data = JSON.parse(this.urlBase64Decode(encoded)); + } + + return data; + } + + private retrieve(key: string): any { + var item = this.storage.getItem(key); + + if (item && item !== 'undefined') { + return JSON.parse(this.storage.getItem(key)); + } + + return; + } + + private store(key: string, value: any) { + this.storage.setItem(key, JSON.stringify(value)); + } + + private getUserData = (): Observable => { + this.setHeaders(); + return this._http.get('http://localhost:5105/connect/userinfo', { + headers: this.headers, + body: '' + }).map(res => res.json()); + } + + private setHeaders() { + this.headers = new Headers(); + this.headers.append('Content-Type', 'application/json'); + this.headers.append('Accept', 'application/json'); + + var token = this.GetToken(); + + if (token !== "") { + this.headers.append('Authorization', 'Bearer ' + token); + } + } +} \ No newline at end of file diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts index b128498fa..c55830018 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/shared.module.ts @@ -17,9 +17,11 @@ import { DataService } from './services/data.service'; import { UtilityService } from './services/utility.service'; import { UppercasePipe } from './pipes/uppercase.pipe'; import { BasketWrapperService} from './services/basket.wrapper.service'; +import { SecurityService } from './services/security.service'; //Components: import {Pager } from './components/pager/pager'; +import {Identity } from './components/identity/identity'; @NgModule({ imports: [ @@ -39,7 +41,8 @@ import {Pager } from './components/pager/pager'; ErrorSummaryComponent, PageHeadingComponent, UppercasePipe, - Pager + Pager, + Identity ], exports: [ // Modules @@ -57,9 +60,9 @@ import {Pager } from './components/pager/pager'; //HeaderComponent, PageHeadingComponent, UppercasePipe, - Pager + Pager, + Identity ] - }) export class SharedModule { static forRoot(): ModuleWithProviders { @@ -70,7 +73,8 @@ export class SharedModule { DataService, FormControlService, UtilityService, - BasketWrapperService + BasketWrapperService, + SecurityService ] }; } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs b/src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs index 690c56eac..1f25bb104 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Program.cs @@ -17,8 +17,8 @@ namespace eShopConContainers.WebSPA .UseKestrel() .UseConfiguration(config) .UseContentRoot(Directory.GetCurrentDirectory()) + .UseUrls("http://localhost:5104/") .UseIISIntegration() - //.UseUrls("http://localhost:5104/") .UseStartup() .Build(); From 72b47764b2445b2884b9e76f681c7f2f5f20a78b Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Fri, 23 Dec 2016 10:15:36 +0100 Subject: [PATCH 07/10] Fix app.component --- .../eShopOnContainers.WebSPA/Client/modules/app.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html index eceebb0c5..4717b8cbb 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html @@ -38,4 +38,4 @@ - \ No newline at end of file + From bced5f0a337028576aa98bc16a7cf58565fdcbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ca=C3=B1izares=20Est=C3=A9vez?= Date: Fri, 23 Dec 2016 10:20:22 +0100 Subject: [PATCH 08/10] SPA: identity component --- .../Client/modules/app.component.html | 2 +- .../shared/components/identity/identity.html | 3 ++- .../shared/components/identity/identity.ts | 23 +++++++++++-------- .../shared/services/security.service.ts | 20 ++++------------ .../eShopOnContainers.WebSPA/project.json | 4 ++-- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html index eeae6d0cd..e5fbb26c6 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.component.html @@ -7,8 +7,8 @@
+ -
diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html index f1cfa47b9..cc2aafb45 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.html @@ -1 +1,2 @@ - + +
userName: {{userName}}
diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts index 1e5fee902..9b8d8352b 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/components/identity/identity.ts @@ -1,4 +1,5 @@ import { Component, OnInit, OnChanges, Output, Input, EventEmitter } from '@angular/core'; +import { Subscription } from 'rxjs/Subscription'; import { IIdentity } from '../../models/identity.model'; import { SecurityService } from '../../services/security.service'; @@ -9,22 +10,26 @@ import { SecurityService } from '../../services/security.service'; styleUrls: ['./identity.scss'] }) export class Identity implements OnInit { - constructor(private service: SecurityService) { - } + private authenticated: boolean = false; + private subscription: Subscription; + private userName: string = ""; - @Output() - changed: EventEmitter = new EventEmitter(); + constructor(private service: SecurityService) { - @Input() - model: IIdentity; + } ngOnInit() { - console.log("ngOnInit _securityService.AuthorizedCallback"); + this.subscription = this.service.authenticationChallenge$.subscribe(res => + { + //console.log(res); + //console.log(this.service.UserData); + //console.log(this.service); + this.authenticated = res; + this.userName = this.service.UserData.email; + }); if (window.location.hash) { this.service.AuthorizedCallback(); - console.log('isAutorized?'); - console.log(this.service.IsAuthorized); } } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts index 8f4776cb6..32756c0cd 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/services/security.service.ts @@ -16,22 +16,17 @@ export class SecurityService { authenticationChallenge$ = this.authenticationSource.asObservable(); constructor(private _http: Http, private _router: Router) { - - //this.actionUrl = _configuration.Server + 'api/DataEventRecords/'; - this.headers = new Headers(); this.headers.append('Content-Type', 'application/json'); this.headers.append('Accept', 'application/json'); this.storage = sessionStorage; //localStorage; if (this.retrieve("IsAuthorized") !== "") { - //this.HasAdminRole = this.retrieve("HasAdminRole"); this.IsAuthorized = this.retrieve("IsAuthorized"); } } public IsAuthorized: boolean; - //public HasAdminRole: boolean; public GetToken(): any { return this.retrieve("authorizationData"); @@ -42,8 +37,6 @@ export class SecurityService { this.store("authorizationDataIdToken", ""); this.IsAuthorized = false; - //this.HasAdminRole = false; - this.store("HasAdminRole", false); this.store("IsAuthorized", false); } @@ -57,11 +50,13 @@ export class SecurityService { this.store("authorizationDataIdToken", id_token); this.IsAuthorized = true; this.store("IsAuthorized", true); - //emit observable - this.authenticationSource.next(true); this.getUserData() - .subscribe(data => this.UserData = data, + .subscribe(data => { + this.UserData = data; + //emit observable + this.authenticationSource.next(true); + }, error => this.HandleError(error), () => { console.log(this.UserData); @@ -71,8 +66,6 @@ export class SecurityService { public Authorize() { this.ResetAuthorizationData(); - console.log("BEGIN Authorize, no auth data"); - var authorizationUrl = 'http://localhost:5105/connect/authorize'; var client_id = 'js'; var redirect_uri = 'http://localhost:5104/'; @@ -83,7 +76,6 @@ export class SecurityService { this.store("authStateControl", state); this.store("authNonce", nonce); - console.log("AuthorizedController created. adding myautostate: " + this.retrieve("authStateControl")); var url = authorizationUrl + "?" + @@ -98,7 +90,6 @@ export class SecurityService { } public AuthorizedCallback() { - console.log("BEGIN AuthorizedCallback, no auth data"); this.ResetAuthorizationData(); var hash = window.location.hash.substr(1); @@ -110,7 +101,6 @@ export class SecurityService { }, {}); console.log(result); - console.log("AuthorizedCallback created, begin token validation"); var token = ""; var id_token = ""; diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json b/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json index dda51e55a..356ae60c3 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/project.json @@ -39,8 +39,8 @@ }, "tools": { "Microsoft.DotNet.Watcher.Tools": { - "version": "1.0.0-*", - "imports": "portable-net451+win8" + "version": "1.0.0-preview2-final", + "imports": "portable-net451+win8+dnxcore50" }, "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview2-final", From d67f5665c723dca0fa8bed4b340f8423f33e4c17 Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Fri, 23 Dec 2016 10:51:17 +0100 Subject: [PATCH 09/10] Init orders component --- .../Client/modules/app.routes.ts | 4 +- .../Client/modules/basket/basket.routes.ts | 9 -- .../Client/modules/catalog/catalog.routes.ts | 9 -- .../modules/orders/orders.component.html | 62 +++++++++++++ .../modules/orders/orders.component.scss | 80 ++++++++++++++++ .../Client/modules/orders/orders.component.ts | 92 +++++++++++++++++++ .../Client/modules/orders/orders.module.ts | 14 +++ .../Client/modules/orders/orders.service.ts | 48 ++++++++++ 8 files changed, 299 insertions(+), 19 deletions(-) delete mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts delete mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts index 3ce3eda19..3ab758b3f 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.routes.ts @@ -2,11 +2,13 @@ import { Routes, RouterModule } from '@angular/router'; import { BasketComponent } from './basket/basket.component'; import { CatalogComponent } from './catalog/catalog.component'; +import { OrdersComponent } from './orders/orders.component'; export const routes: Routes = [ { path: '', redirectTo: 'catalog', pathMatch: 'full' }, { path: 'basket', component: BasketComponent }, - { path: 'catalog', component: CatalogComponent } + { path: 'catalog', component: CatalogComponent }, + { path: 'orders', component: OrdersComponent } //Lazy async modules (angular-loader-router) and enable a router in each module. //{ // path: 'basket', loadChildren: '/basket/basket.module' }); diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts deleted file mode 100644 index 3ed2bc003..000000000 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/basket/basket.routes.ts +++ /dev/null @@ -1,9 +0,0 @@ -//import { Routes, RouterModule } from '@angular/router'; - -//import { BasketComponent } from './basket.component'; - -//const routes: Routes = [ -// { path: '', component: BasketComponent } -//]; - -//export const routing = RouterModule.forChild(routes); diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts deleted file mode 100644 index cee81b259..000000000 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/catalog/catalog.routes.ts +++ /dev/null @@ -1,9 +0,0 @@ -//import { Routes, RouterModule } from '@angular/router'; - -//import { CatalogComponent } from './catalog.component'; - -//const routes: Routes = [ -// { path: '', component: CatalogComponent } -//]; - -//export const routing = RouterModule.forChild(routes); diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html new file mode 100644 index 000000000..6856c90b2 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html @@ -0,0 +1,62 @@ +
+
    +
  • Back to list
  • +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ PRODUCT + + + + BRAND + + PRICE + + QUANTITY + + COST +
{{item.productName}}ROSLYN$ {{item.unitPrice}} + + $ {{item.unitPrice * item.quantity}}
+
+
TOTAL
+ $ {{totalPrice}} +
+
+
+
+
+
\ No newline at end of file diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss new file mode 100644 index 000000000..d748fa784 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss @@ -0,0 +1,80 @@ +@import '../_variables.scss'; + +.esh-orders { + &-header { + background-color: #00A69C; + height: 63px; + + li { + list-style: none; + display: inline; + opacity: 0.5; + margin-top: 25px; + margin-left: 10px; + float: right; + cursor: pointer; + color: white; + } + + li a { + color: white; + } + + &-back { + float: left !important; + margin-top: 20px !important; + text-transform: uppercase; + } + + li a:hover { + text-decoration: none; + } + } + + &-container { + min-height: 70vh; + padding-top: 40px; + margin-bottom: 30px; + min-width: 992px; + } + + &-product-column { + max-width: 120px; + text-transform: uppercase; + vertical-align: middle !important; + } + + &-product-image { + max-width: 210px; + } + + &-total-value { + font-size: 20px; + color: #00a69c; + } + + &-total-label { + font-size: 14px; + color: #404040; + margin-top: 10px; + } + + &-totals { + border-bottom:none!important; + } +} + +.table td { + border-top: solid 1px #ddd; +} + +.table thead th { + border: none !important; +} + + + + + + + diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts new file mode 100644 index 000000000..ea1ec9fed --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { OrdersService } from './orders.service'; +import { ICatalog } from '../shared/models/catalog.model'; +import { ICatalogItem } from '../shared/models/catalogItem.model'; +import { ICatalogType } from '../shared/models/catalogType.model'; +import { ICatalogBrand } from '../shared/models/catalogBrand.model'; +import { IPager } from '../shared/models/pager.model'; +import { BasketWrapperService} from '../shared/services/basket.wrapper.service'; + +@Component({ + selector: 'esh-orders .orders', + styleUrls: ['./orders.component.scss'], + templateUrl: './orders.component.html' +}) +export class CatalogComponent implements OnInit { + brands: ICatalogBrand[]; + types: ICatalogType[]; + catalog: ICatalog; + brandSelected: number; + typeSelected: number; + paginationInfo: IPager; + + constructor(private service: OrdersService, private basketService: BasketWrapperService) { } + + ngOnInit() { + this.getBrands(); + this.getCatalog(10, 0); + this.getTypes(); + } + + onFilterApplied(event: any) { + event.preventDefault(); + this.getCatalog(this.paginationInfo.itemsPage, this.paginationInfo.actualPage, this.brandSelected, this.typeSelected); + } + + onBrandFilterChanged(event: any, value: number) { + event.preventDefault(); + this.brandSelected = value; + } + + onTypeFilterChanged(event: any, value: number) { + event.preventDefault(); + this.typeSelected = value; + } + + onPageChanged(value: any) { + console.log('catalog pager event fired' + value); + event.preventDefault(); + this.paginationInfo.actualPage = value; + this.getCatalog(this.paginationInfo.itemsPage, value); + } + + addToCart(item: ICatalogItem) { + this.basketService.addItemToBasket(item); + } + + getCatalog(pageSize:number, pageIndex: number, brand?: number, type?: number) { + this.service.getCatalog(pageIndex, pageSize, brand, type).subscribe(catalog => { + this.catalog = catalog; + console.log('catalog items retrieved: ' + catalog.count); + + this.paginationInfo = { + actualPage : catalog.pageIndex, + itemsPage : catalog.pageSize, + totalItems : catalog.count, + totalPages: Math.ceil(catalog.count / catalog.pageSize), + items: catalog.pageSize + }; + + console.log(this.paginationInfo); + }); + } + + getTypes() { + this.service.getTypes().subscribe(types => { + this.types = types; + let alltypes = { id: null, type: 'All' }; + this.types.unshift(alltypes); + console.log('types retrieved: ' + types.length); + }); + } + + getBrands() { + this.service.getBrands().subscribe(brands => { + this.brands = brands; + let allBrands = { id: null, brand: 'All' }; + this.brands.unshift(allBrands); + console.log('brands retrieved: ' + brands.length); + }); + } +} + diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts new file mode 100644 index 000000000..ef3bec4d7 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { SharedModule } from '../shared/shared.module'; +import { OrdersComponent } from './orders.component'; +import { OrdersService } from './orders.service'; +import { Pager } from '../shared/components/pager/pager'; + +@NgModule({ + imports: [BrowserModule, SharedModule], + declarations: [OrdersComponent], + providers: [OrdersService] +}) +export class CatalogModule { } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts new file mode 100644 index 000000000..ee57cdc86 --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; +import { Response } from '@angular/http'; + +import { DataService } from '../shared/services/data.service'; +import { ICatalog } from '../shared/models/catalog.model'; +import { ICatalogBrand } from '../shared/models/catalogBrand.model'; +import { ICatalogType } from '../shared/models/catalogType.model'; + +import 'rxjs/Rx'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/throw'; +import { Observer } from 'rxjs/Observer'; +import 'rxjs/add/operator/map'; + +@Injectable() +export class OrdersService { + private catalogUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/items'; + private brandUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/catalogbrands'; + private typesUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/catalogtypes'; + + constructor(private service: DataService) { + } + + getCatalog(pageIndex: number, pageSize: number, brand: number, type: number): Observable { + var url = this.catalogUrl; + if (brand || type) { + url = this.catalogUrl + '/type/' + ((type) ? type.toString() : 'null') + '/brand/' + ((brand) ? brand.toString() : 'null'); + } + + url = url + '?pageIndex=' + pageIndex + '&pageSize=' + pageSize; + + return this.service.get(url).map((response: Response) => { + return response.json(); + }); + } + + getBrands(): Observable { + return this.service.get(this.brandUrl).map((response: Response) => { + return response.json(); + }); + } + + getTypes(): Observable { + return this.service.get(this.typesUrl).map((response: Response) => { + return response.json(); + }); + }; +} \ No newline at end of file From 0f3f4abbc14b765986541c98a91afa929374211b Mon Sep 17 00:00:00 2001 From: Quique Fernandez Date: Fri, 23 Dec 2016 12:14:49 +0100 Subject: [PATCH 10/10] Add orders list --- .../Client/modules/app.module.ts | 2 + .../modules/orders/orders.component.html | 51 ++++------- .../modules/orders/orders.component.scss | 11 ++- .../Client/modules/orders/orders.component.ts | 85 +++---------------- .../Client/modules/orders/orders.module.ts | 2 +- .../Client/modules/orders/orders.service.ts | 31 ++----- .../modules/shared/models/order.model.ts | 6 ++ 7 files changed, 48 insertions(+), 140 deletions(-) create mode 100644 src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts index bdad19f82..997eacbfb 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/app.module.ts @@ -9,6 +9,7 @@ import { AppService } from './app.service'; import { AppComponent } from './app.component'; import { SharedModule } from './shared/shared.module'; import { CatalogModule } from './catalog/catalog.module'; +import { OrdersModule } from './orders/orders.module'; import { BasketModule } from './basket/basket.module'; @NgModule({ @@ -20,6 +21,7 @@ import { BasketModule } from './basket/basket.module'; // Only module that app module loads SharedModule.forRoot(), CatalogModule, + OrdersModule, BasketModule ], providers: [ diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html index 6856c90b2..bbda19c23 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.html @@ -1,57 +1,38 @@ -
+
    -
  • Back to list
  • +
  • Back to list
-
+
- - - + - - - - - - - - - - - - - - - + + + + + diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss index d748fa784..bb0ab0552 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.scss @@ -1,6 +1,8 @@ @import '../_variables.scss'; .esh-orders { + min-height: 80vh; + &-header { background-color: #00A69C; height: 63px; @@ -38,13 +40,16 @@ min-width: 992px; } - &-product-column { + &-order-column { max-width: 120px; - text-transform: uppercase; vertical-align: middle !important; } - &-product-image { + &-order-link { + color: #83d01b; + } + + &-order-image { max-width: 210px; } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts index ea1ec9fed..f52509875 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.component.ts @@ -1,91 +1,26 @@ import { Component, OnInit } from '@angular/core'; -import { OrdersService } from './orders.service'; -import { ICatalog } from '../shared/models/catalog.model'; -import { ICatalogItem } from '../shared/models/catalogItem.model'; -import { ICatalogType } from '../shared/models/catalogType.model'; -import { ICatalogBrand } from '../shared/models/catalogBrand.model'; -import { IPager } from '../shared/models/pager.model'; -import { BasketWrapperService} from '../shared/services/basket.wrapper.service'; +import { OrdersService } from './orders.service'; +import { IOrder } from '../shared/models/order.model'; @Component({ selector: 'esh-orders .orders', styleUrls: ['./orders.component.scss'], templateUrl: './orders.component.html' }) -export class CatalogComponent implements OnInit { - brands: ICatalogBrand[]; - types: ICatalogType[]; - catalog: ICatalog; - brandSelected: number; - typeSelected: number; - paginationInfo: IPager; +export class OrdersComponent implements OnInit { + orders: IOrder[]; - constructor(private service: OrdersService, private basketService: BasketWrapperService) { } + constructor(private service: OrdersService) { } ngOnInit() { - this.getBrands(); - this.getCatalog(10, 0); - this.getTypes(); + this.getOrders(); } - onFilterApplied(event: any) { - event.preventDefault(); - this.getCatalog(this.paginationInfo.itemsPage, this.paginationInfo.actualPage, this.brandSelected, this.typeSelected); - } - - onBrandFilterChanged(event: any, value: number) { - event.preventDefault(); - this.brandSelected = value; - } - - onTypeFilterChanged(event: any, value: number) { - event.preventDefault(); - this.typeSelected = value; - } - - onPageChanged(value: any) { - console.log('catalog pager event fired' + value); - event.preventDefault(); - this.paginationInfo.actualPage = value; - this.getCatalog(this.paginationInfo.itemsPage, value); - } - - addToCart(item: ICatalogItem) { - this.basketService.addItemToBasket(item); - } - - getCatalog(pageSize:number, pageIndex: number, brand?: number, type?: number) { - this.service.getCatalog(pageIndex, pageSize, brand, type).subscribe(catalog => { - this.catalog = catalog; - console.log('catalog items retrieved: ' + catalog.count); - - this.paginationInfo = { - actualPage : catalog.pageIndex, - itemsPage : catalog.pageSize, - totalItems : catalog.count, - totalPages: Math.ceil(catalog.count / catalog.pageSize), - items: catalog.pageSize - }; - - console.log(this.paginationInfo); - }); - } - - getTypes() { - this.service.getTypes().subscribe(types => { - this.types = types; - let alltypes = { id: null, type: 'All' }; - this.types.unshift(alltypes); - console.log('types retrieved: ' + types.length); - }); - } - getBrands() { - this.service.getBrands().subscribe(brands => { - this.brands = brands; - let allBrands = { id: null, brand: 'All' }; - this.brands.unshift(allBrands); - console.log('brands retrieved: ' + brands.length); + getOrders() { + this.service.getOrders().subscribe(orders => { + this.orders = orders; + console.log('orders items retrieved: ' + orders.length); }); } } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts index ef3bec4d7..51f1bb0d9 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.module.ts @@ -11,4 +11,4 @@ import { Pager } from '../shared/components/pager/pager'; declarations: [OrdersComponent], providers: [OrdersService] }) -export class CatalogModule { } +export class OrdersModule { } diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts index ee57cdc86..da1903a11 100644 --- a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/orders/orders.service.ts @@ -2,9 +2,7 @@ import { Injectable } from '@angular/core'; import { Response } from '@angular/http'; import { DataService } from '../shared/services/data.service'; -import { ICatalog } from '../shared/models/catalog.model'; -import { ICatalogBrand } from '../shared/models/catalogBrand.model'; -import { ICatalogType } from '../shared/models/catalogType.model'; +import { IOrder } from '../shared/models/order.model'; import 'rxjs/Rx'; import { Observable } from 'rxjs/Observable'; @@ -14,35 +12,16 @@ import 'rxjs/add/operator/map'; @Injectable() export class OrdersService { - private catalogUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/items'; - private brandUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/catalogbrands'; - private typesUrl: string = 'http://eshopcontainers:5101/api/v1/catalog/catalogtypes'; + private ordersUrl: string = 'http://eshopcontainers:5102/api/v1/orders'; constructor(private service: DataService) { } - getCatalog(pageIndex: number, pageSize: number, brand: number, type: number): Observable { - var url = this.catalogUrl; - if (brand || type) { - url = this.catalogUrl + '/type/' + ((type) ? type.toString() : 'null') + '/brand/' + ((brand) ? brand.toString() : 'null'); - } - - url = url + '?pageIndex=' + pageIndex + '&pageSize=' + pageSize; + getOrders(): Observable { + var url = this.ordersUrl; return this.service.get(url).map((response: Response) => { return response.json(); }); } - - getBrands(): Observable { - return this.service.get(this.brandUrl).map((response: Response) => { - return response.json(); - }); - } - - getTypes(): Observable { - return this.service.get(this.typesUrl).map((response: Response) => { - return response.json(); - }); - }; -} \ No newline at end of file +} diff --git a/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts new file mode 100644 index 000000000..35be8bafb --- /dev/null +++ b/src/Web/WebSPA/eShopOnContainers.WebSPA/Client/modules/shared/models/order.model.ts @@ -0,0 +1,6 @@ +export interface IOrder { + ordernumber: number, + date: Date, + status: string, + total: number +}
- PRODUCT + + ORDER NUMBER - + DATE - BRAND + TOTAL - PRICE - - QUANTITY - - COST + STATUS
{{item.productName}}ROSLYN$ {{item.unitPrice}} - - $ {{item.unitPrice * item.quantity}}
-
-
TOTAL
- $ {{totalPrice}} -
+
{{order.ordernumber}}{{order.date | date:'short'}}$ {{order.total}}{{order.status}} + Detail