Commit 2dc3863f authored by Vitali Stupin's avatar Vitali Stupin

Merge pull request #1 in XTIIM/csapi from develop to release

* commit 'deb3decf':
  Update README
  Updated readme
  Add license
  Updated readme
  Adding tests, coverage and lint
  Adding tests
  Adding tests
  Changing deprecated logger.warn
  Refactoring
  Fixing test
  Testing faulty unittest
  First test
  Corrected readme
  Cleanup
parents 6e79f913 deb3decf
Pipeline #14 failed with stages
The MIT License
Copyright (c) 2019 Estonian Information System Authority (RIA),
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Central Server API
## Developing
This API is used to add new X-Road members directly to X-Road Central Server without web admin interface.
In order to develop on your machine and test on central server you need to add your public SSH key to the server:
An official management API will be included in a future X-Road release. You can find the roadmap for X-Road [here](https://www.niis.org/xroad-roadmap).
**NB! Make sure your API is not accessible from public internet, and is properly secured in your internal network!**
## API description
API is described using OpenAPI specification: [openapi-definition.yaml](openapi-definition.yaml)
## Installation
Installation was tested with Ubuntu 18.04.
### Program
Provided systemd and nginx configurations assume than program files are installed under `/opt/csapi`. Program is running under `xroad` user to be able to access X-Road configuration files and database without any additional configurations.
Create `/opt/csapi` directory:
```bash
sudo mkdir -p /opt/csapi
```
And copy files `member.py`, `server.py`, and `requirements.txt` into `/opt/csapi` directory.
You will need to install support for python venv:
```bash
ssh-keygen
ssh-copy-id riajenk@jan-center2.ci.kit
sudo apt-get install python3-venv
```
Prepare server:
Then install required python modules into venv:
```bash
sudo apt-get install python3.4-venv
cd /opt/csapi
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
Then create a "Run Configuration" that executes `remote_exec.sh`
### Systemd configuration
Testing:
Add service description `systemd/csapi.service` to `/lib/systemd/system/csapi.service`. Then start and enable automatic startup:
```bash
curl -i -d '{"member_code": "XX000002", "member_name": "XX Test 2", "member_class": "GOV"}' -X POST jan-center2.ci.kit:5444/member
sudo systemctl daemon-reload
sudo systemctl start csapi
sudo systemctl enable csapi
```
## API description
API is described using OpenAPI specification: [openapi-definition.yaml](openapi-definition.yaml)
### Nginx configuration
## Nginx configuration
Add nginx configuration from this repository: `nginx/csapi.conf` to nginx server: `/etc/nginx/sites-enabled/csapi.conf`
Create a certificate for nginx (already installed to Central Server):
Create a certificate for nginx (installed by default in X-Road Central Server):
```bash
mkdir -p /etc/nginx/csapi
sudo mkdir -p /etc/nginx/csapi
cd /etc/nginx/csapi
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout csapi.key -out csapi.crt
```
Cert info:
Cert info (NB! CN should be the domain name of your central server):
```
Country Name (2 letter code) [AU]:EE
State or Province Name (full name) [Some-State]:Harjumaa
Locality Name (eg, city) []:Tallinn
Organization Name (eg, company) [Internet Widgits Pty Ltd]:RIA
Organizational Unit Name (eg, section) []:CSAPI
Common Name (e.g. server FQDN or YOUR name) []:jan-center2.ci.kit
Common Name (e.g. server FQDN or YOUR name) []:central-server.domain.local
Email Address []:
```
Make sure key is accessible to nginx:
```bash
sudo chmod 640 /etc/nginx/csapi/csapi.key
sudo chgrp www-data /etc/nginx/csapi/csapi.key
sudo chmod g+r /etc/nginx/csapi/csapi.key
```
On client side (XTSS app):
......@@ -61,25 +86,58 @@ Country Name (2 letter code) [AU]:EE
State or Province Name (full name) [Some-State]:Harjumaa
Locality Name (eg, city) []:Tallinn
Organization Name (eg, company) [Internet Widgits Pty Ltd]:RIA
Organizational Unit Name (eg, section) []:xtss
Organizational Unit Name (eg, section) []:APIClient
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
```
Copy client.srt to nginx machine: `/etc/nginx/csapi/client.crt`
Copy client.crt to Central Server machine: `/etc/nginx/csapi/client.crt`
For testing copy nginx csapi.crt to client and issue curl command:
For testing copy nginx `csapi.crt` to client and issue curl command:
```bash
curl --cert client.crt --key client.key --cacert csapi.crt -i -d '{"member_code": "XX000003", "member_name": "XX Test 3", "member_class": "GOVXXX"}' -X POST https://jan-center2.ci.kit:5443/member
curl --cert client.crt --key client.key --cacert csapi.crt -i -d '{"member_code": "XX000003", "member_name": "XX Test 3", "member_class": "GOVXXX"}' -X POST https://central-server.domain.local:5443/member
```
Add nginx configuration from this repository: `nginx/csapi` to nginx server: `/etc/nginx/sites-enabled/csapi`
Note that you can allow multiple clients (or nodes) by creating certificate bundle. That can be done by concatenating multiple client certificates into single `client.crt` file.
## Testing
## Systemd
Note that `server.py` is a configuration file for logging and Flask and therefore not covered by tests.
Add service description `csapi.service` to `/etc/systemd/system/csapi.service`. Then start and enable automatic startup:
Running the tests:
```bash
sudo systemctl daemon-reload
sudo systemctl start csapi
sudo systemctl enable csapi
cd <project_directory>
python -m unittest
```
Or alternatively run the test file directly:
```bash
python test_member.py
```
In order to measure code coverage install `coverage` module:
```bash
pip install coverage
```
Then run coverage analyse:
```bash
coverage run test_member.py
coverage report member.py
```
Alternatively you can generate html report with:
```bash
coverage run test_member.py
coverage html member.py
```
In order to lint the code install `pylint` module:
```bash
pip install pylint
```
Then run the analyse:
```bash
pylint member.py
```
This diff is collapsed.
#!/usr/bin/env bash
set -e
if [[ ! -d "venv" ]]; then
python3 -m venv venv > /dev/null
fi
source venv/bin/activate
pip install -r requirements.txt > /dev/null
#!/usr/bin/env bash
set -e
SERVER=riajenk@jan-center2.ci.kit
ssh $SERVER "mkdir -p project"
rsync -r -p -l --exclude=\.* --exclude=venv . ${SERVER}:project/
ssh $SERVER "cd project; ./prepare_venv.sh; sudo -u xroad ./run_in_venv.sh"
psycopg2-binary==2.8.2
Flask==1.0.3
Flask-RESTful==0.3.7
gunicorn==19.9.0
coverage
pylint
#!/usr/bin/env bash
set -e
source venv/bin/activate
# Flask (Werkzeug) server
#python server_dev.py
# Production ready Gunicorn server
gunicorn -w 4 -b 127.0.0.1:5444 server:app
#!/usr/bin/env python3
import logging
from flask import Flask, request
from flask_restful import Resource, Api
from member import MemberApi
# NB! For developing only
class StopApi(Resource):
@staticmethod
def get():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
return 'Server shutting down...'
handler = logging.FileHandler('/var/log/xroad/csapi.log')
handler.setFormatter(logging.Formatter('%(asctime)s - %(process)d - %(levelname)s: %(message)s'))
# Member module logger
logger_m = logging.getLogger('member')
logger_m.setLevel(logging.INFO)
logger_m.addHandler(handler)
# Application logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(handler)
app = Flask(__name__)
api = Api(app)
api.add_resource(MemberApi, '/member')
logger.info('Starting Central Server API')
if __name__ == '__main__':
# Flask logger
logger_f = logging.getLogger('werkzeug')
logger_f.setLevel(logging.INFO)
logger_f.addHandler(handler)
# Running Flask (Werkzeug) server for development
api.add_resource(StopApi, '/stop')
app.run(debug=False, host='0.0.0.0', port=5444)
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment