QMUtilities 2.0.0
ip whitelisting security module and okta token validator
Requires Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4

QMUtilities
=============

Utility to validate incoming request to the backend python services
pypi library link: https://pypi.org/project/QMUtilities/

It validates IP and OKTA token of the incoming request
=============

1. IP Validate
-------------

It validates whether incoming request is from trusted or listed IP_LIST

Before every endpoint is served, it will check for the remote IP if it exists in the list of white listed IPs  otherwise it throws abort error:

```
HTTPErr: 403 Abort
```

2. Validate Token
------------
It also validates okta token in incoming header

Authentication header format:

    Authentication: agency <id> user <id>:<assertid>

Setup
----------

```
1. Create VirtualENV
2. source VirtualENV
3. Install dependent libraries, please ignore if already installed. 
                boto3, pymemcache (pip install)
4. pip install QMUtilities
5. create a stored secret at secret manager and store below keys and correspoding values of memcache server
    
    aws_elastic_cache_hostname : <hostname of memcache server>
    port : <port details>
6. Add below blocks to the "view" file of the app

```

``` python
from flask import Flask
from security.validate import ValidateHeader

@app.before_request
def validate_header():
    ip_list = <list of IPs or import from config> 
    secret_name = <AWS secret name>
    memcache_host = CONFIG['memcache_host']
    memcache_port = CONFIG['memcache_port']
    
    ValidateHeader.check_whitelisting(IP_LIST=ip_list) #for ip validation

    #OKTA VALIDATOR
    #if host and port in config
    ValidateHeader.check_okta_token(MEMCACHE_HOST=memcache_host, MEMCACHE_PORT=memcache_port) #for okta token validation
    
    if host and port in AWS secret
    ValidateHeader.check_okta_token(SECRET_NAME=secret_name)
```


Nginx Routing
====

By default headers of the incoming request gets updated with localhost IP when it is passed to the backend Nginx server.
In order to get the real IP of the client/LAN, we need to do following configurations in the nginx config:

```
server {
    real_ip_recursive on;
}

location / {
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  X-Forwarded-For $remote_addr;
    proxy_set_header  X-Forwarded-Host $remote_addr;
   }
   
```

**sample incoming request header dict after naking above changes in Nginx**
```
{'wsgi.version': (1, 0), 'wsgi.url_scheme': 'http', 
'wsgi.input': '<_io.BufferedReader name=5>', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>,
'wsgi.multithread': True, 
'wsgi.multiprocess': False, 'wsgi.run_once': False, 
'werkzeug.server.shutdown': <function WSGIRequestHandler.make_environ.<locals>.shutdown_server at 0x7fba5d1bd598>, 
'SERVER_SOFTWARE': 'Werkzeug/0.14.1', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'PATH_INFO': '/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': 39534, 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '8002', 'SERVER_PROTOCOL': 'HTTP/1.0', 
'HTTP_HOST': '172.30.1.23', 
'HTTP_X_REAL_IP': '10.21.120.11', 
'HTTP_X_FORWARDED_FOR': '10.21.120.11', 
'HTTP_X_FORWARDED_HOST': '10.21.120.11', 
'HTTP_CONNECTION': 'close', 'HTTP_PRAGMA': 'no-cache', 
'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 
'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36', 
HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 
'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT_LANGUAGE': 'en-GB,en-US;q=0.9,en;q=0.8', 'werkzeug.request': <Request 'http://10.21.120.11/' [GET]>}

```