d120774 by Anon Ray at 2014-03-08 1
# -*- coding utf-8 -*-
2
# classes/models/client.py
3
# class:: Client
4
5
from datetime import datetime, timedelta
8fe0bbc by Anon Ray at 2014-05-10 6
from flask import current_app
d120774 by Anon Ray at 2014-03-08 7
8
from swtstore.classes.database import db
1e7645a by Anon Ray at 2014-06-03 9
from swtstore.classes.models import User
d120774 by Anon Ray at 2014-03-08 10
from swtstore.classes import oauth
11
1e7645a by Anon Ray at 2014-06-03 12
d120774 by Anon Ray at 2014-03-08 13
class Client(db.Model):
14
    """
15
    The third-party application registering with the platform
16
    """
17
18
    __tablename__ = 'clients'
19
20
    id = db.Column(db.String(40), primary_key=True)
21
22
    client_secret = db.Column(db.String(55), nullable=False)
23
24
    name = db.Column(db.String(60), nullable=False)
25
26
    description = db.Column(db.String(400))
27
28
    # creator of the client application
29
    user_id = db.Column(db.ForeignKey('users.id'))
30
    creator = db.relationship('User')
31
32
    _is_private = db.Column(db.Boolean)
33
f867edb by Anon Ray at 2014-04-10 34
    _host_url = db.Column(db.String(60))
35
d120774 by Anon Ray at 2014-03-08 36
    _redirect_uris = db.Column(db.Text)
37
    _default_scopes = db.Column(db.Text)
38
39
40
    @property
f867edb by Anon Ray at 2014-04-10 41
    def client_id(self):
42
        return self.id
43
44
    @property
d120774 by Anon Ray at 2014-03-08 45
    def client_type(self):
46
        if self._is_private:
47
            return 'private'
48
        return 'public'
49
50
    @property
f867edb by Anon Ray at 2014-04-10 51
    def host_url(self):
52
        return self._host_url
53
54
    @property
d120774 by Anon Ray at 2014-03-08 55
    def redirect_uris(self):
56
        if self._redirect_uris:
57
            return self._redirect_uris.split()
58
        return []
59
60
    @property
61
    def default_redirect_uri(self):
62
        return self.redirect_uris[0]
63
64
    @property
65
    def default_scopes(self):
66
        if self._default_scopes:
67
            return self._default_scopes.split()
68
        return []
69
70
    def __repr__(self):
f867edb by Anon Ray at 2014-04-10 71
        return '<Client: %s :: ID: %s>' % (self.name, self.id)
d120774 by Anon Ray at 2014-03-08 72
73
    def __str__(self):
f867edb by Anon Ray at 2014-04-10 74
        return '<Client: %s :: ID: %s>' % (self.name, self.id)
d120774 by Anon Ray at 2014-03-08 75
76
77
    # create and persist the client to the database
78
    def persist(self):
79
        db.session.add(self)
80
        db.session.commit()
81
82
    @staticmethod
83
    def getClientsByCreator(user_id):
84
        clients = Client.query.filter_by(user_id=user_id)
85
        return [each for each in clients]
86
87
88
class Grant(db.Model):
89
    """
90
    A grant token is created in the authorization flow, and will be
91
    destroyed when the authorization finished. In this case, it would be better
92
    to store the data in a cache, which would benefit a better performance.
93
    """
94
    #TODO: this would perform better if its only in the cache. and not in a db.
95
96
    __tablename__ = 'grants'
97
98
    id = db.Column(db.Integer, primary_key=True)
99
    user_id = db.Column(db.Integer, db.ForeignKey('users.id',
100
                                                  ondelete='CASCADE'))
101
    user = db.relationship('User')
102
103
    client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),
104
                          nullable=False)
105
    client = db.relationship('Client')
106
107
    code = db.Column(db.String(255), index=True, nullable=False)
108
109
    redirect_uri = db.Column(db.String(255))
110
    expires = db.Column(db.DateTime)
111
112
    _scopes = db.Column(db.Text)
113
114
    @property
115
    def scopes(self):
116
        if self._scopes:
117
            return self._scopes.split()
118
        return []
119
120
    def delete(self):
121
        db.session.delete(self)
122
        db.session.commit()
123
124
125
class Token(db.Model):
126
    """
127
    The final token to be used by a client
128
    """
f867edb by Anon Ray at 2014-04-10 129
130
    __tablename__ = 'tokens'
131
d120774 by Anon Ray at 2014-03-08 132
    id = db.Column(db.Integer, primary_key=True)
133
134
    client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),
135
                          nullable=False)
136
    client = db.relationship('Client')
137
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
138
    user = db.relationship('User')
139
140
    token_type = db.Column(db.String(40))
141
142
    access_token = db.Column(db.String(255), unique=True)
143
    refresh_token = db.Column(db.String(255), unique=True)
144
    expires = db.Column(db.DateTime)
145
    _scopes = db.Column(db.Text)
146
147
    @property
148
    def scopes(self):
149
        if self._scopes:
150
            return self._scopes.split()
151
        return []
152
153
154
155
#TODO: find out how to better structure the following code
156
157
# OAuthLib decorators used by OAuthLib in the OAuth flow
158
159
@oauth.clientgetter
160
def loadClient(client_id):
8fe0bbc by Anon Ray at 2014-05-10 161
    current_app.logger.debug('@oauth.clientgetter')
f867edb by Anon Ray at 2014-04-10 162
    #return Client.query.filter_by(id=client_id).first()
163
    return Client.query.get(client_id)
d120774 by Anon Ray at 2014-03-08 164
165
@oauth.grantgetter
166
def loadGrant(client_id, code):
8fe0bbc by Anon Ray at 2014-05-10 167
    current_app.logger.debug('@oauth.grantgetter')
d120774 by Anon Ray at 2014-03-08 168
    return Grant.query.filter_by(client_id=client_id, code=code).first()
169
170
@oauth.grantsetter
171
def saveGrant(client_id, code, request, *args, **kwargs):
8fe0bbc by Anon Ray at 2014-05-10 172
    current_app.logger.debug('@oauth.grantsetter')
d120774 by Anon Ray at 2014-03-08 173
    expires = datetime.utcnow() + timedelta(seconds=100)
174
    grant = Grant(
175
        client_id = client_id,
176
        code = code['code'],
177
        redirect_uri = request.redirect_uri,
178
        _scopes = ' '.join(request.scopes),
179
        user = User.getCurrentUser(),
180
        expires = expires
181
    )
182
    db.session.add(grant)
183
    db.session.commit()
184
    return grant
185
186
@oauth.tokengetter
187
def loadToken(access_token=None, refresh_token=None):
8fe0bbc by Anon Ray at 2014-05-10 188
    current_app.logger.debug('@oauth.tokengetter')
d120774 by Anon Ray at 2014-03-08 189
    if access_token:
190
        return Token.query.filter_by(access_token=access_token).first()
191
    elif refresh_token:
192
        return Token.query.filter_by(refresh_token=refresh_token).first()
193
194
@oauth.tokensetter
195
def saveToken(token, request, *args, **kwargs):
8fe0bbc by Anon Ray at 2014-05-10 196
    current_app.logger.debug('@oauth.tokensetter')
f867edb by Anon Ray at 2014-04-10 197
d120774 by Anon Ray at 2014-03-08 198
    toks = Token.query.filter_by(client_id=request.client.id,
199
                                 user_id=request.user.id)
200
    # make sure that every client has only one token connected to a user
201
    for t in toks:
202
        db.session.delete(t)
f867edb by Anon Ray at 2014-04-10 203
d120774 by Anon Ray at 2014-03-08 204
    expires_in = token.pop('expires_in')
205
    expires = datetime.utcnow() + timedelta(seconds=expires_in)
206
207
    tok = Token(
208
        access_token = token['access_token'],
209
        refresh_token = token['refresh_token'],
210
        token_type = token['token_type'],
f867edb by Anon Ray at 2014-04-10 211
        _scopes = token['scope'],
d120774 by Anon Ray at 2014-03-08 212
        expires = expires,
213
        client_id = request.client.id,
f867edb by Anon Ray at 2014-04-10 214
        user = request.user
d120774 by Anon Ray at 2014-03-08 215
    )
216
    db.session.add(tok)
217
    db.session.commit()
218
    return tok
219
f867edb by Anon Ray at 2014-04-10 220
@oauth.usergetter
221
def getUser():
222
    return User.getCurrentUser()
427df6d by Anon Ray at 2014-05-11 223
224
225
226
# Authorized Clients
227
class AuthorizedClients(db.Model):
228
    """
229
     The clients authorized by users
230
    """
231
232
    __tablename__ = 'authorized_clients'
233
234
    id = db.Column(db.Integer, primary_key=True)
235
236
    client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),
237
                          nullable=False)
238
    client = db.relationship('Client')
239
240
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
241
    user = db.relationship('User')
242
243
    def persist(self):
244
        db.session.add(self)
245
        db.session.commit()
246
247
    @staticmethod
248
    def revoke(**kwargs):
249
        user = kwargs.get('user')
250
        client = kwargs.get('client')
251
        authorization = AuthorizedClients.query.filter_by(user_id=user.id,
252
                                          client_id=client.client_id).first()
253
        current_app.logger.debug('authorization to be revoked-- %s',
254
                                 authorization)
255
        db.session.delete(authorization)
256
        db.session.commit()
257
258
    @staticmethod
259
    def getByUser(user):
260
        authorized_clients = [row.client for row in \
261
                AuthorizedClients.query.filter_by(user_id=user.id).all()]
262
263
        current_app.logger.debug('authorized clients %s', authorized_clients)
264
265
        return authorized_clients
266