Adding Authentication Connector plugin system
[osm/NBI.git] / osm_nbi / auth.py
1 # -*- coding: utf-8 -*-
2
3 """
4 Authenticator is responsible for authenticating the users,
5 create the tokens unscoped and scoped, retrieve the role
6 list inside the projects that they are inserted
7 """
8
9 __author__ = "Eduardo Sousa <eduardosousa@av.it.pt>"
10 __date__ = "$27-jul-2018 23:59:59$"
11
12 import logging
13
14 import cherrypy
15 from base64 import standard_b64decode
16 from http import HTTPStatus
17
18 from authconn_keystone import AuthconnKeystone
19 from engine import EngineException
20
21
22 class AuthException(Exception):
23 def __init__(self, message, http_code=HTTPStatus.UNAUTHORIZED):
24 self.http_code = http_code
25 Exception.__init__(self, message)
26
27
28 class Authenticator:
29 """
30 This class should hold all the mechanisms for User Authentication and
31 Authorization. Initially it should support Openstack Keystone as a
32 backend through a plugin model where more backends can be added and a
33 RBAC model to manage permissions on operations.
34 """
35
36 def __init__(self, engine):
37 """
38 Authenticator initializer. Setup the initial state of the object,
39 while it waits for the config dictionary and database initialization.
40
41 Note: engine is only here until all the calls can to it can be replaced.
42
43 :param engine: reference to engine object used.
44 """
45 super().__init__()
46
47 self.engine = engine
48
49 self.backend = None
50 self.config = None
51 self.db = None
52 self.logger = logging.getLogger("nbi.authenticator")
53
54 def start(self, config):
55 """
56 Method to configure the Authenticator object. This method should be called
57 after object creation. It is responsible by initializing the selected backend,
58 as well as the initialization of the database connection.
59
60 :param config: dictionary containing the relevant parameters for this object.
61 """
62 self.config = config
63
64 try:
65 if not self.backend:
66 if config["authenticator"]["backend"] == "keystone":
67 self.backend = AuthconnKeystone(self.config["authenticator"])
68 if not self.db:
69 pass
70 # TODO: Implement database initialization
71 # NOTE: Database needed to store the mappings
72 except Exception as e:
73 raise AuthException(str(e))
74
75 pass
76
77 def init_db(self, target_version='1.0'):
78 """
79 Check if the database has been initialized. If not, create the required tables
80 and insert the predefined mappings between roles and permissions.
81
82 :param target_version: schema version that should be present in the database.
83 :return: None if OK, exception if error or version is different.
84 """
85 pass
86
87 def authorize(self):
88 token = None
89 user_passwd64 = None
90 try:
91 # 1. Get token Authorization bearer
92 auth = cherrypy.request.headers.get("Authorization")
93 if auth:
94 auth_list = auth.split(" ")
95 if auth_list[0].lower() == "bearer":
96 token = auth_list[-1]
97 elif auth_list[0].lower() == "basic":
98 user_passwd64 = auth_list[-1]
99 if not token:
100 if cherrypy.session.get("Authorization"):
101 # 2. Try using session before request a new token. If not, basic authentication will generate
102 token = cherrypy.session.get("Authorization")
103 if token == "logout":
104 token = None # force Unauthorized response to insert user pasword again
105 elif user_passwd64 and cherrypy.request.config.get("auth.allow_basic_authentication"):
106 # 3. Get new token from user password
107 user = None
108 passwd = None
109 try:
110 user_passwd = standard_b64decode(user_passwd64).decode()
111 user, _, passwd = user_passwd.partition(":")
112 except Exception:
113 pass
114 outdata = self.engine.new_token(None, {"username": user, "password": passwd})
115 token = outdata["id"]
116 cherrypy.session['Authorization'] = token
117 # 4. Get token from cookie
118 # if not token:
119 # auth_cookie = cherrypy.request.cookie.get("Authorization")
120 # if auth_cookie:
121 # token = auth_cookie.value
122 return self.engine.authorize(token)
123 except EngineException as e:
124 if cherrypy.session.get('Authorization'):
125 del cherrypy.session['Authorization']
126 cherrypy.response.headers["WWW-Authenticate"] = 'Bearer realm="{}"'.format(e)
127 raise AuthException(str(e))
128
129 def new_token(self, session, indata, remote):
130 return self.engine.new_token(session, indata, remote)
131
132 def get_token_list(self, session):
133 return self.engine.get_token_list(session)
134
135 def get_token(self, session, token_id):
136 return self.engine.get_token(session, token_id)
137
138 def del_token(self, token_id):
139 return self.engine.del_token(token_id)