Akron | ca9bd98 | 2016-12-06 16:59:57 +0100 | [diff] [blame] | 1 | from flask import flash |
| 2 | from requests.auth import HTTPBasicAuth |
| 3 | |
| 4 | import APIFactory |
| 5 | import config |
| 6 | |
| 7 | |
| 8 | __author__ = 'hanl' |
| 9 | |
| 10 | |
| 11 | |
| 12 | # instance reference for authentication provider |
| 13 | # must be set before usage! |
| 14 | provider = None |
| 15 | message_handler = None |
| 16 | |
| 17 | |
| 18 | def load_provider(provider_name, handler): |
| 19 | ''' |
| 20 | :param provider_name: |
| 21 | :return: |
| 22 | ''' |
| 23 | global message_handler |
| 24 | message_handler = handler |
| 25 | |
| 26 | split = provider_name.split('.') |
| 27 | # get last element, so you have the class name |
| 28 | _class = split[len(split) - 1] |
| 29 | provider_name = provider_name.replace("." + _class, "") |
| 30 | module = __import__(provider_name) |
| 31 | obj = getattr(module, _class, None) |
| 32 | instance = obj() |
| 33 | if instance is None: |
| 34 | raise KeyError("the provider class %s is undefined!" % str(_class)) |
| 35 | print "successfully loaded provider '%s'" % str(_class) |
| 36 | global provider |
| 37 | provider = instance |
| 38 | return instance |
| 39 | |
| 40 | |
| 41 | class User(object): |
| 42 | ''' |
| 43 | the user object |
| 44 | ''' |
| 45 | |
| 46 | def __init__(self, username, password=None, email=None, firstName=None, lastName=None, address=None, |
| 47 | institution=None, |
| 48 | phone=None, country=None, id_token=None): |
| 49 | print "the username %s" % str(username) |
| 50 | if not username: |
| 51 | raise ValueError("the username must be set") |
| 52 | self.username = username |
| 53 | self.password = password |
| 54 | self.email = email |
| 55 | self.firstName = firstName |
| 56 | self.lastName = lastName |
| 57 | self.address = address |
| 58 | self.institution = institution |
| 59 | self.phone = phone |
| 60 | self.country = country |
| 61 | self.id_token = id_token |
| 62 | |
| 63 | def is_authenticated(self): |
| 64 | return True |
| 65 | |
| 66 | def has_details(self): |
| 67 | if self.firstName and self.lastName and self.email: |
| 68 | return True |
| 69 | return False |
| 70 | |
| 71 | def is_anonymous(self): |
| 72 | return False |
| 73 | |
| 74 | def get_id(self): |
| 75 | ''' |
| 76 | :return: id reference to retrieve user object from middleware |
| 77 | ''' |
| 78 | return unicode(self.username) |
| 79 | |
| 80 | def is_active(self): |
| 81 | return True |
| 82 | |
| 83 | def get_full_name(self): |
| 84 | if self.firstName and self.lastName: |
| 85 | return u' '.join([self.firstName, self.lastName]) |
| 86 | return None |
| 87 | |
| 88 | |
| 89 | class Provider(object): |
| 90 | global message_handler |
| 91 | |
| 92 | def __init__(self): |
| 93 | self.handler = message_handler |
| 94 | |
| 95 | def authenticate(self, username=None, password=None): |
| 96 | pass |
| 97 | |
| 98 | def get_user(self, username=None, session=None, full=False): |
| 99 | if not username or not session: |
| 100 | return ValueError("username and session must be provided!") |
| 101 | |
| 102 | def login(self, session=None, user=None): |
| 103 | pass |
| 104 | |
| 105 | |
| 106 | def logout(self, session=None): |
| 107 | pass |
| 108 | |
| 109 | def is_authenticated(self): |
| 110 | pass |
| 111 | |
| 112 | |
| 113 | class CustomProvider(Provider): |
| 114 | def authenticate(self, username=None, password=None): |
| 115 | pass |
| 116 | |
| 117 | def get_user(self, username=None, session=None, full=False): |
| 118 | """ |
| 119 | Returns the user model instance associated with the given request session. |
| 120 | If no user is retrieved an instance of `AnonymousUser` is returned. |
| 121 | """ |
| 122 | # call super method |
| 123 | super(CustomProvider, self).get_user(username, session, full) |
| 124 | if full: |
| 125 | id_token = session['api_token'] |
| 126 | code = APIFactory.decrypt_openid(token=id_token) |
| 127 | user = User(username=username, email=code['email'], firstName=code['firstName'], |
| 128 | lastName=code['lastName'], address=code['address'], institution=code['institution'], |
| 129 | phone=code['phone']) |
| 130 | return user |
| 131 | else: |
| 132 | return User(username=username) |
| 133 | |
| 134 | def login(self, session=None, user=None): |
| 135 | ''' |
| 136 | :param login_func: client specific login function |
| 137 | :param user: user object to register user for |
| 138 | :return: boolean if login successful |
| 139 | ''' |
| 140 | super(CustomProvider, self).login(session, user) |
| 141 | |
| 142 | response = APIFactory.get("auth/requestToken", auth=HTTPBasicAuth(username=user.username, |
| 143 | password=user.password)) |
| 144 | user.password = None |
| 145 | |
| 146 | if response is None: |
| 147 | return False |
| 148 | elif self.handler.isError(response): |
| 149 | self.handler.notifyNext(response.json(), flash) |
| 150 | return False |
| 151 | print "the response %i:%s" % (response.status_code, str(response.content)) |
| 152 | session['api_token'] = response.content.replace('api_token ', '') |
| 153 | return True |
| 154 | |
| 155 | def logout(self, session=None): |
| 156 | if 'api_token' not in session: |
| 157 | return False |
| 158 | session.pop('api_token', None) |
| 159 | return True |
| 160 | |
| 161 | def is_authenticated(self): |
| 162 | ''' |
| 163 | check that oauth id_token and access_token are not expired! |
| 164 | :return: |
| 165 | ''' |
| 166 | pass |
| 167 | |
| 168 | |
| 169 | class OAuth2Provider(Provider): |
| 170 | def authenticate(self, username=None, password=None): |
| 171 | pass |
| 172 | |
| 173 | def get_user(self, username=None, session=None, full=False): |
| 174 | """ |
| 175 | Returns the user model instance associated with the given request session. |
| 176 | If no user is retrieved an instance of `AnonymousUser` is returned. |
| 177 | """ |
| 178 | # call super method |
| 179 | super(OAuth2Provider, self).get_user(username, session, full) |
| 180 | |
| 181 | if full and "openid" in config.OPENID_CONNECT_SCOPES and "profile" in config.OPENID_CONNECT_SCOPES: |
| 182 | id_token = session['id_token'] |
| 183 | code = APIFactory.decrypt_openid(secret=config.OAUTH2_CLIENT_SECRET, token=id_token) |
| 184 | user = User(username=username, email=code['email'], firstName=code['firstName'], |
| 185 | lastName=code['lastName'], address=code['address'], institution=code['institution'], |
| 186 | phone=code['phone']) |
| 187 | return user |
| 188 | elif full: |
| 189 | response = APIFactory.get("user/info", auth=APIFactory.Oauth2Auth(session['access_token'])) |
| 190 | if response is None: |
| 191 | return None |
| 192 | elif self.handler.isError(response): |
| 193 | self.handler.notifyNext(response.json(), flash) |
| 194 | return None |
| 195 | else: |
| 196 | code = response.json() |
| 197 | user = User(username=username, email=code['email'], firstName=code['firstName'], |
| 198 | lastName=code['lastName'], address=code['address'], institution=code['institution'], |
| 199 | phone=code['phone']) |
| 200 | return user |
| 201 | else: |
| 202 | # for the most tasks its only about to have a user object, not the actual data! |
| 203 | return User(username=username) |
| 204 | |
| 205 | def login(self, session=None, user=None): |
| 206 | ''' |
| 207 | :param login_func: client specific login function |
| 208 | :param user: user object to register user for |
| 209 | :return: boolean if login successful |
| 210 | ''' |
| 211 | super(OAuth2Provider, self).login(session, user) |
| 212 | |
| 213 | params = {"username": user.username, "password": user.password, |
| 214 | "grant_type": "password", "client_id": config.OAUTH2_CLIENT_ID, |
| 215 | "client_secret": config.OAUTH2_CLIENT_SECRET, "scope": config.OPENID_CONNECT_SCOPES} |
| 216 | response = APIFactory.post(path='oauth2/token', params=params) |
| 217 | user.password = None |
| 218 | |
| 219 | if response is None: |
| 220 | return False |
| 221 | elif self.handler.isError(response): |
| 222 | self.handler.notifyNext(response.json(), flash) |
| 223 | return False |
| 224 | print "the response %i:%s" % (response.status_code, str(response.content)) |
| 225 | |
| 226 | session['access_token'] = response.json()['access_token'] |
| 227 | if "openid" in config.OPENID_CONNECT_SCOPES: |
| 228 | session['id_token'] = response.json()['id_token'] |
| 229 | else: |
| 230 | session['id_token'] = "some random string" # todo ??? |
| 231 | return True |
| 232 | |
| 233 | |
| 234 | def logout(self, session=None): |
| 235 | if 'access_token' not in session: |
| 236 | return False |
| 237 | |
| 238 | session.pop('access_token', None) |
| 239 | if 'id_token' in session: |
| 240 | session.pop('id_token', None) |
| 241 | return True |
| 242 | |
| 243 | |
| 244 | def is_authenticated(self): |
| 245 | ''' |
| 246 | check that oauth id_token and access_token are not expired! |
| 247 | add function to auth decorator |
| 248 | :return: |
| 249 | ''' |
| 250 | pass |