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