1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
1
4
from datetime import datetime , timedelta
2
5
from werkzeug .security import gen_salt
3
6
from core import db
4
7
5
8
6
9
class User (db .Model ):
7
- """User which owns resources behind our own api"""
10
+ """ User which will be querying resources from the API.
8
11
9
- id = db .Column (db .Integer , primary_key = True )
12
+ :param db.Model: Base class for database models.
13
+ """
14
+ id = db .Column (db .Integer , primary_key = True )
10
15
username = db .Column (db .String (40 ), unique = True )
11
16
password = db .Column (db .String (40 ))
12
17
13
18
@staticmethod
14
19
def find_with_password (username , password , * args , ** kwargs ):
20
+ """ Query the User collection for a record with matching username and password.
21
+ If only a username is supplied, find the first matching document with that username.
22
+
23
+ :param username: Username of the user.
24
+ :param password: Password of the user.
25
+ :param *args: Variable length argument list.
26
+ :param **kwargs: Arbitrary keyword arguments.
27
+ """
15
28
if password :
16
29
return User .query .filter_by (
17
30
username = username , password = password ).first ()
@@ -20,25 +33,28 @@ def find_with_password(username, password, *args, **kwargs):
20
33
21
34
@staticmethod
22
35
def save (username , password ):
36
+ """ Create a new User record with the supplied username and password.
37
+
38
+ :param username: Username of the user.
39
+ :param password: Password of the user.
40
+ """
23
41
user = User (username = username , password = password )
24
42
db .session .add (user )
25
43
db .session .commit ()
26
44
27
45
@staticmethod
28
46
def all ():
47
+ """ Return all User records found in the database. """
29
48
return User .query .all ()
30
49
31
50
32
51
class Client (db .Model ):
33
- """http://tools.ietf.org/html/rfc6749#section-2
34
-
35
- RFC 6749 Section 2 describes clients.
52
+ """ Client application through which user is authenticating.
36
53
37
- One thing to note is that redirection URIs are mandatory for clients. We
38
- skip this requirement as this example will only allow the resource owner
39
- password credentials grant(described in section 4.3).
54
+ RFC 6749 Section 2 (http://tools.ietf.org/html/rfc6749#section-2)
55
+ describes clients:
40
56
41
- +----------+
57
+ +----------+
42
58
| Resource |
43
59
| Owner |
44
60
| |
@@ -56,84 +72,110 @@ class Client(db.Model):
56
72
| | (w/ Optional Refresh Token) | |
57
73
+---------+ +---------------+
58
74
59
- In this flow the Authorization Server will not redirect the user as
60
- described in subsection 3.1.2 (Redirection Endpoint).
75
+ Redirection URIs are mandatory for clients. We skip this requirement
76
+ as this example only allows the resource owner password credentials
77
+ grant (described in Section 4.3). In this flow, the Authorization
78
+ Server will not redirect the user as described in subsection 3.1.2
79
+ (Redirection Endpoint).
61
80
81
+ :param db.Model: Base class for database models.
62
82
"""
63
-
64
- client_id = db .Column (db .String (40 ), primary_key = True )
83
+ client_id d = db .Column (db .String (40 ), primary_key = True )
65
84
client_type = db .Column (db .String (40 ))
66
85
67
86
@property
68
87
def allowed_grant_types (self ):
88
+ """ Returns allowed grant types.
89
+
90
+ Presently, only the password grant type is allowed.
91
+ """
69
92
return ['password' ]
70
93
71
94
@property
72
95
def default_scopes (self ):
96
+ """ Returns default scopes associated with the Client. """
73
97
return []
74
98
75
99
@staticmethod
76
100
def find (id ):
101
+ """ Queries the Client table and returns first client with
102
+ matching id.
103
+
104
+ :param id: Client id
105
+ """
77
106
return Client .query .filter_by (client_id = id ).first ()
78
107
79
108
@staticmethod
80
109
def generate ():
110
+ """ Generate a new public client with the ObjectID helper."""
81
111
client = Client (client_id = gen_salt (40 ), client_type = 'public' )
82
112
db .session .add (client )
83
113
db .session .commit ()
84
114
85
115
@staticmethod
86
116
def all ():
117
+ """ Return all Client documents found in the database. """
87
118
return Client .query .all ()
88
119
89
120
def default_redirect_uri ():
121
+ """ Return a blank default redirect URI since we are not implementing
122
+ redirects.
123
+ """
90
124
return ''
91
125
92
126
93
127
class Token (db .Model ):
94
- """Access or refresh token
128
+ """ Access or refresh token
95
129
96
- Note that an access token is aware about to which user and client it was
97
- issued, thus we can identify which user is making a request based on this
98
- token.
99
- """
100
-
101
- id = db .Column (db .Integer , primary_key = True )
102
-
103
- client_id = db .Column (db .String (40 ), db .ForeignKey ('client.client_id' ),
104
- nullable = False )
105
- client = db .relationship ('Client' )
106
-
107
- user_id = db .Column (db .Integer , db .ForeignKey ('user.id' ))
108
- user = db .relationship ('User' )
130
+ Because of our current grant flow, we are able to associate tokens
131
+ with the users who are requesting them. This can be used to track usage
132
+ and potential abuse. Only bearer tokens currently supported.
109
133
110
- # currently only bearer is supported
111
- token_type = db .Column (db .String (40 ))
112
-
113
- access_token = db .Column (db .String (255 ), unique = True )
134
+ :param db.Model: Base class for database models.
135
+ """
136
+ id = db .Column (db .Integer , primary_key = True )
137
+ client_id = db .Column (db .String (40 ), db .ForeignKey ('client.client_id' ), nullable = False )
138
+ client = db .relationship ('Client' )
139
+ user_id = db .Column (db .Integer , db .ForeignKey ('user.id' ))
140
+ user = db .relationship ('User' )
141
+ token_type = db .Column (db .String (40 ))
142
+ access_token = db .Column (db .String (255 ), unique = True )
114
143
refresh_token = db .Column (db .String (255 ), unique = True )
115
- expires = db .Column (db .DateTime )
116
- scopes = ['' ]
144
+ expires = db .Column (db .DateTime )
145
+ scopes = ['' ]
117
146
118
147
@staticmethod
119
148
def find (access_token = None , refresh_token = None ):
149
+ """ Retrieve a token record using submitted access token or
150
+ refresh token.
151
+
152
+ :param access_token: User access token.
153
+ :param refresh_token: User refresh token.
154
+ """
120
155
if access_token :
121
156
return Token .query .filter_by (access_token = access_token ).first ()
122
157
elif refresh_token :
123
158
return Token .query .filter_by (refresh_token = refresh_token ).first ()
124
159
125
160
@staticmethod
126
161
def save (token , request , * args , ** kwargs ):
162
+ """ Save a new token to the database.
163
+
164
+ :param token: Token dictionary containing access and refresh tokens, plus token type.
165
+ :param request: Request dictionary containing information about the client and user.
166
+ :param *args: Variable length argument list.
167
+ :param **kwargs: Arbitrary keyword arguments.
168
+ """
127
169
toks = Token .query .filter_by (
128
170
client_id = request .client .client_id ,
129
171
user_id = request .user .id )
130
172
131
- # Make sure that there is only one Grant Token for every
173
+ # Make sure that there is only one grant token for every
132
174
# (client, user) combination.
133
175
[db .session .delete (t ) for t in toks ]
134
176
135
177
expires_in = token .pop ('expires_in' )
136
- expires = datetime .utcnow () + timedelta (seconds = expires_in )
178
+ expires = datetime .utcnow () + timedelta (seconds = expires_in )
137
179
138
180
tok = Token (
139
181
access_token = token ['access_token' ],
0 commit comments