Merge branch 'master' into mapconfig-provirers-refactor

This commit is contained in:
Daniel García Aubert
2018-06-11 15:50:52 +02:00
18 changed files with 182 additions and 352 deletions
+1
View File
@@ -39,6 +39,7 @@ Bug Fixes:
- Static maps fails for unsupported formats
- Handling errors extracting the column type on dataviews
- Fix `meta.stats.estimatedFeatureCount` for aggregations and queries with tokens
- Static maps filters correctly if `layer` option is passed in the url.
## 6.1.0
Released 2018-04-16
+14 -22
View File
@@ -13,21 +13,6 @@ var config = {
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.localhost'
// DEPRECATED: use routes property instead
// ---------------------------------------
// Base URLs for the APIs
//
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
@@ -76,13 +61,13 @@ var config = {
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// This URLs depend on how `routes` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `routes`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
@@ -102,12 +87,12 @@ var config = {
// idle socket timeout, in milliseconds
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: false
,cache_enabled: true
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
,log_filename: undefined
,log_filename: 'logs/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'development_cartodb_user_<%= user_id %>'
@@ -133,7 +118,7 @@ var config = {
,statsd: {
host: 'localhost',
port: 8125,
prefix: 'dev.',
prefix: 'dev.', // could be hostname, better not containing dots
cacheDns: true
// support all allowed node-statsd options
}
@@ -219,7 +204,8 @@ var config = {
persist_connection: false,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
max_size: 500
max_size: 500,
twkb_encoding: true
},
limits: {
@@ -282,7 +268,7 @@ var config = {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: '/tmp/analysis.log'
filename: 'logs/node-windshaft-analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
@@ -352,6 +338,12 @@ var config = {
// X-Tiler-Profile header containing elapsed timing for various
// steps taken for producing the response.
,useProfiler:true
,serverMetadata: {
cdn_url: {
http: undefined,
https: undefined
}
}
// Settings for the health check available at /health
,health: {
enabled: false,
+6 -28
View File
@@ -13,21 +13,6 @@ var config = {
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.cartodb\\.com$'
// DEPRECATED: use routes property instead
// ---------------------------------------
// Base URLs for the APIs
//
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
@@ -76,13 +61,13 @@ var config = {
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// This URLs depend on how `routes` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `routes`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.cdn_url}}/{{=it.user}}/api/v1/map',
@@ -145,15 +130,7 @@ var config = {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false,
dbPoolParams: {
// maximum number of resources to create at any given time
size: 16,
// max milliseconds a resource can go unused before it should be destroyed
idleTimeout: 3000,
// frequency to check for idle resources
reapInterval: 1000
}
usePostGIS: false
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -227,7 +204,8 @@ var config = {
persist_connection: false,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
max_size: 500
max_size: 500,
twkb_encoding: true
},
limits: {
@@ -290,7 +268,7 @@ var config = {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'logs/analysis.log'
filename: 'logs/node-windshaft-analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
+9 -23
View File
@@ -13,21 +13,6 @@ var config = {
// from hostname. Must have a single grabbing block.
,user_from_host: '^(.*)\\.cartodb\\.com$'
// DEPRECATED: use routes property instead
// ---------------------------------------
// Base URLs for the APIs
//
// See http://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Base url for the Templated Maps API
// "/api/v1/maps/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)'
// Base url for the Detached Maps API
// "/api/v1/maps" is the the new API,
// "/tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
@@ -76,16 +61,16 @@ var config = {
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// This URLs depend on how `routes` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `routes`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
http: 'http://{{=it.cdn_url}}/{{=it.user}}/api/v1/map',
https: 'https://{{=it.cdn_url}}/{{=it.user}}/api/v1/map'
}
@@ -103,7 +88,7 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: true
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms (:res[X-Tiler-Profiler]) -> :res[Content-Type] (:res[X-Tiler-Errors])'
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
@@ -133,7 +118,7 @@ var config = {
,statsd: {
host: 'localhost',
port: 8125,
prefix: 'stage.:host.',
prefix: 'stage.:host.', // could be hostname, better not containing dots
cacheDns: true
// support all allowed node-statsd options
}
@@ -219,7 +204,8 @@ var config = {
persist_connection: false,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
max_size: 500
max_size: 500,
twkb_encoding: true
},
limits: {
@@ -282,7 +268,7 @@ var config = {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'logs/analysis.log'
filename: 'logs/node-windshaft-analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
@@ -360,7 +346,7 @@ var config = {
}
// Settings for the health check available at /health
,health: {
enabled: false,
enabled: true,
username: 'localhost',
z: 0,
x: 0,
+25 -37
View File
@@ -13,21 +13,6 @@ var config = {
// from hostname. Must have a single grabbing block.
,user_from_host: '(.*)'
// DEPRECATED: use routes property instead
// ---------------------------------------
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
//
// Base url for the Templated Maps API
// "/api/v1/map/named" is the new API,
// "/tiles/template" is for compatibility with versions up to 1.6.x
,base_url_templated: '(?:/api/v1/map/named|/user/:user/api/v1/map/named|/tiles/template)'
// Base url for the Detached Maps API
// "maps" is the the new API,
// "tiles/layergroup" is for compatibility with versions up to 1.6.x
,base_url_detached: '(?:/api/v1/map|/user/:user/api/v1/map|/tiles/layergroup)'
// Base URLs for the APIs
//
// See https://github.com/CartoDB/Windshaft-cartodb/wiki/Unified-Map-API
@@ -76,16 +61,17 @@ var config = {
// Resource URLs expose endpoints to request/retrieve metadata associated to Maps: dataviews, analysis node status.
//
// This URLs depend on how `base_url_detached` and `user_from_host` are configured: the application can be
// This URLs depend on how `routes` and `user_from_host` are configured: the application can be
// configured to accept request with the {user} in the header host or in the request path.
// It also might depend on the configured cdn_url via `serverMetadata.cdn_url`.
//
// This template allows to make the endpoints generation more flexible, the template exposes the following params:
// 1. {{=it.cdn_url}}: will be used when `serverMetadata.cdn_url` exists.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `base_url_detached`.
// 2. {{=it.user}}: will use the username as extraced from `user_from_host` or `routes`.
// 3. {{=it.port}}: will use the `port` from this very same configuration file.
,resources_url_templates: {
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map'
http: 'http://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map',
https: 'https://{{=it.user}}.localhost.lan:{{=it.port}}/api/v1/map'
}
// Maximum number of connections for one process
@@ -102,11 +88,11 @@ var config = {
,socket_timeout: 600000
,enable_cors: true
,cache_enabled: false
,log_format: '[:date] :req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
,log_format: ':req[X-Real-IP] :method :req[Host]:url :status :response-time ms -> :res[Content-Type] (:res[X-Tiler-Profiler]) (:res[X-Tiler-Errors])'
// If log_filename is given logs will be written
// there, in append mode. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
//,log_filename: 'logs/node-windshaft.log'
,log_filename: '/tmp/node-windshaft.log'
// Templated database username for authorized user
// Supported labels: 'user_id' (read from redis)
,postgres_auth_user: 'test_windshaft_cartodb_user_<%= user_id %>'
@@ -114,8 +100,6 @@ var config = {
// Supported labels: 'user_id', 'user_password' (both read from redis)
,postgres_auth_pass: 'test_windshaft_cartodb_user_<%= user_id %>_pass'
,postgres: {
// Parameters to pass to datasource plugin of mapnik
// See http://github.com/mapnik/mapnik/wiki/PostGIS
user: "test_windshaft_publicuser",
password: "public",
host: '127.0.0.1',
@@ -129,12 +113,12 @@ var config = {
reapInterval: 1000
}
}
,mapnik_version: ''
,mapnik_version: undefined
,mapnik_tile_format: 'png8:m=h'
,statsd: {
host: 'localhost',
port: 8125,
prefix: 'test.:host.',
prefix: 'test.:host.', // could be hostname, better not containing dots
cacheDns: true
// support all allowed node-statsd options
}
@@ -146,15 +130,7 @@ var config = {
//If enabled, MVTs will be generated with PostGIS directly, instead of using Mapnik,
//PostGIS 2.4 is required for this to work
//If disabled it will use Mapnik MVT generation
usePostGIS: false,
dbPoolParams: {
// maximum number of resources to create at any given time
size: 16,
// max milliseconds a resource can go unused before it should be destroyed
idleTimeout: 3000,
// frequency to check for idle resources
reapInterval: 1000
}
usePostGIS: false
},
mapnik: {
// The size of the pool of internal mapnik backend
@@ -228,7 +204,8 @@ var config = {
persist_connection: false,
simplify_geometries: true,
use_overviews: true, // use overviews to retrieve raster
max_size: 500
max_size: 500,
twkb_encoding: false
},
limits: {
@@ -246,10 +223,15 @@ var config = {
// If enabled Mapnik will reuse the features retrieved from the database
// instead of requesting them once per style inside a layer
'cache-features': true,
'cache-features': false,
// Require metrics to the renderer
metrics: false
metrics: false,
// Options for markers attributes, ellipses and images caches
markers_symbolizer_caches: {
disabled: false
}
},
http: {
timeout: 2000, // the timeout in ms for a http tile request
@@ -288,7 +270,7 @@ var config = {
// If filename is given logs comming from analysis client will be written
// there, in append mode. Otherwise 'log_filename' is used. Otherwise stdout is used (default).
// Log file will be re-opened on receiving the HUP signal
filename: 'node-windshaft.log'
filename: '/tmp/node-windshaft-analysis.log'
},
// Define max execution time in ms for analyses or tags
// If analysis or tag are not found in redis this values will be used as default.
@@ -358,6 +340,12 @@ var config = {
// X-Tiler-Profile header containing elapsed timing for various
// steps taken for producing the response.
,useProfiler:true
,serverMetadata: {
cdn_url: {
http: undefined,
https: undefined
}
}
// Settings for the health check available at /health
,health: {
enabled: false,
+4 -1
View File
@@ -11,7 +11,7 @@ Begin by instantiating either a Named or Anonymous Map using the `layergroupid t
#### Definition
```bash
GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format}
GET /api/v1/map/static/center/{token}/{z}/{lat}/{lng}/{width}/{height}.{format}{{?}extra_options}
```
#### Params
@@ -58,6 +58,9 @@ Note: you can see this endpoint as
GET /api/v1/map/static/bbox/{token}/{west},{south},{east},{north}/{width}/{height}.{format}`
```
#### Extra options
* Layer: List of layers to be shown in the image (by default `all`), for example `?layer=0,1`.
### Named Map
#### Definition
@@ -10,12 +10,13 @@ module.exports = function createMapStoreMapConfigProvider (
return function createMapStoreMapConfigProviderMiddleware (req, res, next) {
const { user, token, cache_buster, api_key } = res.locals;
const { dbuser, dbname, dbpassword, dbhost, dbport } = res.locals;
const { layer, z, x, y, scale_factor, format } = req.params;
const { layer: layerFromParams, z, x, y, scale_factor, format } = req.params;
const { layer: layerFromQuery } = req.query;
const params = {
user, token, cache_buster, api_key,
dbuser, dbname, dbpassword, dbhost, dbport,
layer, z, x, y, scale_factor, format
layer: (layerFromQuery || layerFromParams), z, x, y, scale_factor, format
};
if (forcedFormat) {
@@ -105,7 +105,7 @@ function authorizedByAPIKey ({ authBackend, action, label }) {
}
if (!authenticated) {
const error = new Error(`Only authenticated user can ${action} templated maps`);
const error = new Error(`Only authenticated users can ${action} templated maps`);
error.http_status = 403;
error.label = label;
return next(error);
-39
View File
@@ -1,5 +1,3 @@
var _ = require('underscore'); // AUTH_FALLBACK
/**
*
* @param {PgConnection} pgConnection
@@ -77,11 +75,6 @@ AuthBackend.prototype.authorizedByAPIKey = function(user, res, callback) {
return callback(err);
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
apikey.databaseRole = composeUserDatabase(apikey);
apikey.databasePassword = composeDatabasePassword(apikey);
if ( !isValidApiKey(apikey)) {
const error = new Error('Unauthorized');
error.type = 'auth';
@@ -113,38 +106,6 @@ AuthBackend.prototype.authorizedByAPIKey = function(user, res, callback) {
});
};
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function composeUserDatabase (apikey) {
if (shouldComposeUserDatabase(apikey)) {
return _.template(global.environment.postgres_auth_user, apikey);
}
return apikey.databaseRole;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function composeDatabasePassword (apikey) {
if (shouldComposeDatabasePassword(apikey)) {
return global.environment.postgres.password;
}
return apikey.databasePassword;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function shouldComposeDatabasePassword (apikey) {
return !apikey.databasePassword && global.environment.postgres.password;
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
function shouldComposeUserDatabase(apikey) {
return !apikey.databaseRole && apikey.user_id && global.environment.postgres_auth_user;
}
function isNameNotFoundError (err) {
return err.message && -1 !== err.message.indexOf('name not found');
}
-26
View File
@@ -32,12 +32,6 @@ PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callba
params.dbuser = apikey.databaseRole;
params.dbpassword = apikey.databasePassword;
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
if (!params.dbuser && apikey.user_id && global.environment.postgres_auth_user) {
params.dbuser = _.template(global.environment.postgres_auth_user, apikey);
}
return callback();
});
} else if (apikeyType === 'regular') { //Actually it can be any type of api key
@@ -52,20 +46,6 @@ PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callba
params.dbuser = apikey.databaseRole;
params.dbpassword = apikey.databasePassword;
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
// master apikey has been recreated from user's metadata
if (!params.dbuser && apikey.user_id && apikey.type === 'master' && global.environment.postgres_auth_user) {
params.dbuser = _.template(global.environment.postgres_auth_user, apikey);
}
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
// default apikey has been recreated from user's metadata
if (!params.dbpassword && global.environment.postgres.password) {
params.dbpassword = global.environment.postgres.password;
}
return callback();
});
} else if (apikeyType === 'default') {
@@ -80,12 +60,6 @@ PgConnection.prototype.setDBAuth = function(username, params, apikeyType, callba
params.dbuser = apikey.databaseRole;
params.dbpassword = apikey.databasePassword;
//Remove this block when Auth fallback is not used anymore
// AUTH_FALLBACK
if (!params.dbpassword && global.environment.postgres.password) {
params.dbpassword = global.environment.postgres.password;
}
return callback();
});
} else {
+2 -3
View File
@@ -1,4 +1,4 @@
var os = require('os');
const fqdn = require('@carto/fqdn-sync');
var _ = require('underscore');
var OverviewsQueryRewriter = require('./utils/overviews_query_rewriter');
@@ -36,8 +36,7 @@ rendererConfig.mapnik.queryRewriter = overviewsQueryRewriter;
// See https://github.com/CartoDB/Windshaft-cartodb/issues/153
if (global.environment.statsd) {
if (global.environment.statsd.prefix) {
var host_token = os.hostname().split('.').reverse().join('.');
global.environment.statsd.prefix = global.environment.statsd.prefix.replace(/:host/, host_token);
global.environment.statsd.prefix = global.environment.statsd.prefix.replace(/:host/, fqdn.reverse());
}
}
+2 -1
View File
@@ -24,12 +24,13 @@
"Simon Martin <simon@carto.com>"
],
"dependencies": {
"@carto/fqdn-sync": "0.2.2",
"basic-auth": "2.0.0",
"body-parser": "1.18.3",
"camshaft": "0.61.10",
"cartodb-psql": "0.11.0",
"cartodb-query-tables": "0.3.0",
"cartodb-redis": "1.0.0",
"cartodb-redis": "1.0.1",
"debug": "3.1.0",
"dot": "1.1.2",
"express": "4.16.3",
+21
View File
@@ -261,6 +261,27 @@ describe('named-maps analysis', function() {
);
});
it('should fail to retrieve static map preview via layergroup ' +
'when filtering by invalid layers', function(done) {
assert.response(
server,
{
url: '/api/v1/map/static/center/' + layergroupid + '/4/42/-3/320/240.png?layer=1',
method: 'GET',
encoding: 'binary',
headers: {
host: username
}
},
{
status: 400
},
function(res, err) {
done(err);
}
);
});
it('should return and an error requesting unsupported image format', function(done) {
assert.response(
server,
@@ -1,6 +1,3 @@
//Remove this file when Auth fallback is not used anymore
// AUTH_FALLBACK
const assert = require('../../support/assert');
const testHelper = require('../../support/test_helper');
const CartodbWindshaft = require('../../../lib/cartodb/server');
@@ -44,7 +41,7 @@ var pointSqlMaster = "select * from test_table_private_1";
var pointSqlPublic = "select * from test_table";
var keysToDelete;
describe('authorization fallback', function () {
describe('Basic authorization use cases', function () {
var server;
before(function () {
@@ -63,7 +60,7 @@ describe('authorization fallback', function () {
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth', '4444'),
createRequest(layergroup, 'localhost', '1234'),
{
status: 200
},
@@ -75,7 +72,7 @@ describe('authorization fallback', function () {
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
keysToDelete['user:localhost:mapviews:global'] = 5;
done();
}
@@ -87,7 +84,7 @@ describe('authorization fallback', function () {
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth', 'default_public'),
createRequest(layergroup, 'localhost', 'default_public'),
{
status: 200
},
@@ -99,54 +96,27 @@ describe('authorization fallback', function () {
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
keysToDelete['user:localhost:mapviews:global'] = 5;
done();
}
);
});
it("succeed with default - sending no api key token", function (done) {
it("fail with non-existent api key", function (done) {
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth'),
createRequest(layergroup, 'localhost', 'THIS-API-KEY-DOESNT-EXIST'),
{
status: 200
status: 401
},
function (res, err) {
assert.ifError(err);
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
done();
}
);
});
it("succeed with non-existent api key - defaults to default", function (done) {
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth', 'THIS-API-KEY-DOESNT-EXIST'),
{
status: 200
},
function (res, err) {
assert.ifError(err);
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
keysToDelete['user:user_previous_to_project_auth:mapviews:global'] = 5;
assert.ok(parsed.hasOwnProperty('errors'));
assert.equal(parsed.errors.length, 1);
assert.ok(parsed.errors[0].match(/Unauthorized/));
done();
}
);
@@ -156,7 +126,7 @@ describe('authorization fallback', function () {
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth', 'default_public'),
createRequest(layergroup, 'localhost', 'default_public'),
{
status: 403
},
@@ -168,19 +138,44 @@ describe('authorization fallback', function () {
);
});
it("fail with non-existent api key - defaults to default", function (done) {
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
describe('No api key provided - fallback to default_public', function () {
it("succeed with default - public dataset", function (done) {
var layergroup = singleLayergroupConfig(pointSqlPublic, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'user_previous_to_project_auth', 'THIS-API-KEY-DOESNT-EXIST'),
{
status: 403
},
function (res, err) {
assert.ifError(err);
assert.response(server,
createRequest(layergroup, 'localhost'),
{
status: 200
},
function (res, err) {
assert.ifError(err);
done();
}
);
var parsed = JSON.parse(res.body);
assert.ok(parsed.layergroupid);
assert.equal(res.headers['x-layergroup-id'], parsed.layergroupid);
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
done();
}
);
});
it("fail with default - private dataset", function (done) {
var layergroup = singleLayergroupConfig(pointSqlMaster, '#layer { marker-fill:red; }');
assert.response(server,
createRequest(layergroup, 'localhost'),
{
status: 403
},
function (res, err) {
assert.ifError(err);
done();
}
);
});
});
});
-12
View File
@@ -132,18 +132,6 @@ HMSET rails:users:cartodb250user id ${TESTUSERID} \
EOF
# Remove this block when Auth fallback is not used anymore
# AUTH_FALLBACK
# A user to test auth fallback to no api keys mode
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 5
HMSET rails:users:user_previous_to_project_auth id ${TESTUSERID} \
database_name "${TEST_DB}" \
database_host "localhost" \
database_password "${TESTPASS}" \
database_publicuser "${PUBLICUSER}"\
map_key 4444
EOF
cat <<EOF | redis-cli -p ${REDIS_PORT} -n 0
HSET rails:${TEST_DB}:my_table infowindow "this, that, the other"
HSET rails:${TEST_DB}:test_table_private_1 privacy "0"
+3 -3
View File
@@ -1268,9 +1268,6 @@ TestClient.prototype.setUserDatabaseTimeoutLimit = function (timeoutLimit, callb
const dbuser = _.template(global.environment.postgres_auth_user, { user_id: 1 });
const publicuser = global.environment.postgres.user;
// we need to guarantee all new connections have the new settings
helper.cleanPGPoolConnections();
const psql = new PSQL({
user: 'postgres',
dbname: dbname,
@@ -1278,6 +1275,9 @@ TestClient.prototype.setUserDatabaseTimeoutLimit = function (timeoutLimit, callb
port: global.environment.postgres.port
});
// we need to guarantee all new connections have the new settings
psql.end();
step(
function configureTimeouts () {
const timeoutSQLs = [
+1 -9
View File
@@ -13,7 +13,6 @@ var lzmaWorker = new LZMA();
var redis = require('redis');
var log4js = require('log4js');
var pg = require('pg');
const setICUEnvVariable = require('../../lib/cartodb/utils/icu_data_env_setter');
// set environment specific variables
@@ -109,7 +108,6 @@ afterEach(function(done) {
'rails:users:localhost:map_key': true,
'rails:users:cartodb250user': true,
'rails:users:localhost': true,
'rails:users:user_previous_to_project_auth': true, // AUTH_FALLBACK
'api_keys:localhost:1234': true,
'api_keys:localhost:default_public': true,
'api_keys:cartodb250user:4321': true,
@@ -148,11 +146,6 @@ afterEach(function(done) {
});
});
function cleanPGPoolConnections () {
// TODO: this method will be replaced by psql.end
pg.end();
}
function deleteRedisKeys(keysToDelete, callback) {
if (Object.keys(keysToDelete).length === 0) {
@@ -215,6 +208,5 @@ module.exports = {
checkSurrogateKey: checkSurrogateKey,
checkCache: checkCache,
rmdirRecursiveSync: rmdirRecursiveSync,
configureMetadata,
cleanPGPoolConnections
configureMetadata
};
+41 -91
View File
@@ -2,6 +2,10 @@
# yarn lockfile v1
"@carto/fqdn-sync@0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@carto/fqdn-sync/-/fqdn-sync-0.2.2.tgz#cd7c645ed66690a09249b554d254a0f53963b2dc"
"@carto/mapnik@3.6.2-carto.10":
version "3.6.2-carto.10"
resolved "https://registry.yarnpkg.com/@carto/mapnik/-/mapnik-3.6.2-carto.10.tgz#a97c951dcdac09d0eb35b3ea71e5eeaa206c1af6"
@@ -11,7 +15,7 @@
node-pre-gyp "0.10.0"
protozero "1.5.1"
"@carto/tilelive-bridge@github:cartodb/tilelive-bridge#2.5.1-cdb9":
"@carto/tilelive-bridge@cartodb/tilelive-bridge#2.5.1-cdb9":
version "2.5.1-cdb9"
resolved "https://codeload.github.com/cartodb/tilelive-bridge/tar.gz/5129e43223cb55daed31373c7a36c98eb6178fc1"
dependencies:
@@ -23,7 +27,7 @@
version "1.0.5"
resolved "https://registry.yarnpkg.com/@mapbox/sphericalmercator/-/sphericalmercator-1.0.5.tgz#70237b9774095ed1cfdbcea7a8fd1fc82b2691f2"
"abaculus@github:cartodb/abaculus#2.0.3-cdb10":
abaculus@cartodb/abaculus#2.0.3-cdb10:
version "2.0.3-cdb10"
resolved "https://codeload.github.com/cartodb/abaculus/tar.gz/90d537028bb8af8a35e7a40c46493066dd8a76b3"
dependencies:
@@ -272,7 +276,7 @@ camshaft@0.61.10:
dot "^1.0.3"
request "2.85.0"
"canvas@github:cartodb/node-canvas#1.6.2-cdb2":
canvas@cartodb/node-canvas#1.6.2-cdb2:
version "1.6.2-cdb2"
resolved "https://codeload.github.com/cartodb/node-canvas/tar.gz/8acf04557005c633f9e68524488a2657c04f3766"
dependencies:
@@ -290,17 +294,17 @@ carto@0.16.3:
semver "^5.1.0"
yargs "^4.2.0"
carto@cartodb/carto#master:
version "0.15.1"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/31abb8bee02df605521247b0223d508320f7d4c8"
carto@cartodb/carto#0.15.1-cdb3:
version "0.15.1-cdb3"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
dependencies:
mapnik-reference "~6.0.2"
optimist "~0.6.0"
underscore "1.8.3"
"carto@github:cartodb/carto#0.15.1-cdb3":
version "0.15.1-cdb3"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/945f5efb74fd1af1f5e1f69f409f9567f94fb5a7"
carto@cartodb/carto#master:
version "0.15.1"
resolved "https://codeload.github.com/cartodb/carto/tar.gz/31abb8bee02df605521247b0223d508320f7d4c8"
dependencies:
mapnik-reference "~6.0.2"
optimist "~0.6.0"
@@ -324,9 +328,9 @@ cartodb-query-tables@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/cartodb-query-tables/-/cartodb-query-tables-0.3.0.tgz#56e18d869666eb2e8e2cb57d0baf3acc923f8756"
cartodb-redis@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-1.0.0.tgz#83b4888ba7abb5d5895c8958b7e15cf4882602aa"
cartodb-redis@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cartodb-redis/-/cartodb-redis-1.0.1.tgz#b4ecf2d9460170a5e7928dc0b97d2de44772b6d2"
dependencies:
dot "~1.0.2"
redis-mpool "^0.5.0"
@@ -1066,7 +1070,7 @@ htmlparser2@3.8.x:
entities "1.0"
readable-stream "1.1"
http-errors@1.6.2, http-errors@~1.6.2:
http-errors@1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
dependencies:
@@ -1075,7 +1079,7 @@ http-errors@1.6.2, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-errors@1.6.3, http-errors@~1.6.3:
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
@@ -1376,14 +1380,10 @@ lodash@3.7.x:
version "3.7.0"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45"
lodash@^4.17.5:
lodash@^4.17.5, lodash@^4.5.1:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
lodash@^4.5.1:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
log4js@cartodb/log4js-node#cdb:
version "0.6.25"
resolved "https://codeload.github.com/cartodb/log4js-node/tar.gz/145d5f91e35e7fb14a6278cbf7a711ced6603727"
@@ -1470,18 +1470,18 @@ mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.17, mime-types@~2.1.7:
version "2.1.17"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
dependencies:
mime-db "~1.30.0"
mime-types@~2.1.18:
mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.7:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
dependencies:
mime-db "~1.33.0"
mime-types@~2.1.17:
version "2.1.17"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
dependencies:
mime-db "~1.30.0"
mime@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
@@ -1572,11 +1572,11 @@ mv@~2:
ncp "~2.0.0"
rimraf "~2.4.0"
nan@2.10.0, nan@^2.0.8:
nan@2.10.0, nan@^2.0.8, nan@^2.4.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
nan@^2.3.4, nan@^2.4.0:
nan@^2.3.4:
version "2.8.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
@@ -1885,7 +1885,7 @@ pg-types@1.*:
postgres-date "~1.0.0"
postgres-interval "^1.1.0"
"pg@github:CartoDB/node-postgres#6.4.2-cdb1":
pg@CartoDB/node-postgres#6.4.2-cdb1:
version "6.4.2"
resolved "https://codeload.github.com/CartoDB/node-postgres/tar.gz/449fac1d6da711ffcc6694ae3c89f85244f48bdc"
dependencies:
@@ -2008,11 +2008,11 @@ punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@6.5.1, qs@~6.5.1:
qs@6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
qs@6.5.2, qs@^6.5.1:
qs@6.5.2, qs@^6.5.1, qs@~6.5.1:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@@ -2187,7 +2187,7 @@ request@2.85.0:
tunnel-agent "^0.6.0"
uuid "^3.1.0"
request@2.87.0:
request@2.87.0, request@2.x, request@^2.55.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
dependencies:
@@ -2212,33 +2212,6 @@ request@2.87.0:
tunnel-agent "^0.6.0"
uuid "^3.1.0"
request@2.x, request@^2.55.0:
version "2.83.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
hawk "~6.0.2"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
stringstream "~0.0.5"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -2269,11 +2242,11 @@ rimraf@~2.4.0:
dependencies:
glob "^6.0.1"
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
safe-buffer@^5.1.2:
safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -2289,18 +2262,14 @@ sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
semver@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7"
semver@5.5.0, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
semver@~4.3.3:
version "4.3.6"
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
@@ -2420,14 +2389,10 @@ speedometer@~0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d"
sphericalmercator@1.0.5:
sphericalmercator@1.0.5, sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.5.tgz#ddc5a049e360e000d0fad9fc22c4071882584980"
sphericalmercator@1.0.x, sphericalmercator@~1.0.1, sphericalmercator@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/sphericalmercator/-/sphericalmercator-1.0.4.tgz#baad4e34187f06e87f2e92fc1280199fa1b01d4e"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
@@ -2465,15 +2430,7 @@ sshpk@^1.7.0:
jsbn "~0.1.0"
tweetnacl "~0.14.0"
"statuses@>= 1.3.1 < 2":
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
"statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
statuses@~1.4.0:
"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2", statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
@@ -2614,7 +2571,7 @@ through@2:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
"tilelive-mapnik@github:cartodb/tilelive-mapnik#0.6.18-cdb14":
tilelive-mapnik@cartodb/tilelive-mapnik#0.6.18-cdb14:
version "0.6.18-cdb14"
resolved "https://codeload.github.com/cartodb/tilelive-mapnik/tar.gz/6d06f728833d3e34d1adcd05567b3f4379f547bb"
dependencies:
@@ -2696,14 +2653,7 @@ type-detect@^4.0.0:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
type-is@~1.6.15:
version "1.6.15"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
dependencies:
media-typer "0.3.0"
mime-types "~2.1.15"
type-is@~1.6.16:
type-is@~1.6.15, type-is@~1.6.16:
version "1.6.16"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
dependencies: