diff --git a/lib/platformUtils/windows10Utils.js b/lib/platformUtils/windows10Utils.js index b44d48ae..59de5b61 100644 --- a/lib/platformUtils/windows10Utils.js +++ b/lib/platformUtils/windows10Utils.js @@ -14,6 +14,46 @@ var metadataItemTemplate= '\r\n\t\t'; var serviceEndpoint = 'http://cloudappx.azurewebsites.net'; +var baseAcurMatch; + +function findRuleByMatch(acurList, match) { + for (var i = 0; i < acurList.length; i++) { + if (acurList[i].match === match) { + return acurList[i]; + } + } +} + +function tryAddAcurToList(acurList, acur) { + // if match is '*', replace match with base match + if (acur.match === '*') { + acur.match = baseAcurMatch; + } + + // if the match url ends with '/*', remove the '*'. + if (acur.match.indexOf('/*', acur.match.length - 2) !== -1) { + acur.match = acur.match.substring(0, acur.match.length - 1); + } + + // ensure rule is not duplicated + var rule = findRuleByMatch(acurList, acur.match); + if (!rule) { + // if no type is specified in rule and access is 'none', ignore the rule + if (!acur.type && acur.runtimeAccess === 'none') { + return; + } + + rule = { match: acur.match }; + acurList.push(rule); + } + + // override the runtimeAccess property (if any) or use default value ('none') + rule.runtimeAccess = acur.runtimeAccess || rule.runtimeAccess || 'none'; + + // override the type (if any) or use default value ('include') + rule.type = acur.type || rule.type || 'include'; +} + function replaceManifestValues(w3cManifestInfo, content) { var w3cManifest = w3cManifestInfo.content; var timestamp = w3cManifestInfo.timestamp || new Date().toISOString().replace(/T/, ' ').replace(/\.[0-9]+/, ' '); @@ -45,54 +85,54 @@ function replaceManifestValues(w3cManifestInfo, content) { replacedContent = replacedContent.replace(/{MetadataItems}/g, metadataItems); - // Update access rules + // Update ACURs var indentationChars = '\r\n\t\t\t\t'; + var applicationContentUriRules = ''; + var acurList = []; - // Set the base access rule using the start_url's base url - var baseUrlPattern = url.resolve(w3cManifest.start_url, '/'); - var baseApiAccess = 'none'; + // Set the base acur rule using the start_url's base url + baseAcurMatch = url.resolve(w3cManifest.start_url, '/'); if (w3cManifest.scope && w3cManifest.scope.length) { // If the scope is defined, the base access rule is defined by the scope var parsedScopeUrl = url.parse(w3cManifest.scope); if (parsedScopeUrl.host && parsedScopeUrl.protocol) { - baseUrlPattern = w3cManifest.scope; + baseAcurMatch = w3cManifest.scope; } else { - baseUrlPattern = url.resolve(baseUrlPattern, w3cManifest.scope); + baseAcurMatch = url.resolve(baseAcurMatch, w3cManifest.scope); } } - // If the base access rule ends with '/*', remove the '*'. - if (baseUrlPattern.indexOf('/*', baseUrlPattern.length - 2) !== -1) { - baseUrlPattern = baseUrlPattern.substring(0, baseUrlPattern.length - 1); - } + // Add base rule to ACUR list + tryAddAcurToList(acurList, { 'match': baseAcurMatch, 'type': 'include' }); - var applicationContentUriRules = ''; - - // Add additional access rules - if (w3cManifest.mjs_access_whitelist && w3cManifest.mjs_access_whitelist instanceof Array) { - for (var j = 0; j < w3cManifest.mjs_access_whitelist.length; j++) { - var accessUrl = w3cManifest.mjs_access_whitelist[j].url; - // Ignore the '*' rule - if (accessUrl !== '*') { - // If the access url ends with '/*', remove the '*'. - if (accessUrl.indexOf('/*', accessUrl.length - 2) !== -1) { - accessUrl = accessUrl.substring(0, accessUrl.length - 1); - } - - var apiAccess = w3cManifest.mjs_access_whitelist[j].apiAccess || 'none'; - - if (accessUrl === baseUrlPattern) { - baseApiAccess = apiAccess; - } else { - applicationContentUriRules += indentationChars + ''; - } - } - } + // Add rules from mjs_access_whitelist to ACUR list + if (w3cManifest.mjs_access_whitelist) { + w3cManifest.mjs_access_whitelist.forEach(function(whitelistRule) { + tryAddAcurToList(acurList, { 'match': whitelistRule.url, 'type': 'include', 'runtimeAccess': whitelistRule.apiAccess }); + }); } + + // TODO: Add rules from mjs_extended_scope to ACUR list + + // Add rules from mjs_api_access to ACUR list + if (w3cManifest.mjs_api_access) { + w3cManifest.mjs_api_access.forEach(function (apiRule) { + // ensure rule applies to current platform + if (apiRule.platform && apiRule.platform.split(';') + .map(function (item) { return item.trim(); }) + .indexOf('windows10') < 0) { + return false; + } + + tryAddAcurToList(acurList, { match: apiRule.match, runtimeAccess: apiRule.access }); + }); + } - // Added base rule - applicationContentUriRules = '' + applicationContentUriRules; + // Create XML entries for ACUR rules + acurList.forEach(function (acur) { + applicationContentUriRules += indentationChars + ''; + }); replacedContent = replacedContent.replace(/{ApplicationContentUriRules}/g, applicationContentUriRules); diff --git a/test/manifestTools/transformations/windows10.js b/test/manifestTools/transformations/windows10.js index a4c69d34..cd2701b0 100644 --- a/test/manifestTools/transformations/windows10.js +++ b/test/manifestTools/transformations/windows10.js @@ -383,7 +383,7 @@ describe('transformation: Windows 10 Manifest', function () { }); }); - it('Should enable API access in base rule', function (done) { + it('Should use mjs_access_whitelist to enable API access in base ACUR', function (done) { var siteUrl = 'http://url.com/something?query'; var shortName = 'shortName'; @@ -418,7 +418,7 @@ describe('transformation: Windows 10 Manifest', function () { }); }); - it('Should enable API in secondary rule but not in base rule', function (done) { + it('Should use mjs_access_whitelist to enable API access in secondary ACUR but not in base ACUR', function (done) { var siteUrl = 'http://url.com/something?query'; var shortName = 'shortName'; @@ -453,5 +453,261 @@ describe('transformation: Windows 10 Manifest', function () { done(); }); }); + + it('Should use mjs_api_access to enable API access in base ACUR', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_api_access': [ + { 'match': 'http://url.com/', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should use mjs_api_access to enable API access in secondary ACUR but not in base ACUR', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should add different ACURs from mjs_api_access and mjs_access_whitelist if match setting is different', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_access_whitelist': [ + { 'url': 'http://url.com/otherpath/' } + ], + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should add single ACUR from mjs_api_access and mjs_access_whitelist if both have same match setting', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_access_whitelist': [ + { 'url': 'http://url.com/somepath/' } + ], + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should add ACUR from mjs_api_access if windows10 is in platform', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'platform': 'windows10', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should not add ACUR from mjs_api_access if windows10 is not in platform', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'platform': 'other', 'access': 'all' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); + + it('Should not add ACUR from mjs_api_access if access type is \'none\' and match setting is not whitelisted', function (done) { + var siteUrl = 'http://url.com/something?query'; + var shortName = 'shortName'; + + var originalManifestInfo = { + content: { + 'start_url': siteUrl, + 'short_name': shortName, + 'mjs_api_access': [ + { 'match': 'http://url.com/somepath/', 'platform': 'windows10', 'access': 'none' } + ] + } + }; + + transformation.convertFromBase(originalManifestInfo, function (err, result) { + should.not.exist(err); + should.exist(result); + /*jshint -W030 */ + result.should.have.property('content').which.is.an.Object; + result.should.have.property('format', 'windows10'); + + var manifest = result.content; + + manifest.should.have.property('rawData'); + + var expectedContentUriRules = '' + + '' + + ''; + + manifest.rawData.replace(/[\t\r\n]/g, '').indexOf(expectedContentUriRules).should.be.above(-1); + + done(); + }); + }); }); });