Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gnuhealth-thalamus for openSUSE:Factory checked in at 2021-03-11 20:11:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gnuhealth-thalamus (Old) and /work/SRC/openSUSE:Factory/.gnuhealth-thalamus.new.2401 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gnuhealth-thalamus" Thu Mar 11 20:11:13 2021 rev:7 rq:878228 version:0.9.13 Changes: -------- --- /work/SRC/openSUSE:Factory/gnuhealth-thalamus/gnuhealth-thalamus.changes 2020-02-24 15:54:03.731693093 +0100 +++ /work/SRC/openSUSE:Factory/.gnuhealth-thalamus.new.2401/gnuhealth-thalamus.changes 2021-03-11 20:12:53.276721601 +0100 @@ -1,0 +2,5 @@ +Wed Mar 3 19:47:37 UTC 2021 - Axel Braun <axel.br...@gmx.de> + +- version 0.9.13 fixes https://savannah.gnu.org/bugs/?60164 + +------------------------------------------------------------------- Old: ---- thalamus-0.9.12.tar.gz New: ---- thalamus-0.9.13.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gnuhealth-thalamus.spec ++++++ --- /var/tmp/diff_new_pack.Vg9lSP/_old 2021-03-11 20:12:53.844722523 +0100 +++ /var/tmp/diff_new_pack.Vg9lSP/_new 2021-03-11 20:12:53.848722529 +0100 @@ -1,8 +1,8 @@ # # spec file for package gnuhealth-thalamus # -# Copyright (c) 2020 SUSE LLC -# Copyright (c) 2017-2020 Dr. Axel Braun +# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2017-2021 Dr. Axel Braun # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -21,7 +21,7 @@ %define modname thalamus Name: gnuhealth-%{modname} -Version: 0.9.12 +Version: 0.9.13 Release: 0 Summary: The GNU Health Federation Message and Authentication Server License: GPL-3.0-or-later ++++++ thalamus-0.9.12.tar.gz -> thalamus-0.9.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/PKG-INFO new/thalamus-0.9.13/PKG-INFO --- old/thalamus-0.9.12/PKG-INFO 2019-11-20 09:20:03.000000000 +0100 +++ new/thalamus-0.9.13/PKG-INFO 2021-03-03 20:13:30.480479000 +0100 @@ -1,14 +1,14 @@ Metadata-Version: 1.1 Name: thalamus -Version: 0.9.12 +Version: 0.9.13 Summary: The GNU Health Federation Message and Authentication Server Home-page: http://health.gnu.org Author: GNU Solidario Author-email: hea...@gnusolidario.org License: UNKNOWN Download-URL: http://ftp.gnu.org/gnu/health -Description: Thalamus: The GNU Health Message and Authentication Server - ========================================================== +Description: Thalamus: The GNU Health Federation Message and Authentication Server + ===================================================================== The Thalamus project provides a RESTful API hub to all the GNU Health Federation nodes. The main functions are: @@ -30,7 +30,7 @@ ------------ Thalamus is pip-installable:: - $ pip3 install --upgrade --user thalamus + $ pip3 install --upgrade --user thalamus Technology ---------- @@ -91,91 +91,139 @@ Retrieve the demographic information of person:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people/ESPGNU777ORG + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people/ESPGNU777ORG + Yields to:: HTTP/1.1 200 OK + Access-Control-Allow-Origin: * Connection: close - Content-Length: 411 + Content-Length: 547 Content-Type: application/json - Date: Fri, 21 Apr 2017 16:22:38 GMT - Server: gunicorn/19.7.1 + Date: Thu, 30 Jul 2020 12:01:37 GMT + Server: gunicorn/20.0.0 { "_id": "ESPGNU777ORG", "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", + "dob": "1990-10-04", "education": "tertiary", "ethnicity": "latino", - "gender": "female", + "gender": "f", + "id": "ESPGNU777ORG", "lastname": "Betz", "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, "name": "Ana", "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", "profession": "teacher", "roles": [ - "end_user" + "end_user" ] - } **Retrieve the demographics information globally**:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people Yields to:: - HTTP/1.1 200 OK - Connection: close - Content-Length: 933 - Content-Type: application/json - Date: Fri, 21 Apr 2017 16:31:23 GMT - Server: gunicorn/19.7.1 + HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Connection: close + Content-Length: 2715 + Content-Type: application/json + Date: Mon, 20 Jul 2020 18:03:24 GMT + Server: gunicorn/20.0.0 + + [ + [ + { + "active": true, + "dob": "1984-10-05", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ITAPYT999HON", + "lastname": "Cordara", + "marital_status": "married", + "name": "Cameron", + "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", + "profession": "teacher", + "roles": [ + "end_user", + "health_professional" + ] + } + ], + [ + { + "creation_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2019-11-20 10:39:21.162731", + "user": "ITAPYT999HON" + }, + "dob": "1999-10-12", + "gender": "m", + "id": "DEUMOP095IDI", + "lastname": "Dillinger", + "name": "John Doe???", + "roles": [ + "end_user" + ] + } + ], - [ - { - "_id": "ITAPYT999HON", - "active": true, - "biological_sex": "female", - "dob": "Fri, 05 Oct 1984 09:00:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Cordara", - "marital_status": "married", - "name": "Cameron", - "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", - "profession": "teacher", - "roles": [ - "end_user", - "health_professional" - ] - - }, - - { - "_id": "ESPGNU777ORG", - "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Betz", - "marital_status": "married", - "name": "Ana", - "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", - "profession": "teacher", - "roles": [ - "end_user" - ] - - } - - ] + [ + { + "active": true, + "dob": "2008-10-12", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ARGBUE111FAV", + "lastname": "Root", + "marital_status": "married", + "name": "Admin", + "password": "$2b$12$McKDNgArdHKbUssD1fj64ecpdbJGMIt29ns7DGvqzqIT26W0dtJzi", + "profession": "System Administrator", + "roles": [ + "root" + ] + } + ], + [ + { + "_id": "ESPGNU777ORG", + "active": true, + "dob": "1990-10-04", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ESPGNU777ORG", + "lastname": "Betz", + "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, + "name": "Ana", + "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", + "profession": "teacher", + "roles": [ + "end_user" + ] + } + ], + ] + **Using Python requests**:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/README.rst new/thalamus-0.9.13/README.rst --- old/thalamus-0.9.12/README.rst 2019-11-20 09:03:38.000000000 +0100 +++ new/thalamus-0.9.13/README.rst 2021-03-03 20:12:13.000000000 +0100 @@ -1,5 +1,5 @@ -Thalamus: The GNU Health Message and Authentication Server -========================================================== +Thalamus: The GNU Health Federation Message and Authentication Server +===================================================================== The Thalamus project provides a RESTful API hub to all the GNU Health Federation nodes. The main functions are: @@ -21,7 +21,7 @@ ------------ Thalamus is pip-installable:: - $ pip3 install --upgrade --user thalamus + $ pip3 install --upgrade --user thalamus Technology ---------- @@ -82,91 +82,139 @@ Retrieve the demographic information of person:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people/ESPGNU777ORG + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people/ESPGNU777ORG + Yields to:: HTTP/1.1 200 OK + Access-Control-Allow-Origin: * Connection: close - Content-Length: 411 + Content-Length: 547 Content-Type: application/json - Date: Fri, 21 Apr 2017 16:22:38 GMT - Server: gunicorn/19.7.1 + Date: Thu, 30 Jul 2020 12:01:37 GMT + Server: gunicorn/20.0.0 { "_id": "ESPGNU777ORG", "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", + "dob": "1990-10-04", "education": "tertiary", "ethnicity": "latino", - "gender": "female", + "gender": "f", + "id": "ESPGNU777ORG", "lastname": "Betz", "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, "name": "Ana", "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", "profession": "teacher", "roles": [ - "end_user" + "end_user" ] - } **Retrieve the demographics information globally**:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people Yields to:: - HTTP/1.1 200 OK - Connection: close - Content-Length: 933 - Content-Type: application/json - Date: Fri, 21 Apr 2017 16:31:23 GMT - Server: gunicorn/19.7.1 + HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Connection: close + Content-Length: 2715 + Content-Type: application/json + Date: Mon, 20 Jul 2020 18:03:24 GMT + Server: gunicorn/20.0.0 + + [ + [ + { + "active": true, + "dob": "1984-10-05", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ITAPYT999HON", + "lastname": "Cordara", + "marital_status": "married", + "name": "Cameron", + "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", + "profession": "teacher", + "roles": [ + "end_user", + "health_professional" + ] + } + ], + [ + { + "creation_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2019-11-20 10:39:21.162731", + "user": "ITAPYT999HON" + }, + "dob": "1999-10-12", + "gender": "m", + "id": "DEUMOP095IDI", + "lastname": "Dillinger", + "name": "John Doe???", + "roles": [ + "end_user" + ] + } + ], - [ - { - "_id": "ITAPYT999HON", - "active": true, - "biological_sex": "female", - "dob": "Fri, 05 Oct 1984 09:00:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Cordara", - "marital_status": "married", - "name": "Cameron", - "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", - "profession": "teacher", - "roles": [ - "end_user", - "health_professional" - ] - - }, - - { - "_id": "ESPGNU777ORG", - "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Betz", - "marital_status": "married", - "name": "Ana", - "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", - "profession": "teacher", - "roles": [ - "end_user" - ] - - } - - ] + [ + { + "active": true, + "dob": "2008-10-12", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ARGBUE111FAV", + "lastname": "Root", + "marital_status": "married", + "name": "Admin", + "password": "$2b$12$McKDNgArdHKbUssD1fj64ecpdbJGMIt29ns7DGvqzqIT26W0dtJzi", + "profession": "System Administrator", + "roles": [ + "root" + ] + } + ], + [ + { + "_id": "ESPGNU777ORG", + "active": true, + "dob": "1990-10-04", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ESPGNU777ORG", + "lastname": "Betz", + "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, + "name": "Ana", + "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", + "profession": "teacher", + "roles": [ + "end_user" + ] + } + ], + ] + **Using Python requests**:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/setup.py new/thalamus-0.9.13/setup.py --- old/thalamus-0.9.12/setup.py 2019-10-04 22:48:23.000000000 +0200 +++ new/thalamus-0.9.13/setup.py 2021-03-03 20:12:31.000000000 +0100 @@ -8,8 +8,8 @@ # ############################################################################## # -# Copyright (C) 2008 - 2019 Luis Falcon <lfal...@gnusolidario.org> -# Copyright (C) 2008 - 2019 GNU Solidario <hea...@gnusolidario.org> +# Copyright (C) 2008 - 2021 Luis Falcon <fal...@gnuhealth.org> +# Copyright (C) 2008 - 2021 GNU Solidario <hea...@gnusolidario.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,21 +28,22 @@ from setuptools import setup, find_packages -long_desc = open("README.rst", "r").read() +long_desc = open("README.rst").read() version = open("version").read().strip() setup(name='thalamus', - version=version, - description = 'The GNU Health Federation Message and Authentication Server', - keywords='health API REST', - long_description = long_desc, - platforms='any', - author='GNU Solidario', - author_email='hea...@gnusolidario.org', - url='http://health.gnu.org', - download_url='http://ftp.gnu.org/gnu/health', - classifiers=[ + version=version, + description='The GNU Health Federation Message' + ' and Authentication Server', + keywords='health API REST', + long_description=long_desc, + platforms='any', + author='GNU Solidario', + author_email='hea...@gnusolidario.org', + url='http://health.gnu.org', + download_url='http://ftp.gnu.org/gnu/health', + classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Framework :: Flask', @@ -55,15 +56,15 @@ 'Topic :: Scientific/Engineering :: Bio-Informatics', 'Topic :: Scientific/Engineering :: Medical Science Apps.', ], - install_requires = [ + install_requires=[ "flask", "flask_httpauth", "flask_restful", "flask_wtf", "psycopg2-binary", "bcrypt", - ], - packages=find_packages(), - include_package_data=True, - zip_safe=False, - ) + ], + packages=find_packages(), + include_package_data=True, + zip_safe=False, + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/thalamus/thalamus.py new/thalamus-0.9.13/thalamus/thalamus.py --- old/thalamus-0.9.12/thalamus/thalamus.py 2019-11-20 09:03:38.000000000 +0100 +++ new/thalamus-0.9.13/thalamus/thalamus.py 2021-03-03 19:28:32.000000000 +0100 @@ -8,8 +8,8 @@ ############################################################################## # # GNU Health: The Free Health and Hospital Information System -# Copyright (C) 2008-2019 Luis Falcon <fal...@gnuhealth.org> -# Copyright (C) 2011-2019 GNU Solidario <hea...@gnusolidario.org> +# Copyright (C) 2008-2021 Luis Falcon <fal...@gnuhealth.org> +# Copyright (C) 2011-2021 GNU Solidario <hea...@gnusolidario.org> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -41,7 +41,8 @@ import bcrypt import logging -__all__ = ["People","Person","Book","Page", "Login", "PasswordForm","Password"] +__all__ = ["People", "Person", "Book", "Page", "Login", + "PasswordForm", "Password"] app = Flask(__name__) @@ -54,7 +55,7 @@ auth = HTTPBasicAuth() -ACL = json.load(open(app.config['ACL'],'r')) +ACL = json.load(open(app.config['ACL'], 'r')) # Use Gunicorn logging system when Thalamus is run through it # use the gunicorn argument --log-level to specify the starting @@ -65,18 +66,19 @@ app.logger.setLevel(gunicorn_logger.level) -#Open a connection to PG Server +# Open a connection to PG Server conn = psycopg2.connect(app.config['POSTGRESQL_URI']) + def check_id(table, resid): """ Checks if the Federation ID exists on the GNU Health HIS Returns the instance or null """ cur = conn.cursor() - cur.execute ( - sql.SQL("SELECT id from {} where id = %s limit(1)").format(sql.Identifier(table)), \ - (resid,)) + cur.execute( + sql.SQL("SELECT id from {} where id = %s \ + limit(1)").format(sql.Identifier(table)), (resid,)) try: res, = cur.fetchone() except: @@ -86,6 +88,7 @@ # Authentication + @auth.verify_password def verify_password(username, password): """ @@ -94,7 +97,7 @@ The password is bcrypt hashed """ cur = conn.cursor() - cur.execute ('SELECT data from people \ + cur.execute('SELECT data from people \ where id = %s limit(1)', (username,)) try: user, = cur.fetchone() @@ -106,14 +109,14 @@ hashed_password = user['password'] roles = user['roles'] if bcrypt.checkpw(password.encode('utf-8'), - hashed_password.encode('utf-8')): + hashed_password.encode('utf-8')): """ Authentication OK Now check the access level for the resource """ method = request.method endpoint = request.endpoint view_args = request.view_args - return access_control(username,roles, method, endpoint, view_args) + return access_control(username, roles, method, endpoint, view_args) else: return False @@ -122,7 +125,6 @@ return False - # Authorization def access_control(username, roles, method, endpoint, view_args): """ @@ -140,40 +142,43 @@ whether the user role has global access or just can see his/her records""" if (username == view_args["person_id"] or - actions["global"] == "True"): - return True + actions["global"] == "True"): + return True else: return True return False + # People Resource class People(Resource): """Collection resource for demographic information""" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth + def get(self): """ Retrieves all the people on the person collection """ cur = conn.cursor() - cur.execute ('SELECT data from people') + cur.execute('SELECT data from people') people = cur.fetchall() return jsonify(people) + # Person class Person(Resource): """Class that manages the person demographics. """ - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self, person_id): """ Retrieves the person instance """ cur = conn.cursor() - cur.execute ('SELECT data from people \ + cur.execute('SELECT data from people \ where id = %s limit(1)', (person_id,)) try: @@ -194,83 +199,80 @@ hashed password """ - #Grab the data coming from the client, in JSON format + # Grab the data coming from the client, in JSON format values = json.loads(request.data) # Initialize to inactive the newly created person - #values['active'] = False + # values['active'] = False pw = None bcrypt_prefixes = ["$2b$", "$2y$"] if check_id('people', person_id): - abort (422, error="User already exists") + abort(422, error="User already exists") if (person_id): if (type(person_id) is str): - #Use upper case on the person federation account + # Use upper case on the person federation account person_id = person_id.upper() values['id'] = person_id else: - abort (422, error="wrong format on person ID") + abort(422, error="wrong format on person ID") - #If no roles are supplied, assign "end_user" + # If no roles are supplied, assign "end_user" if not ('roles' in values.keys()): values['roles'] = ["end_user"] - if ('password' in values.keys()): pw = values['password'] if (pw): if (len(pw) > 64): - abort (422, error="Password is too long") + abort(422, error="Password is too long") # Check if the password is already in bcrypt format if (pw[0:4] in bcrypt_prefixes): hashed_pw = pw else: hashed_pw = (bcrypt.hashpw(pw.encode('utf-8'), - bcrypt.gensalt())).decode('utf-8') - + bcrypt.gensalt())).decode('utf-8') values['password'] = hashed_pw # Insert the newly created person cur = conn.cursor() cur.execute("INSERT INTO people (ID, DATA) VALUES (%(id)s, \ - %(data)s)", {'id': person_id, 'data':json.dumps(values)}) + %(data)s)", {'id': person_id, 'data': json.dumps(values)}) res = conn.commit() return res - def patch(self, person_id): """ Updates the person instance """ - #Grab all the data coming from the node client, in JSON format + # Grab all the data coming from the node client, in JSON format values = json.loads(request.data) if 'id' in values: # Avoid changing the user ID - print ("Not allowed to change the person ID") + print("Not allowed to change the person ID") abort(422, error="Not allowed to change the person ID") # TO be discussed... # Check if the new ID exist in the Federation, and if it # does not, we may be able to update it. - if check_id('people',person_id): + if check_id('people', person_id): jdata = json.dumps(values) # UPDATE the information from the person # associated to the federation ID cur = conn.cursor() - cur.execute("UPDATE PEOPLE SET data = data || %s where id = %s", \ - (jdata,person_id)) + cur.execute("UPDATE PEOPLE SET data = data || %s where id = %s", + (jdata, person_id)) conn.commit() else: - abort (404, error="User not found") + abort(404, error="User not found") def delete(self, person_id): """ @@ -280,35 +282,35 @@ """ person = check_id('people', person_id) if not person: - abort (404, error="User does not exist") + abort(404, error="User does not exist") else: - if person['active']: - abort (422, error="The user is active.") + if person['active']: + abort(422, error="The user is active.") - #Delete the person + # Delete the person cur = conn.cursor() cur.execute("DELETE FROM people WHERE id = %s", (person_id,)) conn.commit() + # Book of Life Resource class Book(Resource): """Collection resource for a person life information""" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self, person_id): """ Retrieves the pages of life from the person """ cur = conn.cursor() - cur.execute ('SELECT data from pols where book = %s', (person_id,)) + cur.execute('SELECT data from pols where book = %s', (person_id,)) pages = cur.fetchall() return jsonify(pages) - - # Return a 404 if the person ID is not found + # Return a 404 if the person ID is not found if not pages: return '', 404 @@ -319,24 +321,23 @@ class Page(Resource): """Information and events that make and shape a person life""" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self, person_id, page_id): """ Retrieves the page instance """ cur = conn.cursor() - cur.execute ('SELECT data from pols \ + cur.execute('SELECT data from pols \ where id = %s limit(1)', (page_id,)) try: page, = cur.fetchone() except: page = None - - # Return a 404 if the page ID is not found + # Return a 404 if the page ID is not found if not page: - abort (404, error="Book or page or not found") + abort(404, error="Book or page or not found") return jsonify(page) @@ -344,9 +345,14 @@ """ Create a new instance on the Page resource """ - #Grab the data coming from the client, in JSON format + # Grab the data coming from the client, in JSON format values = json.loads(request.data) + if not check_id('people', person_id): + app.logger.error(f"Person not found when trying" + f" to create book {person_id}") + abort(404, error="Person not found") + # Basic validation on page ID exsistance and string type if (person_id and 'id' in values): if (type(person_id) is str and type(values['id'])): @@ -354,14 +360,15 @@ # Insert the newly created Page of Life from the person Book cur = conn.cursor() cur.execute("INSERT INTO pols (ID, BOOK, DATA) \ - VALUES (%(id)s, %(book)s, %(data)s)", \ - {'id': page_id, 'book': person_id, \ - 'data':json.dumps(values)}) + VALUES (%(id)s, %(book)s, %(data)s)", + {'id': page_id, 'book': person_id, + 'data': json.dumps(values)}) + res = conn.commit() else: - print ("wrong format on person or page ID") - abort (422, error="wrong format on person or page ID") + print("wrong format on person or page ID") + abort(422, error="wrong format on person or page ID") return jsonify(res) @@ -369,91 +376,97 @@ """ Updates the Page of Life """ - #Grab all the data coming from the node client, in JSON format + # Grab all the data coming from the node client, in JSON format values = json.loads(request.data) if 'id' in values: # Avoid changing the page ID abort(422, error="Not allowed to change the page ID") - if check_id('pols',page_id): + if check_id('pols', page_id): jdata = json.dumps(values) # UPDATE the information from the page # associated to the federation ID book cur = conn.cursor() - cur.execute("UPDATE pols SET data = data || %s where id = %s", \ - (jdata,page_id)) + cur.execute("UPDATE pols SET data = data || %s where id = %s", + (jdata, page_id)) conn.commit() else: - abort (404, error="Page not found") + abort(404, error="Page not found") # Add resources and endpoints # The endpoints are the class names in lower case (eg, people, life, page...) -#People and person -api.add_resource(People, '/people') #Add resource for People -api.add_resource(Person, '/people/<string:person_id>') #Add person instance -#Book and pages of life resources (in pols collection) +# People and person +api.add_resource(People, '/people') # Add resource for People +api.add_resource(Person, '/people/<string:person_id>') # Add person instance + +# Book and pages of life resources (in pols collection) api.add_resource(Book, '/pols/<string:person_id>') api.add_resource(Page, '/pols/<string:person_id>/<string:page_id>') + # Personal Documents resource class PersonalDocs(Resource): "Documents associated to the person (scanned info, birth certs, ..)" - - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self, person_id): """ Retrieves the documents of a person """ cur = conn.cursor() - cur.execute ('SELECT fedacct, pol, data, document from personal_docs \ + cur.execute('SELECT fedacct, pol, data, document from personal_docs \ where fedacct = %s', (person_id,)) documents = cur.fetchall() return jsonify(documents) + api.add_resource(PersonalDocs, '/personal_docs/<string:person_id>') + # Documents associated to a Page of Life class PolDocs(Resource): "Documents associated to the person in the context of a Page of Life" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self, person_id, pol_id): """ Retrieves the documents of a person """ cur = conn.cursor() - cur.execute ('SELECT fedacct, pol, data, document from personal_docs \ - where fedacct = %s', (person_id,pol_id)) + cur.execute('SELECT fedacct, pol, data, document from personal_docs \ + where fedacct = %s', (person_id, pol_id)) documents = cur.fetchall() return jsonify(documents) + api.add_resource(PolDocs, '/personal_docs/<string:person_id>/<string:pol_id>') + # Domiciliary Units resource class DomiciliaryUnits(Resource): "Domiciliary Units" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth def get(self): """ Retrieves the Domiciliary Units """ cur = conn.cursor() - cur.execute ('SELECT data from dus') + cur.execute('SELECT data from dus') dus = cur.fetchall() return jsonify(dus) + api.add_resource(DomiciliaryUnits, '/domiciliary-units') @@ -461,43 +474,51 @@ class Institutions(Resource): "Health and other institutions" - decorators = [auth.login_required] # Use the decorator from httpauth + decorators = [auth.login_required] # Use the decorator from httpauth + def get(self): """ Retrieves the Institutions """ cur = conn.cursor() - cur.execute ('SELECT data from institutions') + cur.execute('SELECT data from institutions') institutions = cur.fetchall() return jsonify(institutions) + api.add_resource(Institutions, '/institutions') + # Login class Login(Resource): """" - Main class for loggin in from another resources, such the GH Federation Portal + Main class for loggin in from another resources, + such the GH Federation Portal At this point, with the decorator auth.login_required is enough """ - - decorators = [auth.login_required] # Use the decorator from httpauth + + decorators = [auth.login_required] # Use the decorator from httpauth def get(self): return True -api.add_resource(Login, '/login') +api.add_resource(Login, '/login') class PasswordForm(FlaskForm): password = PasswordField('Password', - validators=[validators.DataRequired(), - validators.Length(min=6, max=30), - validators.EqualTo('pconfirm', message='Password mistmatch')]) + validators=[validators.DataRequired(), + validators.Length(min=6, max=30), + validators.EqualTo('pconfirm', + message='Password \ + mistmatch' + )]) pconfirm = PasswordField('Confirm Password') update = SubmitField('Update') + # Update the password of the user with a form @app.route('/password/<person_id>', methods=('GET', 'POST')) @auth.login_required @@ -505,30 +526,33 @@ error = None form = PasswordForm() if (request.method == 'POST' and form.validate()): - pwd = form.password.data.encode() - enc_pwd = bcrypt.hashpw(pwd, bcrypt.gensalt()).decode() - values = {'password': enc_pwd} + pwd = form.password.data.encode() + enc_pwd = bcrypt.hashpw(pwd, bcrypt.gensalt()).decode() + values = {'password': enc_pwd} + + jdata = json.dumps(values) + # UPDATE the information from the person + # associated to the federation ID + cur = conn.cursor() + cur.execute("UPDATE PEOPLE SET data = data || %s where id = %s", + (jdata, person_id)) + update_password = cur.rowcount + conn.commit() - jdata = json.dumps(values) - # UPDATE the information from the person - # associated to the federation ID - cur = conn.cursor() - cur.execute("UPDATE PEOPLE SET data = data || %s where id = %s", \ - (jdata,person_id)) - update_password = cur.rowcount - conn.commit() + if update_password > 0: + return redirect(url_for('index')) + else: + error = "Error updating the password" + + return render_template('password.html', form=form, + fed_account=person_id, error=error) - if update_password > 0: - return redirect(url_for('index')) - else: - error = "Error updating the password" - return render_template('password.html', form=form, \ - fed_account=person_id, error=error) @app.route('/') def index(): return render_template('index.html') + if __name__ == '__main__': app.logger.warning("Running Thalamus without gunicorn ...") app.run() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/thalamus.egg-info/PKG-INFO new/thalamus-0.9.13/thalamus.egg-info/PKG-INFO --- old/thalamus-0.9.12/thalamus.egg-info/PKG-INFO 2019-11-20 09:20:03.000000000 +0100 +++ new/thalamus-0.9.13/thalamus.egg-info/PKG-INFO 2021-03-03 20:13:30.000000000 +0100 @@ -1,14 +1,14 @@ Metadata-Version: 1.1 Name: thalamus -Version: 0.9.12 +Version: 0.9.13 Summary: The GNU Health Federation Message and Authentication Server Home-page: http://health.gnu.org Author: GNU Solidario Author-email: hea...@gnusolidario.org License: UNKNOWN Download-URL: http://ftp.gnu.org/gnu/health -Description: Thalamus: The GNU Health Message and Authentication Server - ========================================================== +Description: Thalamus: The GNU Health Federation Message and Authentication Server + ===================================================================== The Thalamus project provides a RESTful API hub to all the GNU Health Federation nodes. The main functions are: @@ -30,7 +30,7 @@ ------------ Thalamus is pip-installable:: - $ pip3 install --upgrade --user thalamus + $ pip3 install --upgrade --user thalamus Technology ---------- @@ -91,91 +91,139 @@ Retrieve the demographic information of person:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people/ESPGNU777ORG + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people/ESPGNU777ORG + Yields to:: HTTP/1.1 200 OK + Access-Control-Allow-Origin: * Connection: close - Content-Length: 411 + Content-Length: 547 Content-Type: application/json - Date: Fri, 21 Apr 2017 16:22:38 GMT - Server: gunicorn/19.7.1 + Date: Thu, 30 Jul 2020 12:01:37 GMT + Server: gunicorn/20.0.0 { "_id": "ESPGNU777ORG", "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", + "dob": "1990-10-04", "education": "tertiary", "ethnicity": "latino", - "gender": "female", + "gender": "f", + "id": "ESPGNU777ORG", "lastname": "Betz", "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, "name": "Ana", "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", "profession": "teacher", "roles": [ - "end_user" + "end_user" ] - } **Retrieve the demographics information globally**:: - $ http --verify no --auth ITAPYT999HON:gnusolidario https://localhost:8443/people + $ http --verify no --auth ARGBUE111FAV:freedom https://federation.gnuhealth.org:8443/people Yields to:: - HTTP/1.1 200 OK - Connection: close - Content-Length: 933 - Content-Type: application/json - Date: Fri, 21 Apr 2017 16:31:23 GMT - Server: gunicorn/19.7.1 + HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + Connection: close + Content-Length: 2715 + Content-Type: application/json + Date: Mon, 20 Jul 2020 18:03:24 GMT + Server: gunicorn/20.0.0 + + [ + [ + { + "active": true, + "dob": "1984-10-05", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ITAPYT999HON", + "lastname": "Cordara", + "marital_status": "married", + "name": "Cameron", + "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", + "profession": "teacher", + "roles": [ + "end_user", + "health_professional" + ] + } + ], + [ + { + "creation_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2019-11-20 10:39:21.162731", + "user": "ITAPYT999HON" + }, + "dob": "1999-10-12", + "gender": "m", + "id": "DEUMOP095IDI", + "lastname": "Dillinger", + "name": "John Doe???", + "roles": [ + "end_user" + ] + } + ], - [ - { - "_id": "ITAPYT999HON", - "active": true, - "biological_sex": "female", - "dob": "Fri, 05 Oct 1984 09:00:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Cordara", - "marital_status": "married", - "name": "Cameron", - "password": "$2b$12$Y9rX7PoTHRXhTO1H78Tan.8mVmyayGAUIveiYxu2Qeo0ZDRvJQ8/2", - "profession": "teacher", - "roles": [ - "end_user", - "health_professional" - ] - - }, - - { - "_id": "ESPGNU777ORG", - "active": true, - "biological_sex": "female", - "dob": "Fri, 04 Oct 1985 13:05:00 GMT", - "education": "tertiary", - "ethnicity": "latino", - "gender": "female", - "lastname": "Betz", - "marital_status": "married", - "name": "Ana", - "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", - "profession": "teacher", - "roles": [ - "end_user" - ] - - } - - ] + [ + { + "active": true, + "dob": "2008-10-12", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ARGBUE111FAV", + "lastname": "Root", + "marital_status": "married", + "name": "Admin", + "password": "$2b$12$McKDNgArdHKbUssD1fj64ecpdbJGMIt29ns7DGvqzqIT26W0dtJzi", + "profession": "System Administrator", + "roles": [ + "root" + ] + } + ], + [ + { + "_id": "ESPGNU777ORG", + "active": true, + "dob": "1990-10-04", + "education": "tertiary", + "ethnicity": "latino", + "gender": "f", + "id": "ESPGNU777ORG", + "lastname": "Betz", + "marital_status": "married", + "modification_info": { + "node": "SPAIN-LASPALMAS-GNUSOLIDARIO-GRAL_HOSPITAL", + "timestamp": "2018-11-06 19:24:43.662846", + "user": "ITAPYT999HON" + }, + "name": "Ana", + "password": "$2b$12$cjrKVGYEKUwCmVDCtEnwcegcrmECTmeBz526AAD/ZqMGPWFpHJ4FW", + "profession": "teacher", + "roles": [ + "end_user" + ] + } + ], + ] + **Using Python requests**:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/thalamus.egg-info/requires.txt new/thalamus-0.9.13/thalamus.egg-info/requires.txt --- old/thalamus-0.9.12/thalamus.egg-info/requires.txt 2019-11-20 09:20:03.000000000 +0100 +++ new/thalamus-0.9.13/thalamus.egg-info/requires.txt 2021-03-03 20:13:30.000000000 +0100 @@ -1,6 +1,6 @@ +bcrypt flask flask_httpauth flask_restful flask_wtf psycopg2-binary -bcrypt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/thalamus-0.9.12/version new/thalamus-0.9.13/version --- old/thalamus-0.9.12/version 2019-11-20 09:07:57.000000000 +0100 +++ new/thalamus-0.9.13/version 2021-03-03 19:29:38.000000000 +0100 @@ -1 +1 @@ -0.9.12 +0.9.13