Skip to content

Implement dynamic request configurations #11644

@dsuren1

Description

@dsuren1

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:

  • authkey add a parameter using the security user token property
  • bearer add an header using the security user token property
  • basic add an header using the security user authHeader property
  • browserWithCredentials add the withCredentials property to axios requests
  • header allow 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 updateRequestsRules to allow to update the initial requestsConfigurationRules property. This will be useful to allow refreshing
  • ensure the correct updates of related components, in particular layers when the requestsConfigurationRules updates 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:

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 requestsConfigurationRules configuration 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 useAuthenticationRules false 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

Metadata

Metadata

Assignees

Labels

New Featureused for new functionalities

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions