sinesp-django-auth 0.1
Django app to allow external authentication through out Sinesp
License: MIT
Requires Python: >=3.8

# Sinesp-django-auth

-----------
Aplicativo Django para permitir autenticação externa por meio do Sinesp. A documentação detalhada está no diretório `docs`.

## Sumário
1. [`Introdução`](#introduo)
2. [`Quick start`](#quick-start)

## Introdução
O projeto, idealizado pela Polícia Federal (PF), fora desenvolvida, em sua grande totalidade, sobre framework [Django](https://www.djangoproject.com/) e [Geonode](https://geonode.org/) em sua quinta versão. Além disso, conta também com estrutura moderna, sofisticada, containerizada e de fácil implantação. 

Dentre os itens desejáveis nesta versão, a incorporação do serviço de autenticação *Single Sign-on* (SSO) via [Sinesp](https://seguranca.sinesp.gov.br/sinesp-seguranca/login.jsf) (Sistema Nacional de Informações de Segurança Pública) era um destes itens, já que grande parte dos usuários da plataforma, já possuem seus perfis vinculados à respectiva plataforma. 

Sendo assim, este projeto/repositório consiste em um *case* de teste que simula o funcionamento independente da plataforma Inteligeo5 junto ao processo de autenticação de usuários via Sinesp. A idéia é construir a solução de verificação de *tokens*, providos pelo Sinesp aos usuários autenticados, sobre o *case* `sinesp-test` (simulando Inteligeo5), de forma que usuários autenticados possam ser entendidos pela aplicação, e suas sessões criadas corretamente.  

## *Quick start*

1. Depois de ter seu ambiente virtual pronto e ativado, instale: 
```
pip install sinesp-django-auth
```

2. Em seu projeto Django, adicione `sinesp` a variavel `INSTALLED_APPS` em seu `setting.py`:
```
    INSTALLED_APPS = [
        ...
        'sinesp',
    ]
```

3. Adicione `AUTHENTICATION_BACKEND`:
```
    AUTHENTICATION_BACKENDS = [
        'sinesp.backends.SinespBackend',
        'django.contrib.auth.backends.ModelBackend',
    ]
```

4. Edite `TEMPLATES` para:
```
    import os

    TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [TEMPLATE_DIR, ],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
```

5. Configure `nginx.conf` utilizando o *template* disponível em `nginx/nginx.conf`:

    4.1. Primeiro, instale `nginx`:
    ```
    sudo apt install nginx
    ```
   
    4.2. Mova o arquivo `nginx.conf` em `/etc/nginx/conf.d/`. O template está localizado na pasta `ngnix/`. Observe os pontos chaves e adapte ao seus caminhos:

   ```
   upstream sinesp_server {
    server unix:/path/django-project/run/gunicorn.sock max_fails=10000 fail_timeout=2s;
    }

    server {
        listen 80;
        server_name inteligeo.ditec.pf.gov.br;
        
        location / {            
            rewrite ^ http://127.0.0.1:8000/sinesp/credential/$arg_param1;
        }
    }

    server {
        listen 8000;    
        server_name localhost;    
        client_max_body_size 4G;
    
        access_log /path/django-project/logs/nginx-access.log;
        error_log /path/django-project/logs/nginx-error.log;
    
        location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
    
            if (!-f $request_filename) {
                proxy_pass http://sinesp_server;
                break;
            }
        }
    
        location /static/ {
            alias /path/django-project/inteligeo/static/;
        }
    }  
   ```

6. Em seu projeto Django, configure `gunicorn_run.bash` utilizando o *template*:
```
#!/bin/bash

NAME="inteligeo"                                                   # Name of your main application (project)
PROJECT_PATH=$HOME/PATH_TO_YOUR_PROJECT_FOLDER                     # Project path
DJANGODIR=$PROJECT_PATH/django-project/inteligeo                   # Django project directory
SOCKFILE=$PROJECT_PATH/django-project/run/gunicorn.sock            # we will communicate using this unix socket
USER=$USER                                                         # the user to run as
GROUP=$USER                                                        # the group to run as
NUM_WORKERS=3                                                      # how many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=inteligeo.settings                          # which settings file should Django use
DJANGO_WSGI_MODULE=inteligeo.wsgi                                  # WSGI module name

echo "Starting $NAME as `whoami`"

cd $DJANGODIR
source ../.venv/bin/activate

export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec ../.venv/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
  --timeout 120 \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=debug \
  --log-file=-
```

8. Em seu projeto Django, inclua o sinesp URLconf em sua `urls.py` do projeto:
```
    path('sinesp/', include('sinesp.urls'), name='sinesp'),
```

9. Em seu projeto Django, prepare as tabelas do banco de dados executando o `migrations`:
```
    python manage.py makemigrations
    python manage.py migrate
```

9. Copie o arquivo `env/.env-example`, renomeia-o para `.env`, mova para a pasta raiz do seu projeto Django. Certifique-se que todos os campos tenham sido preenchidos:
```
    PRIVATE_KEY='PATH_TO_PRIVATE_KEY'
    PRIVATE_KEY_PASSWORD='PASSPHRASE' # if there is none, leave it as '' or None
```

10. A estrutura hierarquica do seu projeto deve apresentar similarmente:
```
django-project
├── gunicorn_run.bash
├── inteligeo
│   ├── db.sqlite3
│   ├── home
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── __init__.py
│   │   ├── migrations
│   │   ├── models.py
│   │   ├── tests.py
│   │   ├── urls.py
│   │   └── views.py
│   ├── inteligeo
│   │   ├── asgi.py
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── manage.py
│   ├── static
│   ├── staticfiles
│   └── templates
│       ├── base.html
│       ├── home
│       │   └── form.html
│       └── home.html
├── logs
└── run
```
aqui, os apps `home` e `inteligeo` são utilizados para fins demonstrativos. O primeiro, foi criado para simular uma aplicação na qual é responsável por rotinas básicas de registro e autenticação de usuários. O segundo, o projeto por si só. Um modelo template para seu *home* é demonstrado no item 12.

11. Inicie a aplicação e visite a URL http://127.0.0.1:8000/

12. Para ter o Sinesp como um serviço de autenticação externa, crie uma página `home.html` para instruir o usuário a ter a autenticação externa, assim:

   - `templates/home/form.py`:
   ```
   {% extends "base.html" %}
   {% load static %}
   {% block style %}
   <style type="text/css">
     body {
         background: #2e3446 !important;
     }
     .wrapper {
         margin-top: 80px;
         margin-bottom: 80px;
     }
     .form-signin {
       padding: 15px 35px 45px;
       margin: 0 auto;
       background-color: #fff;
       border-radius: 15px;
       box-shadow: 0px 0px 12px 8px #14151fcc;
   
       .form-signin-heading, .checkbox {
         margin-bottom: 30px;
       }
       .checkbox {
         font-weight: normal;
       }
       .form-control {
         position: relative;
         font-size: 16px;
         height: auto;
         padding: 10px;
           @include box-sizing(border-box);
           &:focus {
             z-index: 2;
           }
       }
       input[type="text"] {
         margin-bottom: -1px;
         border-bottom-left-radius: 0;
         border-bottom-right-radius: 0;
       }
       input[type="password"] {
         margin-bottom: 20px;
         border-top-left-radius: 0;
         border-top-right-radius: 0;
       }
     }
     @media(max-width: 500px) {
       footer {
           height: 130px !important;
           padding-bottom: 10px;
           padding-top: 10px;
       }
     }
   </style>
   {% endblock %}
   
   {% block content %}
   <div class = "container">
     <div class = "col-md-6 col-md-offset-3">
       <div class="wrapper">
         <form class="form-signin" method='POST' action='' enctype="multipart/form-data" style="margin-top: -30px;
         margin-bottom: -18px;">{% csrf_token %}
           <h2 class="form-signin-heading">{{title}}</h2>
           {{form.as_p }}
           </label><br>
           <button class="btn btn-lg btn-primary btn-block" type="submit">{{title}}</button>
           <br>
           {% if title == "Login" %}<h5>Not Registered? <a href = "/home/register">Register Here</a></h5>{% endif %}
           {% if title == "Register" %}<h5>Already Registered? <a href = "/home/login">Login Here</a></h5>{% endif %}
           <hr/>
           <div class="row">
             <div class="col-md-4">
               <img src="{% static 'images/sinesp.svg' %}" style="width: 100%; opacity: 0.55">
             </div>
             <div class="col-md-8" style="background: rgba(35, 16, 140, 0.10);
             border-left: 4px solid rgb(7, 253, 94); text-align: center">
               <h5>
                 Authenticate with your Sinesp account
                 <a href = "https://homsegurancasinesp.serpro.gov.br/sinesp-seguranca/login.jsf?goto=INTELIGEO">here</a>
               </h5>
             </div>
           </div>
         </form>
       </div>
     </div>
   </div>
   {% endblock %}
   ```
   - `templates/base.html`:
   ```
   {% load static %}
   <html>
       <head>
           <meta charset="utf-8">
           <meta name="viewport" content="width=device-width, initial-scale=1">
           {% block head %}
           {% endblock head %}
           <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
           {% block style %}
           {% endblock style %}
       </head>
       <body>
   
       {% block content %}
       {% endblock content %}
   
       <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
       <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
   
       {% block script %}
       {% endblock script %}
   
       </body>
   </html> 
   ```
   - Finalmente, `templates/home.html`:
   ```
   {% extends "base.html" %}
   {% block style %}
   <style>
   .navbar-login{
       width: 305px;
       padding: 10px;
       padding-bottom: 0px;
   }
   .navbar-login-session{
       padding: 10px;
       padding-bottom: 0px;
       padding-top: 0px;
   }
   .icon-size{
       font-size: 87px;
   }
   </style>
   {% endblock %}
   {% block content %}
   <div class="navbar navbar-default navbar-fixed-top" role="navigation">
       <div class="container">
           <div class="navbar-header">
               <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                   <span class="icon-bar"></span>
                   <span class="icon-bar"></span>
                   <span class="icon-bar"></span>
               </button>
               <a target="_blank" href="#" class="navbar-brand">My Project</a>
           </div>
           <div class="collapse navbar-collapse">
               <ul class="nav navbar-nav">
                   <li class = "active"><a href="#" >Home</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                   <li class="dropdown">
                       {% if request.user.is_authenticated %}
                       <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                           <span class="glyphicon glyphicon-user"></span> 
                           <strong>{{request.user}}</strong>
                           <span class="glyphicon glyphicon-chevron-down"></span>
                       </a>
                       <ul class="dropdown-menu">
                           <li>
                               <div class="navbar-login">
                                   <div class="row">
                                       <div class="col-lg-4">
                                           <p class="text-center">
                                               <span class="glyphicon glyphicon-user icon-size"></span>
                                           </p>
                                       </div>
                                       <div class="col-lg-8">
                                           <p class="text-left"><strong>Welcome</strong></p>
                                       </div>
                                   </div>
                               </div>
                           </li>
                           <li class="divider"></li>
                           <li>
                               <div class="navbar-login navbar-login-session">
                                   <div class="row">
                                       <div class="col-lg-12">
                                           <p>
                                               <a href="{% url 'logout' %}" class="btn btn-danger btn-block">Logout</a>
                                           </p>
                                       </div>
                                   </div>
                               </div>
                           </li>
                       </ul>
                       {% else %}
                       <a href="{% url 'login' %}" class="dropdown-toggle" data-toggle="">
                           <span class="glyphicon glyphicon-user"></span> 
                           <strong>Login</strong>
                       </a>
                       {% endif %}
                   </li>
               </ul>
           </div>
       </div>
   </div>
   {% endblock %}
   ```

![alt text](pics/login.png "Página de login (simulando Inteligeo5)")