-
Notifications
You must be signed in to change notification settings - Fork 439
Description
Implementation proposal for requests rules
MapStore Core
MapStore is currently using the authenticationRules configuration to apply headers or parameters to a request that match the url pattern, here an example:
[
{
"urlPattern": ".*geostore.*",
"method": "bearer"
},
{
"urlPattern": ".*rest/config.*",
"method": "bearer"
},
{
"urlPattern": "http(s)?\\:\\/\\/gs-stable\\.(geo-solutions\\.it|geosolutionsgroup\\.com)(\\:443|\\:80)?\\/geoserver/.*",
"authkeyParamName": "authkey",
"method": "authkey"
}
]In the authenticationRules currently supports following methods:
authkeyadd a parameter using the security user token propertybeareradd an header using the security user token propertybasicadd an header using the security user authHeader propertybrowserWithCredentialsadd the withCredentials property to axios requestsheaderallow to add a set of static headers (see PR #10049 Add authenticationRules header method support #10050)
The proposal is to deprecate and later remove the authenticationRules property in favor of a new configuration called requestsConfigurationRules. This new configuration will allow to pass headers and/or parameters configuration removing the concept of method, in addition we should:
- save the configuration in the redux state
- introduce a new action called
updateRequestsRulesto allow to update the initialrequestsConfigurationRulesproperty. This will be useful to allow refreshing - ensure the correct updates of related components, in particular layers when the
requestsConfigurationRulesupdates in the state. Currently layer are listening only to securityToken. (We could think of an alternative property that just updates based on the security state changes so we continue to pass a string instead of a full object TBD)
Below and example of the updated configuration
"requestsConfigurationRules": [
{
"urlPattern": ".*geostore.*",
"expires": <Unix Timestamp>,
"headers": {
"Authorization": "Bearer {securityToken}"
}
},
{
"urlPattern": ".*rest/config.*",
"headers": {
"Authorization": "Bearer {securityToken}"
}
},
{
"urlPattern": "http(s)?\\:\\/\\/gs-stable\\.(geo-solutions\\.it|geosolutionsgroup\\.com)(\\:443|\\:80)?\\/geoserver/.*",
"params": {
"authkey": "{securityToken}"
}
}
],At the moment we have different ways used to extract the rules and apply them to the various application components:
- ajax requests
- getAuthenticationHeaders used in some OpenLayers layers
- addAuthenticationParameter used on layers and other component to extend parameters
- getAuthenticationParam used in some layers for Cesium
- addAuthenticationToUrl uses addAuthenticationParameter
- addAuthenticationToSLD uses addAuthenticationParameter
- SecureImage component includes logic to apply bearer or authkey methods
We should refactor the above and try to centralize as much as possible the logic, ideally we should have a single function:
// given a url we should return headers and/or parameters
const { headers, params } = getRequestConfigurationByUrl(url) Expand code implementation example for getRequestConfigurationByUrl
const getVariables = (str) => {
// TODO: review with a generic approach e.g.: /\{.*?\}/g
// but preventing issues with possible configured token e.g.: Bearer _uUns{gT%}asd
const variables = str.match(/\{securityToken\}/g);
return (variables || []).map((value) => ({ name: value.replace(/\{|\}/g, ''), value }));
};
const parseRequestConfiguration = (config = {}, securityProperties) => {
return Object.fromEntries(
Object.entries(config)
.map((entry) => {
const [name, value] = entry;
const variables = getVariables(value || '');
if (variables.length) {
const valid = variables.every((variable) => securityProperties[variable.name] !== undefined);
return valid
? [name, variables.reduce((str, variable) => str.replace(variable.value, securityProperties[variable.name]), value)]
: null;
}
return entry;
})
.filter(entry => entry)
);
};
export const getRequestConfigurationByUrl = (url) => {
// we should support also previuos behaviour with method in this file
const { headers, params } = getRequestConfigurationRule(url) || {};
const token = getToken();
const securityProperties = {
...(!isNil(token) && { securityToken: token })
};
const parsedHeaders = parseRequestConfiguration(headers, securityProperties);
const parsedParams = parseRequestConfiguration(params, securityProperties);
return {
...(!isEmpty(parsedHeaders) && { headers: parsedHeaders }),
...(!isEmpty(parsedParams) && { params: parsedParams })
};
};Note: we should pay attention to the reactor we will implement because recently the security popup implementation #11133 introduced a system to apply basic authentication via UI to catalog services and this behaviour should be maintained
Requirements:
- introduce a new
requestsConfigurationRulesconfiguration controllable with actions - refactor requests rules workflow and logic
- support for WMS, WFS, COG and 3D Tiles. If we are able let's support all type of layer that are using requests
- refresh logic will be moved to geonode-mapstore-client, MapStore will provide action to update rules
- setup rules using LOCAL_CONFIG_LOADED
- remove flag to apply or not rules, let's keep the legacy flag just for the initial parsing, if
useAuthenticationRulesfalse we are passing an empty array - review layers where we are applying headers and parameters to request using the new function. In particular remove all the forEach mutation we have now.
- refresh layer rules after we update the redux state
- [nice to have] let's see if we can move the basic auth inside interceptor