##// END OF EJS Templates
permissions: use same way of sorting of user_group permissions like user ones....
marcink -
r2060:2a7a5569 default
parent child Browse files
Show More
@@ -1,4194 +1,4208 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Database Models for RhodeCode Enterprise
22 Database Models for RhodeCode Enterprise
23 """
23 """
24
24
25 import re
25 import re
26 import os
26 import os
27 import time
27 import time
28 import hashlib
28 import hashlib
29 import logging
29 import logging
30 import datetime
30 import datetime
31 import warnings
31 import warnings
32 import ipaddress
32 import ipaddress
33 import functools
33 import functools
34 import traceback
34 import traceback
35 import collections
35 import collections
36
36
37
37
38 from sqlalchemy import *
38 from sqlalchemy import *
39 from sqlalchemy.ext.declarative import declared_attr
39 from sqlalchemy.ext.declarative import declared_attr
40 from sqlalchemy.ext.hybrid import hybrid_property
40 from sqlalchemy.ext.hybrid import hybrid_property
41 from sqlalchemy.orm import (
41 from sqlalchemy.orm import (
42 relationship, joinedload, class_mapper, validates, aliased)
42 relationship, joinedload, class_mapper, validates, aliased)
43 from sqlalchemy.sql.expression import true
43 from sqlalchemy.sql.expression import true
44 from sqlalchemy.sql.functions import coalesce, count # noqa
44 from sqlalchemy.sql.functions import coalesce, count # noqa
45 from sqlalchemy.exc import IntegrityError # noqa
45 from sqlalchemy.exc import IntegrityError # noqa
46 from beaker.cache import cache_region
46 from beaker.cache import cache_region
47 from zope.cachedescriptors.property import Lazy as LazyProperty
47 from zope.cachedescriptors.property import Lazy as LazyProperty
48
48
49 from pyramid.threadlocal import get_current_request
49 from pyramid.threadlocal import get_current_request
50
50
51 from rhodecode.translation import _
51 from rhodecode.translation import _
52 from rhodecode.lib.vcs import get_vcs_instance
52 from rhodecode.lib.vcs import get_vcs_instance
53 from rhodecode.lib.vcs.backends.base import EmptyCommit, Reference
53 from rhodecode.lib.vcs.backends.base import EmptyCommit, Reference
54 from rhodecode.lib.utils2 import (
54 from rhodecode.lib.utils2 import (
55 str2bool, safe_str, get_commit_safe, safe_unicode, md5_safe,
55 str2bool, safe_str, get_commit_safe, safe_unicode, md5_safe,
56 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
56 time_to_datetime, aslist, Optional, safe_int, get_clone_url, AttributeDict,
57 glob2re, StrictAttributeDict, cleaned_uri)
57 glob2re, StrictAttributeDict, cleaned_uri)
58 from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType
58 from rhodecode.lib.jsonalchemy import MutationObj, MutationList, JsonType
59 from rhodecode.lib.ext_json import json
59 from rhodecode.lib.ext_json import json
60 from rhodecode.lib.caching_query import FromCache
60 from rhodecode.lib.caching_query import FromCache
61 from rhodecode.lib.encrypt import AESCipher
61 from rhodecode.lib.encrypt import AESCipher
62
62
63 from rhodecode.model.meta import Base, Session
63 from rhodecode.model.meta import Base, Session
64
64
65 URL_SEP = '/'
65 URL_SEP = '/'
66 log = logging.getLogger(__name__)
66 log = logging.getLogger(__name__)
67
67
68 # =============================================================================
68 # =============================================================================
69 # BASE CLASSES
69 # BASE CLASSES
70 # =============================================================================
70 # =============================================================================
71
71
72 # this is propagated from .ini file rhodecode.encrypted_values.secret or
72 # this is propagated from .ini file rhodecode.encrypted_values.secret or
73 # beaker.session.secret if first is not set.
73 # beaker.session.secret if first is not set.
74 # and initialized at environment.py
74 # and initialized at environment.py
75 ENCRYPTION_KEY = None
75 ENCRYPTION_KEY = None
76
76
77 # used to sort permissions by types, '#' used here is not allowed to be in
77 # used to sort permissions by types, '#' used here is not allowed to be in
78 # usernames, and it's very early in sorted string.printable table.
78 # usernames, and it's very early in sorted string.printable table.
79 PERMISSION_TYPE_SORT = {
79 PERMISSION_TYPE_SORT = {
80 'admin': '####',
80 'admin': '####',
81 'write': '###',
81 'write': '###',
82 'read': '##',
82 'read': '##',
83 'none': '#',
83 'none': '#',
84 }
84 }
85
85
86
86
87 def display_sort(obj):
87 def display_user_sort(obj):
88 """
88 """
89 Sort function used to sort permissions in .permissions() function of
89 Sort function used to sort permissions in .permissions() function of
90 Repository, RepoGroup, UserGroup. Also it put the default user in front
90 Repository, RepoGroup, UserGroup. Also it put the default user in front
91 of all other resources
91 of all other resources
92 """
92 """
93
93
94 if obj.username == User.DEFAULT_USER:
94 if obj.username == User.DEFAULT_USER:
95 return '#####'
95 return '#####'
96 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
96 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
97 return prefix + obj.username
97 return prefix + obj.username
98
98
99
99
100 def display_user_group_sort(obj):
101 """
102 Sort function used to sort permissions in .permissions() function of
103 Repository, RepoGroup, UserGroup. Also it put the default user in front
104 of all other resources
105 """
106
107 prefix = PERMISSION_TYPE_SORT.get(obj.permission.split('.')[-1], '')
108 return prefix + obj.users_group_name
109
110
100 def _hash_key(k):
111 def _hash_key(k):
101 return md5_safe(k)
112 return md5_safe(k)
102
113
103
114
104 def in_filter_generator(qry, items, limit=500):
115 def in_filter_generator(qry, items, limit=500):
105 """
116 """
106 Splits IN() into multiple with OR
117 Splits IN() into multiple with OR
107 e.g.::
118 e.g.::
108 cnt = Repository.query().filter(
119 cnt = Repository.query().filter(
109 or_(
120 or_(
110 *in_filter_generator(Repository.repo_id, range(100000))
121 *in_filter_generator(Repository.repo_id, range(100000))
111 )).count()
122 )).count()
112 """
123 """
113 parts = []
124 parts = []
114 for chunk in xrange(0, len(items), limit):
125 for chunk in xrange(0, len(items), limit):
115 parts.append(
126 parts.append(
116 qry.in_(items[chunk: chunk + limit])
127 qry.in_(items[chunk: chunk + limit])
117 )
128 )
118
129
119 return parts
130 return parts
120
131
121
132
122 class EncryptedTextValue(TypeDecorator):
133 class EncryptedTextValue(TypeDecorator):
123 """
134 """
124 Special column for encrypted long text data, use like::
135 Special column for encrypted long text data, use like::
125
136
126 value = Column("encrypted_value", EncryptedValue(), nullable=False)
137 value = Column("encrypted_value", EncryptedValue(), nullable=False)
127
138
128 This column is intelligent so if value is in unencrypted form it return
139 This column is intelligent so if value is in unencrypted form it return
129 unencrypted form, but on save it always encrypts
140 unencrypted form, but on save it always encrypts
130 """
141 """
131 impl = Text
142 impl = Text
132
143
133 def process_bind_param(self, value, dialect):
144 def process_bind_param(self, value, dialect):
134 if not value:
145 if not value:
135 return value
146 return value
136 if value.startswith('enc$aes$') or value.startswith('enc$aes_hmac$'):
147 if value.startswith('enc$aes$') or value.startswith('enc$aes_hmac$'):
137 # protect against double encrypting if someone manually starts
148 # protect against double encrypting if someone manually starts
138 # doing
149 # doing
139 raise ValueError('value needs to be in unencrypted format, ie. '
150 raise ValueError('value needs to be in unencrypted format, ie. '
140 'not starting with enc$aes')
151 'not starting with enc$aes')
141 return 'enc$aes_hmac$%s' % AESCipher(
152 return 'enc$aes_hmac$%s' % AESCipher(
142 ENCRYPTION_KEY, hmac=True).encrypt(value)
153 ENCRYPTION_KEY, hmac=True).encrypt(value)
143
154
144 def process_result_value(self, value, dialect):
155 def process_result_value(self, value, dialect):
145 import rhodecode
156 import rhodecode
146
157
147 if not value:
158 if not value:
148 return value
159 return value
149
160
150 parts = value.split('$', 3)
161 parts = value.split('$', 3)
151 if not len(parts) == 3:
162 if not len(parts) == 3:
152 # probably not encrypted values
163 # probably not encrypted values
153 return value
164 return value
154 else:
165 else:
155 if parts[0] != 'enc':
166 if parts[0] != 'enc':
156 # parts ok but without our header ?
167 # parts ok but without our header ?
157 return value
168 return value
158 enc_strict_mode = str2bool(rhodecode.CONFIG.get(
169 enc_strict_mode = str2bool(rhodecode.CONFIG.get(
159 'rhodecode.encrypted_values.strict') or True)
170 'rhodecode.encrypted_values.strict') or True)
160 # at that stage we know it's our encryption
171 # at that stage we know it's our encryption
161 if parts[1] == 'aes':
172 if parts[1] == 'aes':
162 decrypted_data = AESCipher(ENCRYPTION_KEY).decrypt(parts[2])
173 decrypted_data = AESCipher(ENCRYPTION_KEY).decrypt(parts[2])
163 elif parts[1] == 'aes_hmac':
174 elif parts[1] == 'aes_hmac':
164 decrypted_data = AESCipher(
175 decrypted_data = AESCipher(
165 ENCRYPTION_KEY, hmac=True,
176 ENCRYPTION_KEY, hmac=True,
166 strict_verification=enc_strict_mode).decrypt(parts[2])
177 strict_verification=enc_strict_mode).decrypt(parts[2])
167 else:
178 else:
168 raise ValueError(
179 raise ValueError(
169 'Encryption type part is wrong, must be `aes` '
180 'Encryption type part is wrong, must be `aes` '
170 'or `aes_hmac`, got `%s` instead' % (parts[1]))
181 'or `aes_hmac`, got `%s` instead' % (parts[1]))
171 return decrypted_data
182 return decrypted_data
172
183
173
184
174 class BaseModel(object):
185 class BaseModel(object):
175 """
186 """
176 Base Model for all classes
187 Base Model for all classes
177 """
188 """
178
189
179 @classmethod
190 @classmethod
180 def _get_keys(cls):
191 def _get_keys(cls):
181 """return column names for this model """
192 """return column names for this model """
182 return class_mapper(cls).c.keys()
193 return class_mapper(cls).c.keys()
183
194
184 def get_dict(self):
195 def get_dict(self):
185 """
196 """
186 return dict with keys and values corresponding
197 return dict with keys and values corresponding
187 to this model data """
198 to this model data """
188
199
189 d = {}
200 d = {}
190 for k in self._get_keys():
201 for k in self._get_keys():
191 d[k] = getattr(self, k)
202 d[k] = getattr(self, k)
192
203
193 # also use __json__() if present to get additional fields
204 # also use __json__() if present to get additional fields
194 _json_attr = getattr(self, '__json__', None)
205 _json_attr = getattr(self, '__json__', None)
195 if _json_attr:
206 if _json_attr:
196 # update with attributes from __json__
207 # update with attributes from __json__
197 if callable(_json_attr):
208 if callable(_json_attr):
198 _json_attr = _json_attr()
209 _json_attr = _json_attr()
199 for k, val in _json_attr.iteritems():
210 for k, val in _json_attr.iteritems():
200 d[k] = val
211 d[k] = val
201 return d
212 return d
202
213
203 def get_appstruct(self):
214 def get_appstruct(self):
204 """return list with keys and values tuples corresponding
215 """return list with keys and values tuples corresponding
205 to this model data """
216 to this model data """
206
217
207 l = []
218 l = []
208 for k in self._get_keys():
219 for k in self._get_keys():
209 l.append((k, getattr(self, k),))
220 l.append((k, getattr(self, k),))
210 return l
221 return l
211
222
212 def populate_obj(self, populate_dict):
223 def populate_obj(self, populate_dict):
213 """populate model with data from given populate_dict"""
224 """populate model with data from given populate_dict"""
214
225
215 for k in self._get_keys():
226 for k in self._get_keys():
216 if k in populate_dict:
227 if k in populate_dict:
217 setattr(self, k, populate_dict[k])
228 setattr(self, k, populate_dict[k])
218
229
219 @classmethod
230 @classmethod
220 def query(cls):
231 def query(cls):
221 return Session().query(cls)
232 return Session().query(cls)
222
233
223 @classmethod
234 @classmethod
224 def get(cls, id_):
235 def get(cls, id_):
225 if id_:
236 if id_:
226 return cls.query().get(id_)
237 return cls.query().get(id_)
227
238
228 @classmethod
239 @classmethod
229 def get_or_404(cls, id_):
240 def get_or_404(cls, id_):
230 from pyramid.httpexceptions import HTTPNotFound
241 from pyramid.httpexceptions import HTTPNotFound
231
242
232 try:
243 try:
233 id_ = int(id_)
244 id_ = int(id_)
234 except (TypeError, ValueError):
245 except (TypeError, ValueError):
235 raise HTTPNotFound()
246 raise HTTPNotFound()
236
247
237 res = cls.query().get(id_)
248 res = cls.query().get(id_)
238 if not res:
249 if not res:
239 raise HTTPNotFound()
250 raise HTTPNotFound()
240 return res
251 return res
241
252
242 @classmethod
253 @classmethod
243 def getAll(cls):
254 def getAll(cls):
244 # deprecated and left for backward compatibility
255 # deprecated and left for backward compatibility
245 return cls.get_all()
256 return cls.get_all()
246
257
247 @classmethod
258 @classmethod
248 def get_all(cls):
259 def get_all(cls):
249 return cls.query().all()
260 return cls.query().all()
250
261
251 @classmethod
262 @classmethod
252 def delete(cls, id_):
263 def delete(cls, id_):
253 obj = cls.query().get(id_)
264 obj = cls.query().get(id_)
254 Session().delete(obj)
265 Session().delete(obj)
255
266
256 @classmethod
267 @classmethod
257 def identity_cache(cls, session, attr_name, value):
268 def identity_cache(cls, session, attr_name, value):
258 exist_in_session = []
269 exist_in_session = []
259 for (item_cls, pkey), instance in session.identity_map.items():
270 for (item_cls, pkey), instance in session.identity_map.items():
260 if cls == item_cls and getattr(instance, attr_name) == value:
271 if cls == item_cls and getattr(instance, attr_name) == value:
261 exist_in_session.append(instance)
272 exist_in_session.append(instance)
262 if exist_in_session:
273 if exist_in_session:
263 if len(exist_in_session) == 1:
274 if len(exist_in_session) == 1:
264 return exist_in_session[0]
275 return exist_in_session[0]
265 log.exception(
276 log.exception(
266 'multiple objects with attr %s and '
277 'multiple objects with attr %s and '
267 'value %s found with same name: %r',
278 'value %s found with same name: %r',
268 attr_name, value, exist_in_session)
279 attr_name, value, exist_in_session)
269
280
270 def __repr__(self):
281 def __repr__(self):
271 if hasattr(self, '__unicode__'):
282 if hasattr(self, '__unicode__'):
272 # python repr needs to return str
283 # python repr needs to return str
273 try:
284 try:
274 return safe_str(self.__unicode__())
285 return safe_str(self.__unicode__())
275 except UnicodeDecodeError:
286 except UnicodeDecodeError:
276 pass
287 pass
277 return '<DB:%s>' % (self.__class__.__name__)
288 return '<DB:%s>' % (self.__class__.__name__)
278
289
279
290
280 class RhodeCodeSetting(Base, BaseModel):
291 class RhodeCodeSetting(Base, BaseModel):
281 __tablename__ = 'rhodecode_settings'
292 __tablename__ = 'rhodecode_settings'
282 __table_args__ = (
293 __table_args__ = (
283 UniqueConstraint('app_settings_name'),
294 UniqueConstraint('app_settings_name'),
284 {'extend_existing': True, 'mysql_engine': 'InnoDB',
295 {'extend_existing': True, 'mysql_engine': 'InnoDB',
285 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
296 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
286 )
297 )
287
298
288 SETTINGS_TYPES = {
299 SETTINGS_TYPES = {
289 'str': safe_str,
300 'str': safe_str,
290 'int': safe_int,
301 'int': safe_int,
291 'unicode': safe_unicode,
302 'unicode': safe_unicode,
292 'bool': str2bool,
303 'bool': str2bool,
293 'list': functools.partial(aslist, sep=',')
304 'list': functools.partial(aslist, sep=',')
294 }
305 }
295 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
306 DEFAULT_UPDATE_URL = 'https://rhodecode.com/api/v1/info/versions'
296 GLOBAL_CONF_KEY = 'app_settings'
307 GLOBAL_CONF_KEY = 'app_settings'
297
308
298 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
309 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
299 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
310 app_settings_name = Column("app_settings_name", String(255), nullable=True, unique=None, default=None)
300 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
311 _app_settings_value = Column("app_settings_value", String(4096), nullable=True, unique=None, default=None)
301 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
312 _app_settings_type = Column("app_settings_type", String(255), nullable=True, unique=None, default=None)
302
313
303 def __init__(self, key='', val='', type='unicode'):
314 def __init__(self, key='', val='', type='unicode'):
304 self.app_settings_name = key
315 self.app_settings_name = key
305 self.app_settings_type = type
316 self.app_settings_type = type
306 self.app_settings_value = val
317 self.app_settings_value = val
307
318
308 @validates('_app_settings_value')
319 @validates('_app_settings_value')
309 def validate_settings_value(self, key, val):
320 def validate_settings_value(self, key, val):
310 assert type(val) == unicode
321 assert type(val) == unicode
311 return val
322 return val
312
323
313 @hybrid_property
324 @hybrid_property
314 def app_settings_value(self):
325 def app_settings_value(self):
315 v = self._app_settings_value
326 v = self._app_settings_value
316 _type = self.app_settings_type
327 _type = self.app_settings_type
317 if _type:
328 if _type:
318 _type = self.app_settings_type.split('.')[0]
329 _type = self.app_settings_type.split('.')[0]
319 # decode the encrypted value
330 # decode the encrypted value
320 if 'encrypted' in self.app_settings_type:
331 if 'encrypted' in self.app_settings_type:
321 cipher = EncryptedTextValue()
332 cipher = EncryptedTextValue()
322 v = safe_unicode(cipher.process_result_value(v, None))
333 v = safe_unicode(cipher.process_result_value(v, None))
323
334
324 converter = self.SETTINGS_TYPES.get(_type) or \
335 converter = self.SETTINGS_TYPES.get(_type) or \
325 self.SETTINGS_TYPES['unicode']
336 self.SETTINGS_TYPES['unicode']
326 return converter(v)
337 return converter(v)
327
338
328 @app_settings_value.setter
339 @app_settings_value.setter
329 def app_settings_value(self, val):
340 def app_settings_value(self, val):
330 """
341 """
331 Setter that will always make sure we use unicode in app_settings_value
342 Setter that will always make sure we use unicode in app_settings_value
332
343
333 :param val:
344 :param val:
334 """
345 """
335 val = safe_unicode(val)
346 val = safe_unicode(val)
336 # encode the encrypted value
347 # encode the encrypted value
337 if 'encrypted' in self.app_settings_type:
348 if 'encrypted' in self.app_settings_type:
338 cipher = EncryptedTextValue()
349 cipher = EncryptedTextValue()
339 val = safe_unicode(cipher.process_bind_param(val, None))
350 val = safe_unicode(cipher.process_bind_param(val, None))
340 self._app_settings_value = val
351 self._app_settings_value = val
341
352
342 @hybrid_property
353 @hybrid_property
343 def app_settings_type(self):
354 def app_settings_type(self):
344 return self._app_settings_type
355 return self._app_settings_type
345
356
346 @app_settings_type.setter
357 @app_settings_type.setter
347 def app_settings_type(self, val):
358 def app_settings_type(self, val):
348 if val.split('.')[0] not in self.SETTINGS_TYPES:
359 if val.split('.')[0] not in self.SETTINGS_TYPES:
349 raise Exception('type must be one of %s got %s'
360 raise Exception('type must be one of %s got %s'
350 % (self.SETTINGS_TYPES.keys(), val))
361 % (self.SETTINGS_TYPES.keys(), val))
351 self._app_settings_type = val
362 self._app_settings_type = val
352
363
353 def __unicode__(self):
364 def __unicode__(self):
354 return u"<%s('%s:%s[%s]')>" % (
365 return u"<%s('%s:%s[%s]')>" % (
355 self.__class__.__name__,
366 self.__class__.__name__,
356 self.app_settings_name, self.app_settings_value,
367 self.app_settings_name, self.app_settings_value,
357 self.app_settings_type
368 self.app_settings_type
358 )
369 )
359
370
360
371
361 class RhodeCodeUi(Base, BaseModel):
372 class RhodeCodeUi(Base, BaseModel):
362 __tablename__ = 'rhodecode_ui'
373 __tablename__ = 'rhodecode_ui'
363 __table_args__ = (
374 __table_args__ = (
364 UniqueConstraint('ui_key'),
375 UniqueConstraint('ui_key'),
365 {'extend_existing': True, 'mysql_engine': 'InnoDB',
376 {'extend_existing': True, 'mysql_engine': 'InnoDB',
366 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
377 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
367 )
378 )
368
379
369 HOOK_REPO_SIZE = 'changegroup.repo_size'
380 HOOK_REPO_SIZE = 'changegroup.repo_size'
370 # HG
381 # HG
371 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
382 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
372 HOOK_PULL = 'outgoing.pull_logger'
383 HOOK_PULL = 'outgoing.pull_logger'
373 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
384 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
374 HOOK_PRETX_PUSH = 'pretxnchangegroup.pre_push'
385 HOOK_PRETX_PUSH = 'pretxnchangegroup.pre_push'
375 HOOK_PUSH = 'changegroup.push_logger'
386 HOOK_PUSH = 'changegroup.push_logger'
376 HOOK_PUSH_KEY = 'pushkey.key_push'
387 HOOK_PUSH_KEY = 'pushkey.key_push'
377
388
378 # TODO: johbo: Unify way how hooks are configured for git and hg,
389 # TODO: johbo: Unify way how hooks are configured for git and hg,
379 # git part is currently hardcoded.
390 # git part is currently hardcoded.
380
391
381 # SVN PATTERNS
392 # SVN PATTERNS
382 SVN_BRANCH_ID = 'vcs_svn_branch'
393 SVN_BRANCH_ID = 'vcs_svn_branch'
383 SVN_TAG_ID = 'vcs_svn_tag'
394 SVN_TAG_ID = 'vcs_svn_tag'
384
395
385 ui_id = Column(
396 ui_id = Column(
386 "ui_id", Integer(), nullable=False, unique=True, default=None,
397 "ui_id", Integer(), nullable=False, unique=True, default=None,
387 primary_key=True)
398 primary_key=True)
388 ui_section = Column(
399 ui_section = Column(
389 "ui_section", String(255), nullable=True, unique=None, default=None)
400 "ui_section", String(255), nullable=True, unique=None, default=None)
390 ui_key = Column(
401 ui_key = Column(
391 "ui_key", String(255), nullable=True, unique=None, default=None)
402 "ui_key", String(255), nullable=True, unique=None, default=None)
392 ui_value = Column(
403 ui_value = Column(
393 "ui_value", String(255), nullable=True, unique=None, default=None)
404 "ui_value", String(255), nullable=True, unique=None, default=None)
394 ui_active = Column(
405 ui_active = Column(
395 "ui_active", Boolean(), nullable=True, unique=None, default=True)
406 "ui_active", Boolean(), nullable=True, unique=None, default=True)
396
407
397 def __repr__(self):
408 def __repr__(self):
398 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
409 return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.ui_section,
399 self.ui_key, self.ui_value)
410 self.ui_key, self.ui_value)
400
411
401
412
402 class RepoRhodeCodeSetting(Base, BaseModel):
413 class RepoRhodeCodeSetting(Base, BaseModel):
403 __tablename__ = 'repo_rhodecode_settings'
414 __tablename__ = 'repo_rhodecode_settings'
404 __table_args__ = (
415 __table_args__ = (
405 UniqueConstraint(
416 UniqueConstraint(
406 'app_settings_name', 'repository_id',
417 'app_settings_name', 'repository_id',
407 name='uq_repo_rhodecode_setting_name_repo_id'),
418 name='uq_repo_rhodecode_setting_name_repo_id'),
408 {'extend_existing': True, 'mysql_engine': 'InnoDB',
419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
409 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
420 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
410 )
421 )
411
422
412 repository_id = Column(
423 repository_id = Column(
413 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
424 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
414 nullable=False)
425 nullable=False)
415 app_settings_id = Column(
426 app_settings_id = Column(
416 "app_settings_id", Integer(), nullable=False, unique=True,
427 "app_settings_id", Integer(), nullable=False, unique=True,
417 default=None, primary_key=True)
428 default=None, primary_key=True)
418 app_settings_name = Column(
429 app_settings_name = Column(
419 "app_settings_name", String(255), nullable=True, unique=None,
430 "app_settings_name", String(255), nullable=True, unique=None,
420 default=None)
431 default=None)
421 _app_settings_value = Column(
432 _app_settings_value = Column(
422 "app_settings_value", String(4096), nullable=True, unique=None,
433 "app_settings_value", String(4096), nullable=True, unique=None,
423 default=None)
434 default=None)
424 _app_settings_type = Column(
435 _app_settings_type = Column(
425 "app_settings_type", String(255), nullable=True, unique=None,
436 "app_settings_type", String(255), nullable=True, unique=None,
426 default=None)
437 default=None)
427
438
428 repository = relationship('Repository')
439 repository = relationship('Repository')
429
440
430 def __init__(self, repository_id, key='', val='', type='unicode'):
441 def __init__(self, repository_id, key='', val='', type='unicode'):
431 self.repository_id = repository_id
442 self.repository_id = repository_id
432 self.app_settings_name = key
443 self.app_settings_name = key
433 self.app_settings_type = type
444 self.app_settings_type = type
434 self.app_settings_value = val
445 self.app_settings_value = val
435
446
436 @validates('_app_settings_value')
447 @validates('_app_settings_value')
437 def validate_settings_value(self, key, val):
448 def validate_settings_value(self, key, val):
438 assert type(val) == unicode
449 assert type(val) == unicode
439 return val
450 return val
440
451
441 @hybrid_property
452 @hybrid_property
442 def app_settings_value(self):
453 def app_settings_value(self):
443 v = self._app_settings_value
454 v = self._app_settings_value
444 type_ = self.app_settings_type
455 type_ = self.app_settings_type
445 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
456 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
446 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
457 converter = SETTINGS_TYPES.get(type_) or SETTINGS_TYPES['unicode']
447 return converter(v)
458 return converter(v)
448
459
449 @app_settings_value.setter
460 @app_settings_value.setter
450 def app_settings_value(self, val):
461 def app_settings_value(self, val):
451 """
462 """
452 Setter that will always make sure we use unicode in app_settings_value
463 Setter that will always make sure we use unicode in app_settings_value
453
464
454 :param val:
465 :param val:
455 """
466 """
456 self._app_settings_value = safe_unicode(val)
467 self._app_settings_value = safe_unicode(val)
457
468
458 @hybrid_property
469 @hybrid_property
459 def app_settings_type(self):
470 def app_settings_type(self):
460 return self._app_settings_type
471 return self._app_settings_type
461
472
462 @app_settings_type.setter
473 @app_settings_type.setter
463 def app_settings_type(self, val):
474 def app_settings_type(self, val):
464 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
475 SETTINGS_TYPES = RhodeCodeSetting.SETTINGS_TYPES
465 if val not in SETTINGS_TYPES:
476 if val not in SETTINGS_TYPES:
466 raise Exception('type must be one of %s got %s'
477 raise Exception('type must be one of %s got %s'
467 % (SETTINGS_TYPES.keys(), val))
478 % (SETTINGS_TYPES.keys(), val))
468 self._app_settings_type = val
479 self._app_settings_type = val
469
480
470 def __unicode__(self):
481 def __unicode__(self):
471 return u"<%s('%s:%s:%s[%s]')>" % (
482 return u"<%s('%s:%s:%s[%s]')>" % (
472 self.__class__.__name__, self.repository.repo_name,
483 self.__class__.__name__, self.repository.repo_name,
473 self.app_settings_name, self.app_settings_value,
484 self.app_settings_name, self.app_settings_value,
474 self.app_settings_type
485 self.app_settings_type
475 )
486 )
476
487
477
488
478 class RepoRhodeCodeUi(Base, BaseModel):
489 class RepoRhodeCodeUi(Base, BaseModel):
479 __tablename__ = 'repo_rhodecode_ui'
490 __tablename__ = 'repo_rhodecode_ui'
480 __table_args__ = (
491 __table_args__ = (
481 UniqueConstraint(
492 UniqueConstraint(
482 'repository_id', 'ui_section', 'ui_key',
493 'repository_id', 'ui_section', 'ui_key',
483 name='uq_repo_rhodecode_ui_repository_id_section_key'),
494 name='uq_repo_rhodecode_ui_repository_id_section_key'),
484 {'extend_existing': True, 'mysql_engine': 'InnoDB',
495 {'extend_existing': True, 'mysql_engine': 'InnoDB',
485 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
496 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
486 )
497 )
487
498
488 repository_id = Column(
499 repository_id = Column(
489 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
500 "repository_id", Integer(), ForeignKey('repositories.repo_id'),
490 nullable=False)
501 nullable=False)
491 ui_id = Column(
502 ui_id = Column(
492 "ui_id", Integer(), nullable=False, unique=True, default=None,
503 "ui_id", Integer(), nullable=False, unique=True, default=None,
493 primary_key=True)
504 primary_key=True)
494 ui_section = Column(
505 ui_section = Column(
495 "ui_section", String(255), nullable=True, unique=None, default=None)
506 "ui_section", String(255), nullable=True, unique=None, default=None)
496 ui_key = Column(
507 ui_key = Column(
497 "ui_key", String(255), nullable=True, unique=None, default=None)
508 "ui_key", String(255), nullable=True, unique=None, default=None)
498 ui_value = Column(
509 ui_value = Column(
499 "ui_value", String(255), nullable=True, unique=None, default=None)
510 "ui_value", String(255), nullable=True, unique=None, default=None)
500 ui_active = Column(
511 ui_active = Column(
501 "ui_active", Boolean(), nullable=True, unique=None, default=True)
512 "ui_active", Boolean(), nullable=True, unique=None, default=True)
502
513
503 repository = relationship('Repository')
514 repository = relationship('Repository')
504
515
505 def __repr__(self):
516 def __repr__(self):
506 return '<%s[%s:%s]%s=>%s]>' % (
517 return '<%s[%s:%s]%s=>%s]>' % (
507 self.__class__.__name__, self.repository.repo_name,
518 self.__class__.__name__, self.repository.repo_name,
508 self.ui_section, self.ui_key, self.ui_value)
519 self.ui_section, self.ui_key, self.ui_value)
509
520
510
521
511 class User(Base, BaseModel):
522 class User(Base, BaseModel):
512 __tablename__ = 'users'
523 __tablename__ = 'users'
513 __table_args__ = (
524 __table_args__ = (
514 UniqueConstraint('username'), UniqueConstraint('email'),
525 UniqueConstraint('username'), UniqueConstraint('email'),
515 Index('u_username_idx', 'username'),
526 Index('u_username_idx', 'username'),
516 Index('u_email_idx', 'email'),
527 Index('u_email_idx', 'email'),
517 {'extend_existing': True, 'mysql_engine': 'InnoDB',
528 {'extend_existing': True, 'mysql_engine': 'InnoDB',
518 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
529 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
519 )
530 )
520 DEFAULT_USER = 'default'
531 DEFAULT_USER = 'default'
521 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
532 DEFAULT_USER_EMAIL = 'anonymous@rhodecode.org'
522 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
533 DEFAULT_GRAVATAR_URL = 'https://secure.gravatar.com/avatar/{md5email}?d=identicon&s={size}'
523
534
524 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
535 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
525 username = Column("username", String(255), nullable=True, unique=None, default=None)
536 username = Column("username", String(255), nullable=True, unique=None, default=None)
526 password = Column("password", String(255), nullable=True, unique=None, default=None)
537 password = Column("password", String(255), nullable=True, unique=None, default=None)
527 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
538 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
528 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
539 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
529 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
540 name = Column("firstname", String(255), nullable=True, unique=None, default=None)
530 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
541 lastname = Column("lastname", String(255), nullable=True, unique=None, default=None)
531 _email = Column("email", String(255), nullable=True, unique=None, default=None)
542 _email = Column("email", String(255), nullable=True, unique=None, default=None)
532 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
543 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
533 last_activity = Column('last_activity', DateTime(timezone=False), nullable=True, unique=None, default=None)
544 last_activity = Column('last_activity', DateTime(timezone=False), nullable=True, unique=None, default=None)
534
545
535 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
546 extern_type = Column("extern_type", String(255), nullable=True, unique=None, default=None)
536 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
547 extern_name = Column("extern_name", String(255), nullable=True, unique=None, default=None)
537 _api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
548 _api_key = Column("api_key", String(255), nullable=True, unique=None, default=None)
538 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
549 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
539 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
550 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
540 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
551 _user_data = Column("user_data", LargeBinary(), nullable=True) # JSON data
541
552
542 user_log = relationship('UserLog')
553 user_log = relationship('UserLog')
543 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
554 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
544
555
545 repositories = relationship('Repository')
556 repositories = relationship('Repository')
546 repository_groups = relationship('RepoGroup')
557 repository_groups = relationship('RepoGroup')
547 user_groups = relationship('UserGroup')
558 user_groups = relationship('UserGroup')
548
559
549 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
560 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
550 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
561 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
551
562
552 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
563 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
553 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
564 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
554 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all')
565 user_group_to_perm = relationship('UserUserGroupToPerm', primaryjoin='UserUserGroupToPerm.user_id==User.user_id', cascade='all')
555
566
556 group_member = relationship('UserGroupMember', cascade='all')
567 group_member = relationship('UserGroupMember', cascade='all')
557
568
558 notifications = relationship('UserNotification', cascade='all')
569 notifications = relationship('UserNotification', cascade='all')
559 # notifications assigned to this user
570 # notifications assigned to this user
560 user_created_notifications = relationship('Notification', cascade='all')
571 user_created_notifications = relationship('Notification', cascade='all')
561 # comments created by this user
572 # comments created by this user
562 user_comments = relationship('ChangesetComment', cascade='all')
573 user_comments = relationship('ChangesetComment', cascade='all')
563 # user profile extra info
574 # user profile extra info
564 user_emails = relationship('UserEmailMap', cascade='all')
575 user_emails = relationship('UserEmailMap', cascade='all')
565 user_ip_map = relationship('UserIpMap', cascade='all')
576 user_ip_map = relationship('UserIpMap', cascade='all')
566 user_auth_tokens = relationship('UserApiKeys', cascade='all')
577 user_auth_tokens = relationship('UserApiKeys', cascade='all')
567 user_ssh_keys = relationship('UserSshKeys', cascade='all')
578 user_ssh_keys = relationship('UserSshKeys', cascade='all')
568
579
569 # gists
580 # gists
570 user_gists = relationship('Gist', cascade='all')
581 user_gists = relationship('Gist', cascade='all')
571 # user pull requests
582 # user pull requests
572 user_pull_requests = relationship('PullRequest', cascade='all')
583 user_pull_requests = relationship('PullRequest', cascade='all')
573 # external identities
584 # external identities
574 extenal_identities = relationship(
585 extenal_identities = relationship(
575 'ExternalIdentity',
586 'ExternalIdentity',
576 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
587 primaryjoin="User.user_id==ExternalIdentity.local_user_id",
577 cascade='all')
588 cascade='all')
578 # review rules
589 # review rules
579 user_review_rules = relationship('RepoReviewRuleUser', cascade='all')
590 user_review_rules = relationship('RepoReviewRuleUser', cascade='all')
580
591
581 def __unicode__(self):
592 def __unicode__(self):
582 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
593 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
583 self.user_id, self.username)
594 self.user_id, self.username)
584
595
585 @hybrid_property
596 @hybrid_property
586 def email(self):
597 def email(self):
587 return self._email
598 return self._email
588
599
589 @email.setter
600 @email.setter
590 def email(self, val):
601 def email(self, val):
591 self._email = val.lower() if val else None
602 self._email = val.lower() if val else None
592
603
593 @hybrid_property
604 @hybrid_property
594 def first_name(self):
605 def first_name(self):
595 from rhodecode.lib import helpers as h
606 from rhodecode.lib import helpers as h
596 if self.name:
607 if self.name:
597 return h.escape(self.name)
608 return h.escape(self.name)
598 return self.name
609 return self.name
599
610
600 @hybrid_property
611 @hybrid_property
601 def last_name(self):
612 def last_name(self):
602 from rhodecode.lib import helpers as h
613 from rhodecode.lib import helpers as h
603 if self.lastname:
614 if self.lastname:
604 return h.escape(self.lastname)
615 return h.escape(self.lastname)
605 return self.lastname
616 return self.lastname
606
617
607 @hybrid_property
618 @hybrid_property
608 def api_key(self):
619 def api_key(self):
609 """
620 """
610 Fetch if exist an auth-token with role ALL connected to this user
621 Fetch if exist an auth-token with role ALL connected to this user
611 """
622 """
612 user_auth_token = UserApiKeys.query()\
623 user_auth_token = UserApiKeys.query()\
613 .filter(UserApiKeys.user_id == self.user_id)\
624 .filter(UserApiKeys.user_id == self.user_id)\
614 .filter(or_(UserApiKeys.expires == -1,
625 .filter(or_(UserApiKeys.expires == -1,
615 UserApiKeys.expires >= time.time()))\
626 UserApiKeys.expires >= time.time()))\
616 .filter(UserApiKeys.role == UserApiKeys.ROLE_ALL).first()
627 .filter(UserApiKeys.role == UserApiKeys.ROLE_ALL).first()
617 if user_auth_token:
628 if user_auth_token:
618 user_auth_token = user_auth_token.api_key
629 user_auth_token = user_auth_token.api_key
619
630
620 return user_auth_token
631 return user_auth_token
621
632
622 @api_key.setter
633 @api_key.setter
623 def api_key(self, val):
634 def api_key(self, val):
624 # don't allow to set API key this is deprecated for now
635 # don't allow to set API key this is deprecated for now
625 self._api_key = None
636 self._api_key = None
626
637
627 @property
638 @property
628 def reviewer_pull_requests(self):
639 def reviewer_pull_requests(self):
629 return PullRequestReviewers.query() \
640 return PullRequestReviewers.query() \
630 .options(joinedload(PullRequestReviewers.pull_request)) \
641 .options(joinedload(PullRequestReviewers.pull_request)) \
631 .filter(PullRequestReviewers.user_id == self.user_id) \
642 .filter(PullRequestReviewers.user_id == self.user_id) \
632 .all()
643 .all()
633
644
634 @property
645 @property
635 def firstname(self):
646 def firstname(self):
636 # alias for future
647 # alias for future
637 return self.name
648 return self.name
638
649
639 @property
650 @property
640 def emails(self):
651 def emails(self):
641 other = UserEmailMap.query()\
652 other = UserEmailMap.query()\
642 .filter(UserEmailMap.user == self) \
653 .filter(UserEmailMap.user == self) \
643 .order_by(UserEmailMap.email_id.asc()) \
654 .order_by(UserEmailMap.email_id.asc()) \
644 .all()
655 .all()
645 return [self.email] + [x.email for x in other]
656 return [self.email] + [x.email for x in other]
646
657
647 @property
658 @property
648 def auth_tokens(self):
659 def auth_tokens(self):
649 auth_tokens = self.get_auth_tokens()
660 auth_tokens = self.get_auth_tokens()
650 return [x.api_key for x in auth_tokens]
661 return [x.api_key for x in auth_tokens]
651
662
652 def get_auth_tokens(self):
663 def get_auth_tokens(self):
653 return UserApiKeys.query()\
664 return UserApiKeys.query()\
654 .filter(UserApiKeys.user == self)\
665 .filter(UserApiKeys.user == self)\
655 .order_by(UserApiKeys.user_api_key_id.asc())\
666 .order_by(UserApiKeys.user_api_key_id.asc())\
656 .all()
667 .all()
657
668
658 @property
669 @property
659 def feed_token(self):
670 def feed_token(self):
660 return self.get_feed_token()
671 return self.get_feed_token()
661
672
662 def get_feed_token(self):
673 def get_feed_token(self):
663 feed_tokens = UserApiKeys.query()\
674 feed_tokens = UserApiKeys.query()\
664 .filter(UserApiKeys.user == self)\
675 .filter(UserApiKeys.user == self)\
665 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\
676 .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\
666 .all()
677 .all()
667 if feed_tokens:
678 if feed_tokens:
668 return feed_tokens[0].api_key
679 return feed_tokens[0].api_key
669 return 'NO_FEED_TOKEN_AVAILABLE'
680 return 'NO_FEED_TOKEN_AVAILABLE'
670
681
671 @classmethod
682 @classmethod
672 def extra_valid_auth_tokens(cls, user, role=None):
683 def extra_valid_auth_tokens(cls, user, role=None):
673 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
684 tokens = UserApiKeys.query().filter(UserApiKeys.user == user)\
674 .filter(or_(UserApiKeys.expires == -1,
685 .filter(or_(UserApiKeys.expires == -1,
675 UserApiKeys.expires >= time.time()))
686 UserApiKeys.expires >= time.time()))
676 if role:
687 if role:
677 tokens = tokens.filter(or_(UserApiKeys.role == role,
688 tokens = tokens.filter(or_(UserApiKeys.role == role,
678 UserApiKeys.role == UserApiKeys.ROLE_ALL))
689 UserApiKeys.role == UserApiKeys.ROLE_ALL))
679 return tokens.all()
690 return tokens.all()
680
691
681 def authenticate_by_token(self, auth_token, roles=None, scope_repo_id=None):
692 def authenticate_by_token(self, auth_token, roles=None, scope_repo_id=None):
682 from rhodecode.lib import auth
693 from rhodecode.lib import auth
683
694
684 log.debug('Trying to authenticate user: %s via auth-token, '
695 log.debug('Trying to authenticate user: %s via auth-token, '
685 'and roles: %s', self, roles)
696 'and roles: %s', self, roles)
686
697
687 if not auth_token:
698 if not auth_token:
688 return False
699 return False
689
700
690 crypto_backend = auth.crypto_backend()
701 crypto_backend = auth.crypto_backend()
691
702
692 roles = (roles or []) + [UserApiKeys.ROLE_ALL]
703 roles = (roles or []) + [UserApiKeys.ROLE_ALL]
693 tokens_q = UserApiKeys.query()\
704 tokens_q = UserApiKeys.query()\
694 .filter(UserApiKeys.user_id == self.user_id)\
705 .filter(UserApiKeys.user_id == self.user_id)\
695 .filter(or_(UserApiKeys.expires == -1,
706 .filter(or_(UserApiKeys.expires == -1,
696 UserApiKeys.expires >= time.time()))
707 UserApiKeys.expires >= time.time()))
697
708
698 tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles))
709 tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles))
699
710
700 plain_tokens = []
711 plain_tokens = []
701 hash_tokens = []
712 hash_tokens = []
702
713
703 for token in tokens_q.all():
714 for token in tokens_q.all():
704 # verify scope first
715 # verify scope first
705 if token.repo_id:
716 if token.repo_id:
706 # token has a scope, we need to verify it
717 # token has a scope, we need to verify it
707 if scope_repo_id != token.repo_id:
718 if scope_repo_id != token.repo_id:
708 log.debug(
719 log.debug(
709 'Scope mismatch: token has a set repo scope: %s, '
720 'Scope mismatch: token has a set repo scope: %s, '
710 'and calling scope is:%s, skipping further checks',
721 'and calling scope is:%s, skipping further checks',
711 token.repo, scope_repo_id)
722 token.repo, scope_repo_id)
712 # token has a scope, and it doesn't match, skip token
723 # token has a scope, and it doesn't match, skip token
713 continue
724 continue
714
725
715 if token.api_key.startswith(crypto_backend.ENC_PREF):
726 if token.api_key.startswith(crypto_backend.ENC_PREF):
716 hash_tokens.append(token.api_key)
727 hash_tokens.append(token.api_key)
717 else:
728 else:
718 plain_tokens.append(token.api_key)
729 plain_tokens.append(token.api_key)
719
730
720 is_plain_match = auth_token in plain_tokens
731 is_plain_match = auth_token in plain_tokens
721 if is_plain_match:
732 if is_plain_match:
722 return True
733 return True
723
734
724 for hashed in hash_tokens:
735 for hashed in hash_tokens:
725 # TODO(marcink): this is expensive to calculate, but most secure
736 # TODO(marcink): this is expensive to calculate, but most secure
726 match = crypto_backend.hash_check(auth_token, hashed)
737 match = crypto_backend.hash_check(auth_token, hashed)
727 if match:
738 if match:
728 return True
739 return True
729
740
730 return False
741 return False
731
742
732 @property
743 @property
733 def ip_addresses(self):
744 def ip_addresses(self):
734 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
745 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
735 return [x.ip_addr for x in ret]
746 return [x.ip_addr for x in ret]
736
747
737 @property
748 @property
738 def username_and_name(self):
749 def username_and_name(self):
739 return '%s (%s %s)' % (self.username, self.first_name, self.last_name)
750 return '%s (%s %s)' % (self.username, self.first_name, self.last_name)
740
751
741 @property
752 @property
742 def username_or_name_or_email(self):
753 def username_or_name_or_email(self):
743 full_name = self.full_name if self.full_name is not ' ' else None
754 full_name = self.full_name if self.full_name is not ' ' else None
744 return self.username or full_name or self.email
755 return self.username or full_name or self.email
745
756
746 @property
757 @property
747 def full_name(self):
758 def full_name(self):
748 return '%s %s' % (self.first_name, self.last_name)
759 return '%s %s' % (self.first_name, self.last_name)
749
760
750 @property
761 @property
751 def full_name_or_username(self):
762 def full_name_or_username(self):
752 return ('%s %s' % (self.first_name, self.last_name)
763 return ('%s %s' % (self.first_name, self.last_name)
753 if (self.first_name and self.last_name) else self.username)
764 if (self.first_name and self.last_name) else self.username)
754
765
755 @property
766 @property
756 def full_contact(self):
767 def full_contact(self):
757 return '%s %s <%s>' % (self.first_name, self.last_name, self.email)
768 return '%s %s <%s>' % (self.first_name, self.last_name, self.email)
758
769
759 @property
770 @property
760 def short_contact(self):
771 def short_contact(self):
761 return '%s %s' % (self.first_name, self.last_name)
772 return '%s %s' % (self.first_name, self.last_name)
762
773
763 @property
774 @property
764 def is_admin(self):
775 def is_admin(self):
765 return self.admin
776 return self.admin
766
777
767 def AuthUser(self, **kwargs):
778 def AuthUser(self, **kwargs):
768 """
779 """
769 Returns instance of AuthUser for this user
780 Returns instance of AuthUser for this user
770 """
781 """
771 from rhodecode.lib.auth import AuthUser
782 from rhodecode.lib.auth import AuthUser
772 return AuthUser(user_id=self.user_id, username=self.username, **kwargs)
783 return AuthUser(user_id=self.user_id, username=self.username, **kwargs)
773
784
774 @hybrid_property
785 @hybrid_property
775 def user_data(self):
786 def user_data(self):
776 if not self._user_data:
787 if not self._user_data:
777 return {}
788 return {}
778
789
779 try:
790 try:
780 return json.loads(self._user_data)
791 return json.loads(self._user_data)
781 except TypeError:
792 except TypeError:
782 return {}
793 return {}
783
794
784 @user_data.setter
795 @user_data.setter
785 def user_data(self, val):
796 def user_data(self, val):
786 if not isinstance(val, dict):
797 if not isinstance(val, dict):
787 raise Exception('user_data must be dict, got %s' % type(val))
798 raise Exception('user_data must be dict, got %s' % type(val))
788 try:
799 try:
789 self._user_data = json.dumps(val)
800 self._user_data = json.dumps(val)
790 except Exception:
801 except Exception:
791 log.error(traceback.format_exc())
802 log.error(traceback.format_exc())
792
803
793 @classmethod
804 @classmethod
794 def get_by_username(cls, username, case_insensitive=False,
805 def get_by_username(cls, username, case_insensitive=False,
795 cache=False, identity_cache=False):
806 cache=False, identity_cache=False):
796 session = Session()
807 session = Session()
797
808
798 if case_insensitive:
809 if case_insensitive:
799 q = cls.query().filter(
810 q = cls.query().filter(
800 func.lower(cls.username) == func.lower(username))
811 func.lower(cls.username) == func.lower(username))
801 else:
812 else:
802 q = cls.query().filter(cls.username == username)
813 q = cls.query().filter(cls.username == username)
803
814
804 if cache:
815 if cache:
805 if identity_cache:
816 if identity_cache:
806 val = cls.identity_cache(session, 'username', username)
817 val = cls.identity_cache(session, 'username', username)
807 if val:
818 if val:
808 return val
819 return val
809 else:
820 else:
810 cache_key = "get_user_by_name_%s" % _hash_key(username)
821 cache_key = "get_user_by_name_%s" % _hash_key(username)
811 q = q.options(
822 q = q.options(
812 FromCache("sql_cache_short", cache_key))
823 FromCache("sql_cache_short", cache_key))
813
824
814 return q.scalar()
825 return q.scalar()
815
826
816 @classmethod
827 @classmethod
817 def get_by_auth_token(cls, auth_token, cache=False):
828 def get_by_auth_token(cls, auth_token, cache=False):
818 q = UserApiKeys.query()\
829 q = UserApiKeys.query()\
819 .filter(UserApiKeys.api_key == auth_token)\
830 .filter(UserApiKeys.api_key == auth_token)\
820 .filter(or_(UserApiKeys.expires == -1,
831 .filter(or_(UserApiKeys.expires == -1,
821 UserApiKeys.expires >= time.time()))
832 UserApiKeys.expires >= time.time()))
822 if cache:
833 if cache:
823 q = q.options(
834 q = q.options(
824 FromCache("sql_cache_short", "get_auth_token_%s" % auth_token))
835 FromCache("sql_cache_short", "get_auth_token_%s" % auth_token))
825
836
826 match = q.first()
837 match = q.first()
827 if match:
838 if match:
828 return match.user
839 return match.user
829
840
830 @classmethod
841 @classmethod
831 def get_by_email(cls, email, case_insensitive=False, cache=False):
842 def get_by_email(cls, email, case_insensitive=False, cache=False):
832
843
833 if case_insensitive:
844 if case_insensitive:
834 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
845 q = cls.query().filter(func.lower(cls.email) == func.lower(email))
835
846
836 else:
847 else:
837 q = cls.query().filter(cls.email == email)
848 q = cls.query().filter(cls.email == email)
838
849
839 email_key = _hash_key(email)
850 email_key = _hash_key(email)
840 if cache:
851 if cache:
841 q = q.options(
852 q = q.options(
842 FromCache("sql_cache_short", "get_email_key_%s" % email_key))
853 FromCache("sql_cache_short", "get_email_key_%s" % email_key))
843
854
844 ret = q.scalar()
855 ret = q.scalar()
845 if ret is None:
856 if ret is None:
846 q = UserEmailMap.query()
857 q = UserEmailMap.query()
847 # try fetching in alternate email map
858 # try fetching in alternate email map
848 if case_insensitive:
859 if case_insensitive:
849 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
860 q = q.filter(func.lower(UserEmailMap.email) == func.lower(email))
850 else:
861 else:
851 q = q.filter(UserEmailMap.email == email)
862 q = q.filter(UserEmailMap.email == email)
852 q = q.options(joinedload(UserEmailMap.user))
863 q = q.options(joinedload(UserEmailMap.user))
853 if cache:
864 if cache:
854 q = q.options(
865 q = q.options(
855 FromCache("sql_cache_short", "get_email_map_key_%s" % email_key))
866 FromCache("sql_cache_short", "get_email_map_key_%s" % email_key))
856 ret = getattr(q.scalar(), 'user', None)
867 ret = getattr(q.scalar(), 'user', None)
857
868
858 return ret
869 return ret
859
870
860 @classmethod
871 @classmethod
861 def get_from_cs_author(cls, author):
872 def get_from_cs_author(cls, author):
862 """
873 """
863 Tries to get User objects out of commit author string
874 Tries to get User objects out of commit author string
864
875
865 :param author:
876 :param author:
866 """
877 """
867 from rhodecode.lib.helpers import email, author_name
878 from rhodecode.lib.helpers import email, author_name
868 # Valid email in the attribute passed, see if they're in the system
879 # Valid email in the attribute passed, see if they're in the system
869 _email = email(author)
880 _email = email(author)
870 if _email:
881 if _email:
871 user = cls.get_by_email(_email, case_insensitive=True)
882 user = cls.get_by_email(_email, case_insensitive=True)
872 if user:
883 if user:
873 return user
884 return user
874 # Maybe we can match by username?
885 # Maybe we can match by username?
875 _author = author_name(author)
886 _author = author_name(author)
876 user = cls.get_by_username(_author, case_insensitive=True)
887 user = cls.get_by_username(_author, case_insensitive=True)
877 if user:
888 if user:
878 return user
889 return user
879
890
880 def update_userdata(self, **kwargs):
891 def update_userdata(self, **kwargs):
881 usr = self
892 usr = self
882 old = usr.user_data
893 old = usr.user_data
883 old.update(**kwargs)
894 old.update(**kwargs)
884 usr.user_data = old
895 usr.user_data = old
885 Session().add(usr)
896 Session().add(usr)
886 log.debug('updated userdata with ', kwargs)
897 log.debug('updated userdata with ', kwargs)
887
898
888 def update_lastlogin(self):
899 def update_lastlogin(self):
889 """Update user lastlogin"""
900 """Update user lastlogin"""
890 self.last_login = datetime.datetime.now()
901 self.last_login = datetime.datetime.now()
891 Session().add(self)
902 Session().add(self)
892 log.debug('updated user %s lastlogin', self.username)
903 log.debug('updated user %s lastlogin', self.username)
893
904
894 def update_lastactivity(self):
905 def update_lastactivity(self):
895 """Update user lastactivity"""
906 """Update user lastactivity"""
896 self.last_activity = datetime.datetime.now()
907 self.last_activity = datetime.datetime.now()
897 Session().add(self)
908 Session().add(self)
898 log.debug('updated user %s lastactivity', self.username)
909 log.debug('updated user %s lastactivity', self.username)
899
910
900 def update_password(self, new_password):
911 def update_password(self, new_password):
901 from rhodecode.lib.auth import get_crypt_password
912 from rhodecode.lib.auth import get_crypt_password
902
913
903 self.password = get_crypt_password(new_password)
914 self.password = get_crypt_password(new_password)
904 Session().add(self)
915 Session().add(self)
905
916
906 @classmethod
917 @classmethod
907 def get_first_super_admin(cls):
918 def get_first_super_admin(cls):
908 user = User.query().filter(User.admin == true()).first()
919 user = User.query().filter(User.admin == true()).first()
909 if user is None:
920 if user is None:
910 raise Exception('FATAL: Missing administrative account!')
921 raise Exception('FATAL: Missing administrative account!')
911 return user
922 return user
912
923
913 @classmethod
924 @classmethod
914 def get_all_super_admins(cls):
925 def get_all_super_admins(cls):
915 """
926 """
916 Returns all admin accounts sorted by username
927 Returns all admin accounts sorted by username
917 """
928 """
918 return User.query().filter(User.admin == true())\
929 return User.query().filter(User.admin == true())\
919 .order_by(User.username.asc()).all()
930 .order_by(User.username.asc()).all()
920
931
921 @classmethod
932 @classmethod
922 def get_default_user(cls, cache=False, refresh=False):
933 def get_default_user(cls, cache=False, refresh=False):
923 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
934 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
924 if user is None:
935 if user is None:
925 raise Exception('FATAL: Missing default account!')
936 raise Exception('FATAL: Missing default account!')
926 if refresh:
937 if refresh:
927 # The default user might be based on outdated state which
938 # The default user might be based on outdated state which
928 # has been loaded from the cache.
939 # has been loaded from the cache.
929 # A call to refresh() ensures that the
940 # A call to refresh() ensures that the
930 # latest state from the database is used.
941 # latest state from the database is used.
931 Session().refresh(user)
942 Session().refresh(user)
932 return user
943 return user
933
944
934 def _get_default_perms(self, user, suffix=''):
945 def _get_default_perms(self, user, suffix=''):
935 from rhodecode.model.permission import PermissionModel
946 from rhodecode.model.permission import PermissionModel
936 return PermissionModel().get_default_perms(user.user_perms, suffix)
947 return PermissionModel().get_default_perms(user.user_perms, suffix)
937
948
938 def get_default_perms(self, suffix=''):
949 def get_default_perms(self, suffix=''):
939 return self._get_default_perms(self, suffix)
950 return self._get_default_perms(self, suffix)
940
951
941 def get_api_data(self, include_secrets=False, details='full'):
952 def get_api_data(self, include_secrets=False, details='full'):
942 """
953 """
943 Common function for generating user related data for API
954 Common function for generating user related data for API
944
955
945 :param include_secrets: By default secrets in the API data will be replaced
956 :param include_secrets: By default secrets in the API data will be replaced
946 by a placeholder value to prevent exposing this data by accident. In case
957 by a placeholder value to prevent exposing this data by accident. In case
947 this data shall be exposed, set this flag to ``True``.
958 this data shall be exposed, set this flag to ``True``.
948
959
949 :param details: details can be 'basic|full' basic gives only a subset of
960 :param details: details can be 'basic|full' basic gives only a subset of
950 the available user information that includes user_id, name and emails.
961 the available user information that includes user_id, name and emails.
951 """
962 """
952 user = self
963 user = self
953 user_data = self.user_data
964 user_data = self.user_data
954 data = {
965 data = {
955 'user_id': user.user_id,
966 'user_id': user.user_id,
956 'username': user.username,
967 'username': user.username,
957 'firstname': user.name,
968 'firstname': user.name,
958 'lastname': user.lastname,
969 'lastname': user.lastname,
959 'email': user.email,
970 'email': user.email,
960 'emails': user.emails,
971 'emails': user.emails,
961 }
972 }
962 if details == 'basic':
973 if details == 'basic':
963 return data
974 return data
964
975
965 auth_token_length = 40
976 auth_token_length = 40
966 auth_token_replacement = '*' * auth_token_length
977 auth_token_replacement = '*' * auth_token_length
967
978
968 extras = {
979 extras = {
969 'auth_tokens': [auth_token_replacement],
980 'auth_tokens': [auth_token_replacement],
970 'active': user.active,
981 'active': user.active,
971 'admin': user.admin,
982 'admin': user.admin,
972 'extern_type': user.extern_type,
983 'extern_type': user.extern_type,
973 'extern_name': user.extern_name,
984 'extern_name': user.extern_name,
974 'last_login': user.last_login,
985 'last_login': user.last_login,
975 'last_activity': user.last_activity,
986 'last_activity': user.last_activity,
976 'ip_addresses': user.ip_addresses,
987 'ip_addresses': user.ip_addresses,
977 'language': user_data.get('language')
988 'language': user_data.get('language')
978 }
989 }
979 data.update(extras)
990 data.update(extras)
980
991
981 if include_secrets:
992 if include_secrets:
982 data['auth_tokens'] = user.auth_tokens
993 data['auth_tokens'] = user.auth_tokens
983 return data
994 return data
984
995
985 def __json__(self):
996 def __json__(self):
986 data = {
997 data = {
987 'full_name': self.full_name,
998 'full_name': self.full_name,
988 'full_name_or_username': self.full_name_or_username,
999 'full_name_or_username': self.full_name_or_username,
989 'short_contact': self.short_contact,
1000 'short_contact': self.short_contact,
990 'full_contact': self.full_contact,
1001 'full_contact': self.full_contact,
991 }
1002 }
992 data.update(self.get_api_data())
1003 data.update(self.get_api_data())
993 return data
1004 return data
994
1005
995
1006
996 class UserApiKeys(Base, BaseModel):
1007 class UserApiKeys(Base, BaseModel):
997 __tablename__ = 'user_api_keys'
1008 __tablename__ = 'user_api_keys'
998 __table_args__ = (
1009 __table_args__ = (
999 Index('uak_api_key_idx', 'api_key', unique=True),
1010 Index('uak_api_key_idx', 'api_key', unique=True),
1000 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
1011 Index('uak_api_key_expires_idx', 'api_key', 'expires'),
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1013 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1003 )
1014 )
1004 __mapper_args__ = {}
1015 __mapper_args__ = {}
1005
1016
1006 # ApiKey role
1017 # ApiKey role
1007 ROLE_ALL = 'token_role_all'
1018 ROLE_ALL = 'token_role_all'
1008 ROLE_HTTP = 'token_role_http'
1019 ROLE_HTTP = 'token_role_http'
1009 ROLE_VCS = 'token_role_vcs'
1020 ROLE_VCS = 'token_role_vcs'
1010 ROLE_API = 'token_role_api'
1021 ROLE_API = 'token_role_api'
1011 ROLE_FEED = 'token_role_feed'
1022 ROLE_FEED = 'token_role_feed'
1012 ROLE_PASSWORD_RESET = 'token_password_reset'
1023 ROLE_PASSWORD_RESET = 'token_password_reset'
1013
1024
1014 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED]
1025 ROLES = [ROLE_ALL, ROLE_HTTP, ROLE_VCS, ROLE_API, ROLE_FEED]
1015
1026
1016 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1027 user_api_key_id = Column("user_api_key_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1017 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1028 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1018 api_key = Column("api_key", String(255), nullable=False, unique=True)
1029 api_key = Column("api_key", String(255), nullable=False, unique=True)
1019 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1030 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1020 expires = Column('expires', Float(53), nullable=False)
1031 expires = Column('expires', Float(53), nullable=False)
1021 role = Column('role', String(255), nullable=True)
1032 role = Column('role', String(255), nullable=True)
1022 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1033 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1023
1034
1024 # scope columns
1035 # scope columns
1025 repo_id = Column(
1036 repo_id = Column(
1026 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
1037 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
1027 nullable=True, unique=None, default=None)
1038 nullable=True, unique=None, default=None)
1028 repo = relationship('Repository', lazy='joined')
1039 repo = relationship('Repository', lazy='joined')
1029
1040
1030 repo_group_id = Column(
1041 repo_group_id = Column(
1031 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
1042 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
1032 nullable=True, unique=None, default=None)
1043 nullable=True, unique=None, default=None)
1033 repo_group = relationship('RepoGroup', lazy='joined')
1044 repo_group = relationship('RepoGroup', lazy='joined')
1034
1045
1035 user = relationship('User', lazy='joined')
1046 user = relationship('User', lazy='joined')
1036
1047
1037 def __unicode__(self):
1048 def __unicode__(self):
1038 return u"<%s('%s')>" % (self.__class__.__name__, self.role)
1049 return u"<%s('%s')>" % (self.__class__.__name__, self.role)
1039
1050
1040 def __json__(self):
1051 def __json__(self):
1041 data = {
1052 data = {
1042 'auth_token': self.api_key,
1053 'auth_token': self.api_key,
1043 'role': self.role,
1054 'role': self.role,
1044 'scope': self.scope_humanized,
1055 'scope': self.scope_humanized,
1045 'expired': self.expired
1056 'expired': self.expired
1046 }
1057 }
1047 return data
1058 return data
1048
1059
1049 def get_api_data(self, include_secrets=False):
1060 def get_api_data(self, include_secrets=False):
1050 data = self.__json__()
1061 data = self.__json__()
1051 if include_secrets:
1062 if include_secrets:
1052 return data
1063 return data
1053 else:
1064 else:
1054 data['auth_token'] = self.token_obfuscated
1065 data['auth_token'] = self.token_obfuscated
1055 return data
1066 return data
1056
1067
1057 @hybrid_property
1068 @hybrid_property
1058 def description_safe(self):
1069 def description_safe(self):
1059 from rhodecode.lib import helpers as h
1070 from rhodecode.lib import helpers as h
1060 return h.escape(self.description)
1071 return h.escape(self.description)
1061
1072
1062 @property
1073 @property
1063 def expired(self):
1074 def expired(self):
1064 if self.expires == -1:
1075 if self.expires == -1:
1065 return False
1076 return False
1066 return time.time() > self.expires
1077 return time.time() > self.expires
1067
1078
1068 @classmethod
1079 @classmethod
1069 def _get_role_name(cls, role):
1080 def _get_role_name(cls, role):
1070 return {
1081 return {
1071 cls.ROLE_ALL: _('all'),
1082 cls.ROLE_ALL: _('all'),
1072 cls.ROLE_HTTP: _('http/web interface'),
1083 cls.ROLE_HTTP: _('http/web interface'),
1073 cls.ROLE_VCS: _('vcs (git/hg/svn protocol)'),
1084 cls.ROLE_VCS: _('vcs (git/hg/svn protocol)'),
1074 cls.ROLE_API: _('api calls'),
1085 cls.ROLE_API: _('api calls'),
1075 cls.ROLE_FEED: _('feed access'),
1086 cls.ROLE_FEED: _('feed access'),
1076 }.get(role, role)
1087 }.get(role, role)
1077
1088
1078 @property
1089 @property
1079 def role_humanized(self):
1090 def role_humanized(self):
1080 return self._get_role_name(self.role)
1091 return self._get_role_name(self.role)
1081
1092
1082 def _get_scope(self):
1093 def _get_scope(self):
1083 if self.repo:
1094 if self.repo:
1084 return repr(self.repo)
1095 return repr(self.repo)
1085 if self.repo_group:
1096 if self.repo_group:
1086 return repr(self.repo_group) + ' (recursive)'
1097 return repr(self.repo_group) + ' (recursive)'
1087 return 'global'
1098 return 'global'
1088
1099
1089 @property
1100 @property
1090 def scope_humanized(self):
1101 def scope_humanized(self):
1091 return self._get_scope()
1102 return self._get_scope()
1092
1103
1093 @property
1104 @property
1094 def token_obfuscated(self):
1105 def token_obfuscated(self):
1095 if self.api_key:
1106 if self.api_key:
1096 return self.api_key[:4] + "****"
1107 return self.api_key[:4] + "****"
1097
1108
1098
1109
1099 class UserEmailMap(Base, BaseModel):
1110 class UserEmailMap(Base, BaseModel):
1100 __tablename__ = 'user_email_map'
1111 __tablename__ = 'user_email_map'
1101 __table_args__ = (
1112 __table_args__ = (
1102 Index('uem_email_idx', 'email'),
1113 Index('uem_email_idx', 'email'),
1103 UniqueConstraint('email'),
1114 UniqueConstraint('email'),
1104 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1115 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1105 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1116 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1106 )
1117 )
1107 __mapper_args__ = {}
1118 __mapper_args__ = {}
1108
1119
1109 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1120 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1110 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1121 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1111 _email = Column("email", String(255), nullable=True, unique=False, default=None)
1122 _email = Column("email", String(255), nullable=True, unique=False, default=None)
1112 user = relationship('User', lazy='joined')
1123 user = relationship('User', lazy='joined')
1113
1124
1114 @validates('_email')
1125 @validates('_email')
1115 def validate_email(self, key, email):
1126 def validate_email(self, key, email):
1116 # check if this email is not main one
1127 # check if this email is not main one
1117 main_email = Session().query(User).filter(User.email == email).scalar()
1128 main_email = Session().query(User).filter(User.email == email).scalar()
1118 if main_email is not None:
1129 if main_email is not None:
1119 raise AttributeError('email %s is present is user table' % email)
1130 raise AttributeError('email %s is present is user table' % email)
1120 return email
1131 return email
1121
1132
1122 @hybrid_property
1133 @hybrid_property
1123 def email(self):
1134 def email(self):
1124 return self._email
1135 return self._email
1125
1136
1126 @email.setter
1137 @email.setter
1127 def email(self, val):
1138 def email(self, val):
1128 self._email = val.lower() if val else None
1139 self._email = val.lower() if val else None
1129
1140
1130
1141
1131 class UserIpMap(Base, BaseModel):
1142 class UserIpMap(Base, BaseModel):
1132 __tablename__ = 'user_ip_map'
1143 __tablename__ = 'user_ip_map'
1133 __table_args__ = (
1144 __table_args__ = (
1134 UniqueConstraint('user_id', 'ip_addr'),
1145 UniqueConstraint('user_id', 'ip_addr'),
1135 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1136 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1147 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1137 )
1148 )
1138 __mapper_args__ = {}
1149 __mapper_args__ = {}
1139
1150
1140 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1151 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1141 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1152 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1142 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
1153 ip_addr = Column("ip_addr", String(255), nullable=True, unique=False, default=None)
1143 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
1154 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
1144 description = Column("description", String(10000), nullable=True, unique=None, default=None)
1155 description = Column("description", String(10000), nullable=True, unique=None, default=None)
1145 user = relationship('User', lazy='joined')
1156 user = relationship('User', lazy='joined')
1146
1157
1147 @hybrid_property
1158 @hybrid_property
1148 def description_safe(self):
1159 def description_safe(self):
1149 from rhodecode.lib import helpers as h
1160 from rhodecode.lib import helpers as h
1150 return h.escape(self.description)
1161 return h.escape(self.description)
1151
1162
1152 @classmethod
1163 @classmethod
1153 def _get_ip_range(cls, ip_addr):
1164 def _get_ip_range(cls, ip_addr):
1154 net = ipaddress.ip_network(safe_unicode(ip_addr), strict=False)
1165 net = ipaddress.ip_network(safe_unicode(ip_addr), strict=False)
1155 return [str(net.network_address), str(net.broadcast_address)]
1166 return [str(net.network_address), str(net.broadcast_address)]
1156
1167
1157 def __json__(self):
1168 def __json__(self):
1158 return {
1169 return {
1159 'ip_addr': self.ip_addr,
1170 'ip_addr': self.ip_addr,
1160 'ip_range': self._get_ip_range(self.ip_addr),
1171 'ip_range': self._get_ip_range(self.ip_addr),
1161 }
1172 }
1162
1173
1163 def __unicode__(self):
1174 def __unicode__(self):
1164 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
1175 return u"<%s('user_id:%s=>%s')>" % (self.__class__.__name__,
1165 self.user_id, self.ip_addr)
1176 self.user_id, self.ip_addr)
1166
1177
1167
1178
1168 class UserSshKeys(Base, BaseModel):
1179 class UserSshKeys(Base, BaseModel):
1169 __tablename__ = 'user_ssh_keys'
1180 __tablename__ = 'user_ssh_keys'
1170 __table_args__ = (
1181 __table_args__ = (
1171 Index('usk_ssh_key_fingerprint_idx', 'ssh_key_fingerprint'),
1182 Index('usk_ssh_key_fingerprint_idx', 'ssh_key_fingerprint'),
1172
1183
1173 UniqueConstraint('ssh_key_fingerprint'),
1184 UniqueConstraint('ssh_key_fingerprint'),
1174
1185
1175 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1186 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1176 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1187 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
1177 )
1188 )
1178 __mapper_args__ = {}
1189 __mapper_args__ = {}
1179
1190
1180 ssh_key_id = Column('ssh_key_id', Integer(), nullable=False, unique=True, default=None, primary_key=True)
1191 ssh_key_id = Column('ssh_key_id', Integer(), nullable=False, unique=True, default=None, primary_key=True)
1181 ssh_key_data = Column('ssh_key_data', String(10240), nullable=False, unique=None, default=None)
1192 ssh_key_data = Column('ssh_key_data', String(10240), nullable=False, unique=None, default=None)
1182 ssh_key_fingerprint = Column('ssh_key_fingerprint', String(1024), nullable=False, unique=None, default=None)
1193 ssh_key_fingerprint = Column('ssh_key_fingerprint', String(1024), nullable=False, unique=None, default=None)
1183
1194
1184 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1195 description = Column('description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
1185
1196
1186 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1197 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1187 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True, default=None)
1198 accessed_on = Column('accessed_on', DateTime(timezone=False), nullable=True, default=None)
1188 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1199 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1189
1200
1190 user = relationship('User', lazy='joined')
1201 user = relationship('User', lazy='joined')
1191
1202
1192 def __json__(self):
1203 def __json__(self):
1193 data = {
1204 data = {
1194 'ssh_fingerprint': self.ssh_key_fingerprint,
1205 'ssh_fingerprint': self.ssh_key_fingerprint,
1195 'description': self.description,
1206 'description': self.description,
1196 'created_on': self.created_on
1207 'created_on': self.created_on
1197 }
1208 }
1198 return data
1209 return data
1199
1210
1200 def get_api_data(self):
1211 def get_api_data(self):
1201 data = self.__json__()
1212 data = self.__json__()
1202 return data
1213 return data
1203
1214
1204
1215
1205 class UserLog(Base, BaseModel):
1216 class UserLog(Base, BaseModel):
1206 __tablename__ = 'user_logs'
1217 __tablename__ = 'user_logs'
1207 __table_args__ = (
1218 __table_args__ = (
1208 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1209 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1220 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1210 )
1221 )
1211 VERSION_1 = 'v1'
1222 VERSION_1 = 'v1'
1212 VERSION_2 = 'v2'
1223 VERSION_2 = 'v2'
1213 VERSIONS = [VERSION_1, VERSION_2]
1224 VERSIONS = [VERSION_1, VERSION_2]
1214
1225
1215 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1226 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1216 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1227 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1217 username = Column("username", String(255), nullable=True, unique=None, default=None)
1228 username = Column("username", String(255), nullable=True, unique=None, default=None)
1218 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
1229 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
1219 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
1230 repository_name = Column("repository_name", String(255), nullable=True, unique=None, default=None)
1220 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
1231 user_ip = Column("user_ip", String(255), nullable=True, unique=None, default=None)
1221 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
1232 action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
1222 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
1233 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
1223
1234
1224 version = Column("version", String(255), nullable=True, default=VERSION_1)
1235 version = Column("version", String(255), nullable=True, default=VERSION_1)
1225 user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
1236 user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
1226 action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
1237 action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
1227
1238
1228 def __unicode__(self):
1239 def __unicode__(self):
1229 return u"<%s('id:%s:%s')>" % (
1240 return u"<%s('id:%s:%s')>" % (
1230 self.__class__.__name__, self.repository_name, self.action)
1241 self.__class__.__name__, self.repository_name, self.action)
1231
1242
1232 def __json__(self):
1243 def __json__(self):
1233 return {
1244 return {
1234 'user_id': self.user_id,
1245 'user_id': self.user_id,
1235 'username': self.username,
1246 'username': self.username,
1236 'repository_id': self.repository_id,
1247 'repository_id': self.repository_id,
1237 'repository_name': self.repository_name,
1248 'repository_name': self.repository_name,
1238 'user_ip': self.user_ip,
1249 'user_ip': self.user_ip,
1239 'action_date': self.action_date,
1250 'action_date': self.action_date,
1240 'action': self.action,
1251 'action': self.action,
1241 }
1252 }
1242
1253
1243 @property
1254 @property
1244 def action_as_day(self):
1255 def action_as_day(self):
1245 return datetime.date(*self.action_date.timetuple()[:3])
1256 return datetime.date(*self.action_date.timetuple()[:3])
1246
1257
1247 user = relationship('User')
1258 user = relationship('User')
1248 repository = relationship('Repository', cascade='')
1259 repository = relationship('Repository', cascade='')
1249
1260
1250
1261
1251 class UserGroup(Base, BaseModel):
1262 class UserGroup(Base, BaseModel):
1252 __tablename__ = 'users_groups'
1263 __tablename__ = 'users_groups'
1253 __table_args__ = (
1264 __table_args__ = (
1254 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1265 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1255 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1266 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1256 )
1267 )
1257
1268
1258 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1269 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1259 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
1270 users_group_name = Column("users_group_name", String(255), nullable=False, unique=True, default=None)
1260 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
1271 user_group_description = Column("user_group_description", String(10000), nullable=True, unique=None, default=None)
1261 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
1272 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
1262 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
1273 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
1263 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1274 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1264 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1275 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1265 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
1276 _group_data = Column("group_data", LargeBinary(), nullable=True) # JSON data
1266
1277
1267 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
1278 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
1268 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
1279 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
1269 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1280 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1270 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1281 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1271 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1282 user_user_group_to_perm = relationship('UserUserGroupToPerm', cascade='all')
1272 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1283 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
1273
1284
1274 user_group_review_rules = relationship('RepoReviewRuleUserGroup', cascade='all')
1285 user_group_review_rules = relationship('RepoReviewRuleUserGroup', cascade='all')
1275 user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id")
1286 user = relationship('User', primaryjoin="User.user_id==UserGroup.user_id")
1276
1287
1277 @classmethod
1288 @classmethod
1278 def _load_group_data(cls, column):
1289 def _load_group_data(cls, column):
1279 if not column:
1290 if not column:
1280 return {}
1291 return {}
1281
1292
1282 try:
1293 try:
1283 return json.loads(column) or {}
1294 return json.loads(column) or {}
1284 except TypeError:
1295 except TypeError:
1285 return {}
1296 return {}
1286
1297
1287 @hybrid_property
1298 @hybrid_property
1288 def description_safe(self):
1299 def description_safe(self):
1289 from rhodecode.lib import helpers as h
1300 from rhodecode.lib import helpers as h
1290 return h.escape(self.description)
1301 return h.escape(self.description)
1291
1302
1292 @hybrid_property
1303 @hybrid_property
1293 def group_data(self):
1304 def group_data(self):
1294 return self._load_group_data(self._group_data)
1305 return self._load_group_data(self._group_data)
1295
1306
1296 @group_data.expression
1307 @group_data.expression
1297 def group_data(self, **kwargs):
1308 def group_data(self, **kwargs):
1298 return self._group_data
1309 return self._group_data
1299
1310
1300 @group_data.setter
1311 @group_data.setter
1301 def group_data(self, val):
1312 def group_data(self, val):
1302 try:
1313 try:
1303 self._group_data = json.dumps(val)
1314 self._group_data = json.dumps(val)
1304 except Exception:
1315 except Exception:
1305 log.error(traceback.format_exc())
1316 log.error(traceback.format_exc())
1306
1317
1307 def __unicode__(self):
1318 def __unicode__(self):
1308 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1319 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
1309 self.users_group_id,
1320 self.users_group_id,
1310 self.users_group_name)
1321 self.users_group_name)
1311
1322
1312 @classmethod
1323 @classmethod
1313 def get_by_group_name(cls, group_name, cache=False,
1324 def get_by_group_name(cls, group_name, cache=False,
1314 case_insensitive=False):
1325 case_insensitive=False):
1315 if case_insensitive:
1326 if case_insensitive:
1316 q = cls.query().filter(func.lower(cls.users_group_name) ==
1327 q = cls.query().filter(func.lower(cls.users_group_name) ==
1317 func.lower(group_name))
1328 func.lower(group_name))
1318
1329
1319 else:
1330 else:
1320 q = cls.query().filter(cls.users_group_name == group_name)
1331 q = cls.query().filter(cls.users_group_name == group_name)
1321 if cache:
1332 if cache:
1322 q = q.options(
1333 q = q.options(
1323 FromCache("sql_cache_short", "get_group_%s" % _hash_key(group_name)))
1334 FromCache("sql_cache_short", "get_group_%s" % _hash_key(group_name)))
1324 return q.scalar()
1335 return q.scalar()
1325
1336
1326 @classmethod
1337 @classmethod
1327 def get(cls, user_group_id, cache=False):
1338 def get(cls, user_group_id, cache=False):
1328 user_group = cls.query()
1339 user_group = cls.query()
1329 if cache:
1340 if cache:
1330 user_group = user_group.options(
1341 user_group = user_group.options(
1331 FromCache("sql_cache_short", "get_users_group_%s" % user_group_id))
1342 FromCache("sql_cache_short", "get_users_group_%s" % user_group_id))
1332 return user_group.get(user_group_id)
1343 return user_group.get(user_group_id)
1333
1344
1334 def permissions(self, with_admins=True, with_owner=True):
1345 def permissions(self, with_admins=True, with_owner=True):
1335 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1346 q = UserUserGroupToPerm.query().filter(UserUserGroupToPerm.user_group == self)
1336 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1347 q = q.options(joinedload(UserUserGroupToPerm.user_group),
1337 joinedload(UserUserGroupToPerm.user),
1348 joinedload(UserUserGroupToPerm.user),
1338 joinedload(UserUserGroupToPerm.permission),)
1349 joinedload(UserUserGroupToPerm.permission),)
1339
1350
1340 # get owners and admins and permissions. We do a trick of re-writing
1351 # get owners and admins and permissions. We do a trick of re-writing
1341 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1352 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1342 # has a global reference and changing one object propagates to all
1353 # has a global reference and changing one object propagates to all
1343 # others. This means if admin is also an owner admin_row that change
1354 # others. This means if admin is also an owner admin_row that change
1344 # would propagate to both objects
1355 # would propagate to both objects
1345 perm_rows = []
1356 perm_rows = []
1346 for _usr in q.all():
1357 for _usr in q.all():
1347 usr = AttributeDict(_usr.user.get_dict())
1358 usr = AttributeDict(_usr.user.get_dict())
1348 usr.permission = _usr.permission.permission_name
1359 usr.permission = _usr.permission.permission_name
1349 perm_rows.append(usr)
1360 perm_rows.append(usr)
1350
1361
1351 # filter the perm rows by 'default' first and then sort them by
1362 # filter the perm rows by 'default' first and then sort them by
1352 # admin,write,read,none permissions sorted again alphabetically in
1363 # admin,write,read,none permissions sorted again alphabetically in
1353 # each group
1364 # each group
1354 perm_rows = sorted(perm_rows, key=display_sort)
1365 perm_rows = sorted(perm_rows, key=display_user_sort)
1355
1366
1356 _admin_perm = 'usergroup.admin'
1367 _admin_perm = 'usergroup.admin'
1357 owner_row = []
1368 owner_row = []
1358 if with_owner:
1369 if with_owner:
1359 usr = AttributeDict(self.user.get_dict())
1370 usr = AttributeDict(self.user.get_dict())
1360 usr.owner_row = True
1371 usr.owner_row = True
1361 usr.permission = _admin_perm
1372 usr.permission = _admin_perm
1362 owner_row.append(usr)
1373 owner_row.append(usr)
1363
1374
1364 super_admin_rows = []
1375 super_admin_rows = []
1365 if with_admins:
1376 if with_admins:
1366 for usr in User.get_all_super_admins():
1377 for usr in User.get_all_super_admins():
1367 # if this admin is also owner, don't double the record
1378 # if this admin is also owner, don't double the record
1368 if usr.user_id == owner_row[0].user_id:
1379 if usr.user_id == owner_row[0].user_id:
1369 owner_row[0].admin_row = True
1380 owner_row[0].admin_row = True
1370 else:
1381 else:
1371 usr = AttributeDict(usr.get_dict())
1382 usr = AttributeDict(usr.get_dict())
1372 usr.admin_row = True
1383 usr.admin_row = True
1373 usr.permission = _admin_perm
1384 usr.permission = _admin_perm
1374 super_admin_rows.append(usr)
1385 super_admin_rows.append(usr)
1375
1386
1376 return super_admin_rows + owner_row + perm_rows
1387 return super_admin_rows + owner_row + perm_rows
1377
1388
1378 def permission_user_groups(self):
1389 def permission_user_groups(self):
1379 q = UserGroupUserGroupToPerm.query().filter(UserGroupUserGroupToPerm.target_user_group == self)
1390 q = UserGroupUserGroupToPerm.query().filter(UserGroupUserGroupToPerm.target_user_group == self)
1380 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1391 q = q.options(joinedload(UserGroupUserGroupToPerm.user_group),
1381 joinedload(UserGroupUserGroupToPerm.target_user_group),
1392 joinedload(UserGroupUserGroupToPerm.target_user_group),
1382 joinedload(UserGroupUserGroupToPerm.permission),)
1393 joinedload(UserGroupUserGroupToPerm.permission),)
1383
1394
1384 perm_rows = []
1395 perm_rows = []
1385 for _user_group in q.all():
1396 for _user_group in q.all():
1386 usr = AttributeDict(_user_group.user_group.get_dict())
1397 usr = AttributeDict(_user_group.user_group.get_dict())
1387 usr.permission = _user_group.permission.permission_name
1398 usr.permission = _user_group.permission.permission_name
1388 perm_rows.append(usr)
1399 perm_rows.append(usr)
1389
1400
1401 perm_rows = sorted(perm_rows, key=display_user_group_sort)
1390 return perm_rows
1402 return perm_rows
1391
1403
1392 def _get_default_perms(self, user_group, suffix=''):
1404 def _get_default_perms(self, user_group, suffix=''):
1393 from rhodecode.model.permission import PermissionModel
1405 from rhodecode.model.permission import PermissionModel
1394 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1406 return PermissionModel().get_default_perms(user_group.users_group_to_perm, suffix)
1395
1407
1396 def get_default_perms(self, suffix=''):
1408 def get_default_perms(self, suffix=''):
1397 return self._get_default_perms(self, suffix)
1409 return self._get_default_perms(self, suffix)
1398
1410
1399 def get_api_data(self, with_group_members=True, include_secrets=False):
1411 def get_api_data(self, with_group_members=True, include_secrets=False):
1400 """
1412 """
1401 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1413 :param include_secrets: See :meth:`User.get_api_data`, this parameter is
1402 basically forwarded.
1414 basically forwarded.
1403
1415
1404 """
1416 """
1405 user_group = self
1417 user_group = self
1406 data = {
1418 data = {
1407 'users_group_id': user_group.users_group_id,
1419 'users_group_id': user_group.users_group_id,
1408 'group_name': user_group.users_group_name,
1420 'group_name': user_group.users_group_name,
1409 'group_description': user_group.user_group_description,
1421 'group_description': user_group.user_group_description,
1410 'active': user_group.users_group_active,
1422 'active': user_group.users_group_active,
1411 'owner': user_group.user.username,
1423 'owner': user_group.user.username,
1412 'owner_email': user_group.user.email,
1424 'owner_email': user_group.user.email,
1413 }
1425 }
1414
1426
1415 if with_group_members:
1427 if with_group_members:
1416 users = []
1428 users = []
1417 for user in user_group.members:
1429 for user in user_group.members:
1418 user = user.user
1430 user = user.user
1419 users.append(user.get_api_data(include_secrets=include_secrets))
1431 users.append(user.get_api_data(include_secrets=include_secrets))
1420 data['users'] = users
1432 data['users'] = users
1421
1433
1422 return data
1434 return data
1423
1435
1424
1436
1425 class UserGroupMember(Base, BaseModel):
1437 class UserGroupMember(Base, BaseModel):
1426 __tablename__ = 'users_groups_members'
1438 __tablename__ = 'users_groups_members'
1427 __table_args__ = (
1439 __table_args__ = (
1428 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1429 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1441 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1430 )
1442 )
1431
1443
1432 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1444 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1433 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1445 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1446 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1435
1447
1436 user = relationship('User', lazy='joined')
1448 user = relationship('User', lazy='joined')
1437 users_group = relationship('UserGroup')
1449 users_group = relationship('UserGroup')
1438
1450
1439 def __init__(self, gr_id='', u_id=''):
1451 def __init__(self, gr_id='', u_id=''):
1440 self.users_group_id = gr_id
1452 self.users_group_id = gr_id
1441 self.user_id = u_id
1453 self.user_id = u_id
1442
1454
1443
1455
1444 class RepositoryField(Base, BaseModel):
1456 class RepositoryField(Base, BaseModel):
1445 __tablename__ = 'repositories_fields'
1457 __tablename__ = 'repositories_fields'
1446 __table_args__ = (
1458 __table_args__ = (
1447 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1459 UniqueConstraint('repository_id', 'field_key'), # no-multi field
1448 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1460 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1449 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1461 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1450 )
1462 )
1451 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1463 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
1452
1464
1453 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1465 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1454 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1466 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1455 field_key = Column("field_key", String(250))
1467 field_key = Column("field_key", String(250))
1456 field_label = Column("field_label", String(1024), nullable=False)
1468 field_label = Column("field_label", String(1024), nullable=False)
1457 field_value = Column("field_value", String(10000), nullable=False)
1469 field_value = Column("field_value", String(10000), nullable=False)
1458 field_desc = Column("field_desc", String(1024), nullable=False)
1470 field_desc = Column("field_desc", String(1024), nullable=False)
1459 field_type = Column("field_type", String(255), nullable=False, unique=None)
1471 field_type = Column("field_type", String(255), nullable=False, unique=None)
1460 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1472 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1461
1473
1462 repository = relationship('Repository')
1474 repository = relationship('Repository')
1463
1475
1464 @property
1476 @property
1465 def field_key_prefixed(self):
1477 def field_key_prefixed(self):
1466 return 'ex_%s' % self.field_key
1478 return 'ex_%s' % self.field_key
1467
1479
1468 @classmethod
1480 @classmethod
1469 def un_prefix_key(cls, key):
1481 def un_prefix_key(cls, key):
1470 if key.startswith(cls.PREFIX):
1482 if key.startswith(cls.PREFIX):
1471 return key[len(cls.PREFIX):]
1483 return key[len(cls.PREFIX):]
1472 return key
1484 return key
1473
1485
1474 @classmethod
1486 @classmethod
1475 def get_by_key_name(cls, key, repo):
1487 def get_by_key_name(cls, key, repo):
1476 row = cls.query()\
1488 row = cls.query()\
1477 .filter(cls.repository == repo)\
1489 .filter(cls.repository == repo)\
1478 .filter(cls.field_key == key).scalar()
1490 .filter(cls.field_key == key).scalar()
1479 return row
1491 return row
1480
1492
1481
1493
1482 class Repository(Base, BaseModel):
1494 class Repository(Base, BaseModel):
1483 __tablename__ = 'repositories'
1495 __tablename__ = 'repositories'
1484 __table_args__ = (
1496 __table_args__ = (
1485 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1497 Index('r_repo_name_idx', 'repo_name', mysql_length=255),
1486 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1498 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1487 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1499 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
1488 )
1500 )
1489 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1501 DEFAULT_CLONE_URI = '{scheme}://{user}@{netloc}/{repo}'
1490 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1502 DEFAULT_CLONE_URI_ID = '{scheme}://{user}@{netloc}/_{repoid}'
1491
1503
1492 STATE_CREATED = 'repo_state_created'
1504 STATE_CREATED = 'repo_state_created'
1493 STATE_PENDING = 'repo_state_pending'
1505 STATE_PENDING = 'repo_state_pending'
1494 STATE_ERROR = 'repo_state_error'
1506 STATE_ERROR = 'repo_state_error'
1495
1507
1496 LOCK_AUTOMATIC = 'lock_auto'
1508 LOCK_AUTOMATIC = 'lock_auto'
1497 LOCK_API = 'lock_api'
1509 LOCK_API = 'lock_api'
1498 LOCK_WEB = 'lock_web'
1510 LOCK_WEB = 'lock_web'
1499 LOCK_PULL = 'lock_pull'
1511 LOCK_PULL = 'lock_pull'
1500
1512
1501 NAME_SEP = URL_SEP
1513 NAME_SEP = URL_SEP
1502
1514
1503 repo_id = Column(
1515 repo_id = Column(
1504 "repo_id", Integer(), nullable=False, unique=True, default=None,
1516 "repo_id", Integer(), nullable=False, unique=True, default=None,
1505 primary_key=True)
1517 primary_key=True)
1506 _repo_name = Column(
1518 _repo_name = Column(
1507 "repo_name", Text(), nullable=False, default=None)
1519 "repo_name", Text(), nullable=False, default=None)
1508 _repo_name_hash = Column(
1520 _repo_name_hash = Column(
1509 "repo_name_hash", String(255), nullable=False, unique=True)
1521 "repo_name_hash", String(255), nullable=False, unique=True)
1510 repo_state = Column("repo_state", String(255), nullable=True)
1522 repo_state = Column("repo_state", String(255), nullable=True)
1511
1523
1512 clone_uri = Column(
1524 clone_uri = Column(
1513 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1525 "clone_uri", EncryptedTextValue(), nullable=True, unique=False,
1514 default=None)
1526 default=None)
1515 repo_type = Column(
1527 repo_type = Column(
1516 "repo_type", String(255), nullable=False, unique=False, default=None)
1528 "repo_type", String(255), nullable=False, unique=False, default=None)
1517 user_id = Column(
1529 user_id = Column(
1518 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1530 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
1519 unique=False, default=None)
1531 unique=False, default=None)
1520 private = Column(
1532 private = Column(
1521 "private", Boolean(), nullable=True, unique=None, default=None)
1533 "private", Boolean(), nullable=True, unique=None, default=None)
1522 enable_statistics = Column(
1534 enable_statistics = Column(
1523 "statistics", Boolean(), nullable=True, unique=None, default=True)
1535 "statistics", Boolean(), nullable=True, unique=None, default=True)
1524 enable_downloads = Column(
1536 enable_downloads = Column(
1525 "downloads", Boolean(), nullable=True, unique=None, default=True)
1537 "downloads", Boolean(), nullable=True, unique=None, default=True)
1526 description = Column(
1538 description = Column(
1527 "description", String(10000), nullable=True, unique=None, default=None)
1539 "description", String(10000), nullable=True, unique=None, default=None)
1528 created_on = Column(
1540 created_on = Column(
1529 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1541 'created_on', DateTime(timezone=False), nullable=True, unique=None,
1530 default=datetime.datetime.now)
1542 default=datetime.datetime.now)
1531 updated_on = Column(
1543 updated_on = Column(
1532 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1544 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
1533 default=datetime.datetime.now)
1545 default=datetime.datetime.now)
1534 _landing_revision = Column(
1546 _landing_revision = Column(
1535 "landing_revision", String(255), nullable=False, unique=False,
1547 "landing_revision", String(255), nullable=False, unique=False,
1536 default=None)
1548 default=None)
1537 enable_locking = Column(
1549 enable_locking = Column(
1538 "enable_locking", Boolean(), nullable=False, unique=None,
1550 "enable_locking", Boolean(), nullable=False, unique=None,
1539 default=False)
1551 default=False)
1540 _locked = Column(
1552 _locked = Column(
1541 "locked", String(255), nullable=True, unique=False, default=None)
1553 "locked", String(255), nullable=True, unique=False, default=None)
1542 _changeset_cache = Column(
1554 _changeset_cache = Column(
1543 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1555 "changeset_cache", LargeBinary(), nullable=True) # JSON data
1544
1556
1545 fork_id = Column(
1557 fork_id = Column(
1546 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1558 "fork_id", Integer(), ForeignKey('repositories.repo_id'),
1547 nullable=True, unique=False, default=None)
1559 nullable=True, unique=False, default=None)
1548 group_id = Column(
1560 group_id = Column(
1549 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1561 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1550 unique=False, default=None)
1562 unique=False, default=None)
1551
1563
1552 user = relationship('User', lazy='joined')
1564 user = relationship('User', lazy='joined')
1553 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1565 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1554 group = relationship('RepoGroup', lazy='joined')
1566 group = relationship('RepoGroup', lazy='joined')
1555 repo_to_perm = relationship(
1567 repo_to_perm = relationship(
1556 'UserRepoToPerm', cascade='all',
1568 'UserRepoToPerm', cascade='all',
1557 order_by='UserRepoToPerm.repo_to_perm_id')
1569 order_by='UserRepoToPerm.repo_to_perm_id')
1558 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1570 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
1559 stats = relationship('Statistics', cascade='all', uselist=False)
1571 stats = relationship('Statistics', cascade='all', uselist=False)
1560
1572
1561 followers = relationship(
1573 followers = relationship(
1562 'UserFollowing',
1574 'UserFollowing',
1563 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1575 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
1564 cascade='all')
1576 cascade='all')
1565 extra_fields = relationship(
1577 extra_fields = relationship(
1566 'RepositoryField', cascade="all, delete, delete-orphan")
1578 'RepositoryField', cascade="all, delete, delete-orphan")
1567 logs = relationship('UserLog')
1579 logs = relationship('UserLog')
1568 comments = relationship(
1580 comments = relationship(
1569 'ChangesetComment', cascade="all, delete, delete-orphan")
1581 'ChangesetComment', cascade="all, delete, delete-orphan")
1570 pull_requests_source = relationship(
1582 pull_requests_source = relationship(
1571 'PullRequest',
1583 'PullRequest',
1572 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1584 primaryjoin='PullRequest.source_repo_id==Repository.repo_id',
1573 cascade="all, delete, delete-orphan")
1585 cascade="all, delete, delete-orphan")
1574 pull_requests_target = relationship(
1586 pull_requests_target = relationship(
1575 'PullRequest',
1587 'PullRequest',
1576 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1588 primaryjoin='PullRequest.target_repo_id==Repository.repo_id',
1577 cascade="all, delete, delete-orphan")
1589 cascade="all, delete, delete-orphan")
1578 ui = relationship('RepoRhodeCodeUi', cascade="all")
1590 ui = relationship('RepoRhodeCodeUi', cascade="all")
1579 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1591 settings = relationship('RepoRhodeCodeSetting', cascade="all")
1580 integrations = relationship('Integration',
1592 integrations = relationship('Integration',
1581 cascade="all, delete, delete-orphan")
1593 cascade="all, delete, delete-orphan")
1582
1594
1583 def __unicode__(self):
1595 def __unicode__(self):
1584 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1596 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
1585 safe_unicode(self.repo_name))
1597 safe_unicode(self.repo_name))
1586
1598
1587 @hybrid_property
1599 @hybrid_property
1588 def description_safe(self):
1600 def description_safe(self):
1589 from rhodecode.lib import helpers as h
1601 from rhodecode.lib import helpers as h
1590 return h.escape(self.description)
1602 return h.escape(self.description)
1591
1603
1592 @hybrid_property
1604 @hybrid_property
1593 def landing_rev(self):
1605 def landing_rev(self):
1594 # always should return [rev_type, rev]
1606 # always should return [rev_type, rev]
1595 if self._landing_revision:
1607 if self._landing_revision:
1596 _rev_info = self._landing_revision.split(':')
1608 _rev_info = self._landing_revision.split(':')
1597 if len(_rev_info) < 2:
1609 if len(_rev_info) < 2:
1598 _rev_info.insert(0, 'rev')
1610 _rev_info.insert(0, 'rev')
1599 return [_rev_info[0], _rev_info[1]]
1611 return [_rev_info[0], _rev_info[1]]
1600 return [None, None]
1612 return [None, None]
1601
1613
1602 @landing_rev.setter
1614 @landing_rev.setter
1603 def landing_rev(self, val):
1615 def landing_rev(self, val):
1604 if ':' not in val:
1616 if ':' not in val:
1605 raise ValueError('value must be delimited with `:` and consist '
1617 raise ValueError('value must be delimited with `:` and consist '
1606 'of <rev_type>:<rev>, got %s instead' % val)
1618 'of <rev_type>:<rev>, got %s instead' % val)
1607 self._landing_revision = val
1619 self._landing_revision = val
1608
1620
1609 @hybrid_property
1621 @hybrid_property
1610 def locked(self):
1622 def locked(self):
1611 if self._locked:
1623 if self._locked:
1612 user_id, timelocked, reason = self._locked.split(':')
1624 user_id, timelocked, reason = self._locked.split(':')
1613 lock_values = int(user_id), timelocked, reason
1625 lock_values = int(user_id), timelocked, reason
1614 else:
1626 else:
1615 lock_values = [None, None, None]
1627 lock_values = [None, None, None]
1616 return lock_values
1628 return lock_values
1617
1629
1618 @locked.setter
1630 @locked.setter
1619 def locked(self, val):
1631 def locked(self, val):
1620 if val and isinstance(val, (list, tuple)):
1632 if val and isinstance(val, (list, tuple)):
1621 self._locked = ':'.join(map(str, val))
1633 self._locked = ':'.join(map(str, val))
1622 else:
1634 else:
1623 self._locked = None
1635 self._locked = None
1624
1636
1625 @hybrid_property
1637 @hybrid_property
1626 def changeset_cache(self):
1638 def changeset_cache(self):
1627 from rhodecode.lib.vcs.backends.base import EmptyCommit
1639 from rhodecode.lib.vcs.backends.base import EmptyCommit
1628 dummy = EmptyCommit().__json__()
1640 dummy = EmptyCommit().__json__()
1629 if not self._changeset_cache:
1641 if not self._changeset_cache:
1630 return dummy
1642 return dummy
1631 try:
1643 try:
1632 return json.loads(self._changeset_cache)
1644 return json.loads(self._changeset_cache)
1633 except TypeError:
1645 except TypeError:
1634 return dummy
1646 return dummy
1635 except Exception:
1647 except Exception:
1636 log.error(traceback.format_exc())
1648 log.error(traceback.format_exc())
1637 return dummy
1649 return dummy
1638
1650
1639 @changeset_cache.setter
1651 @changeset_cache.setter
1640 def changeset_cache(self, val):
1652 def changeset_cache(self, val):
1641 try:
1653 try:
1642 self._changeset_cache = json.dumps(val)
1654 self._changeset_cache = json.dumps(val)
1643 except Exception:
1655 except Exception:
1644 log.error(traceback.format_exc())
1656 log.error(traceback.format_exc())
1645
1657
1646 @hybrid_property
1658 @hybrid_property
1647 def repo_name(self):
1659 def repo_name(self):
1648 return self._repo_name
1660 return self._repo_name
1649
1661
1650 @repo_name.setter
1662 @repo_name.setter
1651 def repo_name(self, value):
1663 def repo_name(self, value):
1652 self._repo_name = value
1664 self._repo_name = value
1653 self._repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1665 self._repo_name_hash = hashlib.sha1(safe_str(value)).hexdigest()
1654
1666
1655 @classmethod
1667 @classmethod
1656 def normalize_repo_name(cls, repo_name):
1668 def normalize_repo_name(cls, repo_name):
1657 """
1669 """
1658 Normalizes os specific repo_name to the format internally stored inside
1670 Normalizes os specific repo_name to the format internally stored inside
1659 database using URL_SEP
1671 database using URL_SEP
1660
1672
1661 :param cls:
1673 :param cls:
1662 :param repo_name:
1674 :param repo_name:
1663 """
1675 """
1664 return cls.NAME_SEP.join(repo_name.split(os.sep))
1676 return cls.NAME_SEP.join(repo_name.split(os.sep))
1665
1677
1666 @classmethod
1678 @classmethod
1667 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1679 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1668 session = Session()
1680 session = Session()
1669 q = session.query(cls).filter(cls.repo_name == repo_name)
1681 q = session.query(cls).filter(cls.repo_name == repo_name)
1670
1682
1671 if cache:
1683 if cache:
1672 if identity_cache:
1684 if identity_cache:
1673 val = cls.identity_cache(session, 'repo_name', repo_name)
1685 val = cls.identity_cache(session, 'repo_name', repo_name)
1674 if val:
1686 if val:
1675 return val
1687 return val
1676 else:
1688 else:
1677 cache_key = "get_repo_by_name_%s" % _hash_key(repo_name)
1689 cache_key = "get_repo_by_name_%s" % _hash_key(repo_name)
1678 q = q.options(
1690 q = q.options(
1679 FromCache("sql_cache_short", cache_key))
1691 FromCache("sql_cache_short", cache_key))
1680
1692
1681 return q.scalar()
1693 return q.scalar()
1682
1694
1683 @classmethod
1695 @classmethod
1684 def get_by_full_path(cls, repo_full_path):
1696 def get_by_full_path(cls, repo_full_path):
1685 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1697 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
1686 repo_name = cls.normalize_repo_name(repo_name)
1698 repo_name = cls.normalize_repo_name(repo_name)
1687 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1699 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
1688
1700
1689 @classmethod
1701 @classmethod
1690 def get_repo_forks(cls, repo_id):
1702 def get_repo_forks(cls, repo_id):
1691 return cls.query().filter(Repository.fork_id == repo_id)
1703 return cls.query().filter(Repository.fork_id == repo_id)
1692
1704
1693 @classmethod
1705 @classmethod
1694 def base_path(cls):
1706 def base_path(cls):
1695 """
1707 """
1696 Returns base path when all repos are stored
1708 Returns base path when all repos are stored
1697
1709
1698 :param cls:
1710 :param cls:
1699 """
1711 """
1700 q = Session().query(RhodeCodeUi)\
1712 q = Session().query(RhodeCodeUi)\
1701 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1713 .filter(RhodeCodeUi.ui_key == cls.NAME_SEP)
1702 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1714 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1703 return q.one().ui_value
1715 return q.one().ui_value
1704
1716
1705 @classmethod
1717 @classmethod
1706 def is_valid(cls, repo_name):
1718 def is_valid(cls, repo_name):
1707 """
1719 """
1708 returns True if given repo name is a valid filesystem repository
1720 returns True if given repo name is a valid filesystem repository
1709
1721
1710 :param cls:
1722 :param cls:
1711 :param repo_name:
1723 :param repo_name:
1712 """
1724 """
1713 from rhodecode.lib.utils import is_valid_repo
1725 from rhodecode.lib.utils import is_valid_repo
1714
1726
1715 return is_valid_repo(repo_name, cls.base_path())
1727 return is_valid_repo(repo_name, cls.base_path())
1716
1728
1717 @classmethod
1729 @classmethod
1718 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1730 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1719 case_insensitive=True):
1731 case_insensitive=True):
1720 q = Repository.query()
1732 q = Repository.query()
1721
1733
1722 if not isinstance(user_id, Optional):
1734 if not isinstance(user_id, Optional):
1723 q = q.filter(Repository.user_id == user_id)
1735 q = q.filter(Repository.user_id == user_id)
1724
1736
1725 if not isinstance(group_id, Optional):
1737 if not isinstance(group_id, Optional):
1726 q = q.filter(Repository.group_id == group_id)
1738 q = q.filter(Repository.group_id == group_id)
1727
1739
1728 if case_insensitive:
1740 if case_insensitive:
1729 q = q.order_by(func.lower(Repository.repo_name))
1741 q = q.order_by(func.lower(Repository.repo_name))
1730 else:
1742 else:
1731 q = q.order_by(Repository.repo_name)
1743 q = q.order_by(Repository.repo_name)
1732 return q.all()
1744 return q.all()
1733
1745
1734 @property
1746 @property
1735 def forks(self):
1747 def forks(self):
1736 """
1748 """
1737 Return forks of this repo
1749 Return forks of this repo
1738 """
1750 """
1739 return Repository.get_repo_forks(self.repo_id)
1751 return Repository.get_repo_forks(self.repo_id)
1740
1752
1741 @property
1753 @property
1742 def parent(self):
1754 def parent(self):
1743 """
1755 """
1744 Returns fork parent
1756 Returns fork parent
1745 """
1757 """
1746 return self.fork
1758 return self.fork
1747
1759
1748 @property
1760 @property
1749 def just_name(self):
1761 def just_name(self):
1750 return self.repo_name.split(self.NAME_SEP)[-1]
1762 return self.repo_name.split(self.NAME_SEP)[-1]
1751
1763
1752 @property
1764 @property
1753 def groups_with_parents(self):
1765 def groups_with_parents(self):
1754 groups = []
1766 groups = []
1755 if self.group is None:
1767 if self.group is None:
1756 return groups
1768 return groups
1757
1769
1758 cur_gr = self.group
1770 cur_gr = self.group
1759 groups.insert(0, cur_gr)
1771 groups.insert(0, cur_gr)
1760 while 1:
1772 while 1:
1761 gr = getattr(cur_gr, 'parent_group', None)
1773 gr = getattr(cur_gr, 'parent_group', None)
1762 cur_gr = cur_gr.parent_group
1774 cur_gr = cur_gr.parent_group
1763 if gr is None:
1775 if gr is None:
1764 break
1776 break
1765 groups.insert(0, gr)
1777 groups.insert(0, gr)
1766
1778
1767 return groups
1779 return groups
1768
1780
1769 @property
1781 @property
1770 def groups_and_repo(self):
1782 def groups_and_repo(self):
1771 return self.groups_with_parents, self
1783 return self.groups_with_parents, self
1772
1784
1773 @LazyProperty
1785 @LazyProperty
1774 def repo_path(self):
1786 def repo_path(self):
1775 """
1787 """
1776 Returns base full path for that repository means where it actually
1788 Returns base full path for that repository means where it actually
1777 exists on a filesystem
1789 exists on a filesystem
1778 """
1790 """
1779 q = Session().query(RhodeCodeUi).filter(
1791 q = Session().query(RhodeCodeUi).filter(
1780 RhodeCodeUi.ui_key == self.NAME_SEP)
1792 RhodeCodeUi.ui_key == self.NAME_SEP)
1781 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1793 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
1782 return q.one().ui_value
1794 return q.one().ui_value
1783
1795
1784 @property
1796 @property
1785 def repo_full_path(self):
1797 def repo_full_path(self):
1786 p = [self.repo_path]
1798 p = [self.repo_path]
1787 # we need to split the name by / since this is how we store the
1799 # we need to split the name by / since this is how we store the
1788 # names in the database, but that eventually needs to be converted
1800 # names in the database, but that eventually needs to be converted
1789 # into a valid system path
1801 # into a valid system path
1790 p += self.repo_name.split(self.NAME_SEP)
1802 p += self.repo_name.split(self.NAME_SEP)
1791 return os.path.join(*map(safe_unicode, p))
1803 return os.path.join(*map(safe_unicode, p))
1792
1804
1793 @property
1805 @property
1794 def cache_keys(self):
1806 def cache_keys(self):
1795 """
1807 """
1796 Returns associated cache keys for that repo
1808 Returns associated cache keys for that repo
1797 """
1809 """
1798 return CacheKey.query()\
1810 return CacheKey.query()\
1799 .filter(CacheKey.cache_args == self.repo_name)\
1811 .filter(CacheKey.cache_args == self.repo_name)\
1800 .order_by(CacheKey.cache_key)\
1812 .order_by(CacheKey.cache_key)\
1801 .all()
1813 .all()
1802
1814
1803 def get_new_name(self, repo_name):
1815 def get_new_name(self, repo_name):
1804 """
1816 """
1805 returns new full repository name based on assigned group and new new
1817 returns new full repository name based on assigned group and new new
1806
1818
1807 :param group_name:
1819 :param group_name:
1808 """
1820 """
1809 path_prefix = self.group.full_path_splitted if self.group else []
1821 path_prefix = self.group.full_path_splitted if self.group else []
1810 return self.NAME_SEP.join(path_prefix + [repo_name])
1822 return self.NAME_SEP.join(path_prefix + [repo_name])
1811
1823
1812 @property
1824 @property
1813 def _config(self):
1825 def _config(self):
1814 """
1826 """
1815 Returns db based config object.
1827 Returns db based config object.
1816 """
1828 """
1817 from rhodecode.lib.utils import make_db_config
1829 from rhodecode.lib.utils import make_db_config
1818 return make_db_config(clear_session=False, repo=self)
1830 return make_db_config(clear_session=False, repo=self)
1819
1831
1820 def permissions(self, with_admins=True, with_owner=True):
1832 def permissions(self, with_admins=True, with_owner=True):
1821 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
1833 q = UserRepoToPerm.query().filter(UserRepoToPerm.repository == self)
1822 q = q.options(joinedload(UserRepoToPerm.repository),
1834 q = q.options(joinedload(UserRepoToPerm.repository),
1823 joinedload(UserRepoToPerm.user),
1835 joinedload(UserRepoToPerm.user),
1824 joinedload(UserRepoToPerm.permission),)
1836 joinedload(UserRepoToPerm.permission),)
1825
1837
1826 # get owners and admins and permissions. We do a trick of re-writing
1838 # get owners and admins and permissions. We do a trick of re-writing
1827 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1839 # objects from sqlalchemy to named-tuples due to sqlalchemy session
1828 # has a global reference and changing one object propagates to all
1840 # has a global reference and changing one object propagates to all
1829 # others. This means if admin is also an owner admin_row that change
1841 # others. This means if admin is also an owner admin_row that change
1830 # would propagate to both objects
1842 # would propagate to both objects
1831 perm_rows = []
1843 perm_rows = []
1832 for _usr in q.all():
1844 for _usr in q.all():
1833 usr = AttributeDict(_usr.user.get_dict())
1845 usr = AttributeDict(_usr.user.get_dict())
1834 usr.permission = _usr.permission.permission_name
1846 usr.permission = _usr.permission.permission_name
1835 perm_rows.append(usr)
1847 perm_rows.append(usr)
1836
1848
1837 # filter the perm rows by 'default' first and then sort them by
1849 # filter the perm rows by 'default' first and then sort them by
1838 # admin,write,read,none permissions sorted again alphabetically in
1850 # admin,write,read,none permissions sorted again alphabetically in
1839 # each group
1851 # each group
1840 perm_rows = sorted(perm_rows, key=display_sort)
1852 perm_rows = sorted(perm_rows, key=display_user_sort)
1841
1853
1842 _admin_perm = 'repository.admin'
1854 _admin_perm = 'repository.admin'
1843 owner_row = []
1855 owner_row = []
1844 if with_owner:
1856 if with_owner:
1845 usr = AttributeDict(self.user.get_dict())
1857 usr = AttributeDict(self.user.get_dict())
1846 usr.owner_row = True
1858 usr.owner_row = True
1847 usr.permission = _admin_perm
1859 usr.permission = _admin_perm
1848 owner_row.append(usr)
1860 owner_row.append(usr)
1849
1861
1850 super_admin_rows = []
1862 super_admin_rows = []
1851 if with_admins:
1863 if with_admins:
1852 for usr in User.get_all_super_admins():
1864 for usr in User.get_all_super_admins():
1853 # if this admin is also owner, don't double the record
1865 # if this admin is also owner, don't double the record
1854 if usr.user_id == owner_row[0].user_id:
1866 if usr.user_id == owner_row[0].user_id:
1855 owner_row[0].admin_row = True
1867 owner_row[0].admin_row = True
1856 else:
1868 else:
1857 usr = AttributeDict(usr.get_dict())
1869 usr = AttributeDict(usr.get_dict())
1858 usr.admin_row = True
1870 usr.admin_row = True
1859 usr.permission = _admin_perm
1871 usr.permission = _admin_perm
1860 super_admin_rows.append(usr)
1872 super_admin_rows.append(usr)
1861
1873
1862 return super_admin_rows + owner_row + perm_rows
1874 return super_admin_rows + owner_row + perm_rows
1863
1875
1864 def permission_user_groups(self):
1876 def permission_user_groups(self):
1865 q = UserGroupRepoToPerm.query().filter(
1877 q = UserGroupRepoToPerm.query().filter(
1866 UserGroupRepoToPerm.repository == self)
1878 UserGroupRepoToPerm.repository == self)
1867 q = q.options(joinedload(UserGroupRepoToPerm.repository),
1879 q = q.options(joinedload(UserGroupRepoToPerm.repository),
1868 joinedload(UserGroupRepoToPerm.users_group),
1880 joinedload(UserGroupRepoToPerm.users_group),
1869 joinedload(UserGroupRepoToPerm.permission),)
1881 joinedload(UserGroupRepoToPerm.permission),)
1870
1882
1871 perm_rows = []
1883 perm_rows = []
1872 for _user_group in q.all():
1884 for _user_group in q.all():
1873 usr = AttributeDict(_user_group.users_group.get_dict())
1885 usr = AttributeDict(_user_group.users_group.get_dict())
1874 usr.permission = _user_group.permission.permission_name
1886 usr.permission = _user_group.permission.permission_name
1875 perm_rows.append(usr)
1887 perm_rows.append(usr)
1876
1888
1889 perm_rows = sorted(perm_rows, key=display_user_group_sort)
1877 return perm_rows
1890 return perm_rows
1878
1891
1879 def get_api_data(self, include_secrets=False):
1892 def get_api_data(self, include_secrets=False):
1880 """
1893 """
1881 Common function for generating repo api data
1894 Common function for generating repo api data
1882
1895
1883 :param include_secrets: See :meth:`User.get_api_data`.
1896 :param include_secrets: See :meth:`User.get_api_data`.
1884
1897
1885 """
1898 """
1886 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1899 # TODO: mikhail: Here there is an anti-pattern, we probably need to
1887 # move this methods on models level.
1900 # move this methods on models level.
1888 from rhodecode.model.settings import SettingsModel
1901 from rhodecode.model.settings import SettingsModel
1889 from rhodecode.model.repo import RepoModel
1902 from rhodecode.model.repo import RepoModel
1890
1903
1891 repo = self
1904 repo = self
1892 _user_id, _time, _reason = self.locked
1905 _user_id, _time, _reason = self.locked
1893
1906
1894 data = {
1907 data = {
1895 'repo_id': repo.repo_id,
1908 'repo_id': repo.repo_id,
1896 'repo_name': repo.repo_name,
1909 'repo_name': repo.repo_name,
1897 'repo_type': repo.repo_type,
1910 'repo_type': repo.repo_type,
1898 'clone_uri': repo.clone_uri or '',
1911 'clone_uri': repo.clone_uri or '',
1899 'url': RepoModel().get_url(self),
1912 'url': RepoModel().get_url(self),
1900 'private': repo.private,
1913 'private': repo.private,
1901 'created_on': repo.created_on,
1914 'created_on': repo.created_on,
1902 'description': repo.description_safe,
1915 'description': repo.description_safe,
1903 'landing_rev': repo.landing_rev,
1916 'landing_rev': repo.landing_rev,
1904 'owner': repo.user.username,
1917 'owner': repo.user.username,
1905 'fork_of': repo.fork.repo_name if repo.fork else None,
1918 'fork_of': repo.fork.repo_name if repo.fork else None,
1906 'fork_of_id': repo.fork.repo_id if repo.fork else None,
1919 'fork_of_id': repo.fork.repo_id if repo.fork else None,
1907 'enable_statistics': repo.enable_statistics,
1920 'enable_statistics': repo.enable_statistics,
1908 'enable_locking': repo.enable_locking,
1921 'enable_locking': repo.enable_locking,
1909 'enable_downloads': repo.enable_downloads,
1922 'enable_downloads': repo.enable_downloads,
1910 'last_changeset': repo.changeset_cache,
1923 'last_changeset': repo.changeset_cache,
1911 'locked_by': User.get(_user_id).get_api_data(
1924 'locked_by': User.get(_user_id).get_api_data(
1912 include_secrets=include_secrets) if _user_id else None,
1925 include_secrets=include_secrets) if _user_id else None,
1913 'locked_date': time_to_datetime(_time) if _time else None,
1926 'locked_date': time_to_datetime(_time) if _time else None,
1914 'lock_reason': _reason if _reason else None,
1927 'lock_reason': _reason if _reason else None,
1915 }
1928 }
1916
1929
1917 # TODO: mikhail: should be per-repo settings here
1930 # TODO: mikhail: should be per-repo settings here
1918 rc_config = SettingsModel().get_all_settings()
1931 rc_config = SettingsModel().get_all_settings()
1919 repository_fields = str2bool(
1932 repository_fields = str2bool(
1920 rc_config.get('rhodecode_repository_fields'))
1933 rc_config.get('rhodecode_repository_fields'))
1921 if repository_fields:
1934 if repository_fields:
1922 for f in self.extra_fields:
1935 for f in self.extra_fields:
1923 data[f.field_key_prefixed] = f.field_value
1936 data[f.field_key_prefixed] = f.field_value
1924
1937
1925 return data
1938 return data
1926
1939
1927 @classmethod
1940 @classmethod
1928 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
1941 def lock(cls, repo, user_id, lock_time=None, lock_reason=None):
1929 if not lock_time:
1942 if not lock_time:
1930 lock_time = time.time()
1943 lock_time = time.time()
1931 if not lock_reason:
1944 if not lock_reason:
1932 lock_reason = cls.LOCK_AUTOMATIC
1945 lock_reason = cls.LOCK_AUTOMATIC
1933 repo.locked = [user_id, lock_time, lock_reason]
1946 repo.locked = [user_id, lock_time, lock_reason]
1934 Session().add(repo)
1947 Session().add(repo)
1935 Session().commit()
1948 Session().commit()
1936
1949
1937 @classmethod
1950 @classmethod
1938 def unlock(cls, repo):
1951 def unlock(cls, repo):
1939 repo.locked = None
1952 repo.locked = None
1940 Session().add(repo)
1953 Session().add(repo)
1941 Session().commit()
1954 Session().commit()
1942
1955
1943 @classmethod
1956 @classmethod
1944 def getlock(cls, repo):
1957 def getlock(cls, repo):
1945 return repo.locked
1958 return repo.locked
1946
1959
1947 def is_user_lock(self, user_id):
1960 def is_user_lock(self, user_id):
1948 if self.lock[0]:
1961 if self.lock[0]:
1949 lock_user_id = safe_int(self.lock[0])
1962 lock_user_id = safe_int(self.lock[0])
1950 user_id = safe_int(user_id)
1963 user_id = safe_int(user_id)
1951 # both are ints, and they are equal
1964 # both are ints, and they are equal
1952 return all([lock_user_id, user_id]) and lock_user_id == user_id
1965 return all([lock_user_id, user_id]) and lock_user_id == user_id
1953
1966
1954 return False
1967 return False
1955
1968
1956 def get_locking_state(self, action, user_id, only_when_enabled=True):
1969 def get_locking_state(self, action, user_id, only_when_enabled=True):
1957 """
1970 """
1958 Checks locking on this repository, if locking is enabled and lock is
1971 Checks locking on this repository, if locking is enabled and lock is
1959 present returns a tuple of make_lock, locked, locked_by.
1972 present returns a tuple of make_lock, locked, locked_by.
1960 make_lock can have 3 states None (do nothing) True, make lock
1973 make_lock can have 3 states None (do nothing) True, make lock
1961 False release lock, This value is later propagated to hooks, which
1974 False release lock, This value is later propagated to hooks, which
1962 do the locking. Think about this as signals passed to hooks what to do.
1975 do the locking. Think about this as signals passed to hooks what to do.
1963
1976
1964 """
1977 """
1965 # TODO: johbo: This is part of the business logic and should be moved
1978 # TODO: johbo: This is part of the business logic and should be moved
1966 # into the RepositoryModel.
1979 # into the RepositoryModel.
1967
1980
1968 if action not in ('push', 'pull'):
1981 if action not in ('push', 'pull'):
1969 raise ValueError("Invalid action value: %s" % repr(action))
1982 raise ValueError("Invalid action value: %s" % repr(action))
1970
1983
1971 # defines if locked error should be thrown to user
1984 # defines if locked error should be thrown to user
1972 currently_locked = False
1985 currently_locked = False
1973 # defines if new lock should be made, tri-state
1986 # defines if new lock should be made, tri-state
1974 make_lock = None
1987 make_lock = None
1975 repo = self
1988 repo = self
1976 user = User.get(user_id)
1989 user = User.get(user_id)
1977
1990
1978 lock_info = repo.locked
1991 lock_info = repo.locked
1979
1992
1980 if repo and (repo.enable_locking or not only_when_enabled):
1993 if repo and (repo.enable_locking or not only_when_enabled):
1981 if action == 'push':
1994 if action == 'push':
1982 # check if it's already locked !, if it is compare users
1995 # check if it's already locked !, if it is compare users
1983 locked_by_user_id = lock_info[0]
1996 locked_by_user_id = lock_info[0]
1984 if user.user_id == locked_by_user_id:
1997 if user.user_id == locked_by_user_id:
1985 log.debug(
1998 log.debug(
1986 'Got `push` action from user %s, now unlocking', user)
1999 'Got `push` action from user %s, now unlocking', user)
1987 # unlock if we have push from user who locked
2000 # unlock if we have push from user who locked
1988 make_lock = False
2001 make_lock = False
1989 else:
2002 else:
1990 # we're not the same user who locked, ban with
2003 # we're not the same user who locked, ban with
1991 # code defined in settings (default is 423 HTTP Locked) !
2004 # code defined in settings (default is 423 HTTP Locked) !
1992 log.debug('Repo %s is currently locked by %s', repo, user)
2005 log.debug('Repo %s is currently locked by %s', repo, user)
1993 currently_locked = True
2006 currently_locked = True
1994 elif action == 'pull':
2007 elif action == 'pull':
1995 # [0] user [1] date
2008 # [0] user [1] date
1996 if lock_info[0] and lock_info[1]:
2009 if lock_info[0] and lock_info[1]:
1997 log.debug('Repo %s is currently locked by %s', repo, user)
2010 log.debug('Repo %s is currently locked by %s', repo, user)
1998 currently_locked = True
2011 currently_locked = True
1999 else:
2012 else:
2000 log.debug('Setting lock on repo %s by %s', repo, user)
2013 log.debug('Setting lock on repo %s by %s', repo, user)
2001 make_lock = True
2014 make_lock = True
2002
2015
2003 else:
2016 else:
2004 log.debug('Repository %s do not have locking enabled', repo)
2017 log.debug('Repository %s do not have locking enabled', repo)
2005
2018
2006 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
2019 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s',
2007 make_lock, currently_locked, lock_info)
2020 make_lock, currently_locked, lock_info)
2008
2021
2009 from rhodecode.lib.auth import HasRepoPermissionAny
2022 from rhodecode.lib.auth import HasRepoPermissionAny
2010 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
2023 perm_check = HasRepoPermissionAny('repository.write', 'repository.admin')
2011 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
2024 if make_lock and not perm_check(repo_name=repo.repo_name, user=user):
2012 # if we don't have at least write permission we cannot make a lock
2025 # if we don't have at least write permission we cannot make a lock
2013 log.debug('lock state reset back to FALSE due to lack '
2026 log.debug('lock state reset back to FALSE due to lack '
2014 'of at least read permission')
2027 'of at least read permission')
2015 make_lock = False
2028 make_lock = False
2016
2029
2017 return make_lock, currently_locked, lock_info
2030 return make_lock, currently_locked, lock_info
2018
2031
2019 @property
2032 @property
2020 def last_db_change(self):
2033 def last_db_change(self):
2021 return self.updated_on
2034 return self.updated_on
2022
2035
2023 @property
2036 @property
2024 def clone_uri_hidden(self):
2037 def clone_uri_hidden(self):
2025 clone_uri = self.clone_uri
2038 clone_uri = self.clone_uri
2026 if clone_uri:
2039 if clone_uri:
2027 import urlobject
2040 import urlobject
2028 url_obj = urlobject.URLObject(cleaned_uri(clone_uri))
2041 url_obj = urlobject.URLObject(cleaned_uri(clone_uri))
2029 if url_obj.password:
2042 if url_obj.password:
2030 clone_uri = url_obj.with_password('*****')
2043 clone_uri = url_obj.with_password('*****')
2031 return clone_uri
2044 return clone_uri
2032
2045
2033 def clone_url(self, **override):
2046 def clone_url(self, **override):
2034 from rhodecode.model.settings import SettingsModel
2047 from rhodecode.model.settings import SettingsModel
2035
2048
2036 uri_tmpl = None
2049 uri_tmpl = None
2037 if 'with_id' in override:
2050 if 'with_id' in override:
2038 uri_tmpl = self.DEFAULT_CLONE_URI_ID
2051 uri_tmpl = self.DEFAULT_CLONE_URI_ID
2039 del override['with_id']
2052 del override['with_id']
2040
2053
2041 if 'uri_tmpl' in override:
2054 if 'uri_tmpl' in override:
2042 uri_tmpl = override['uri_tmpl']
2055 uri_tmpl = override['uri_tmpl']
2043 del override['uri_tmpl']
2056 del override['uri_tmpl']
2044
2057
2045 # we didn't override our tmpl from **overrides
2058 # we didn't override our tmpl from **overrides
2046 if not uri_tmpl:
2059 if not uri_tmpl:
2047 rc_config = SettingsModel().get_all_settings(cache=True)
2060 rc_config = SettingsModel().get_all_settings(cache=True)
2048 uri_tmpl = rc_config.get(
2061 uri_tmpl = rc_config.get(
2049 'rhodecode_clone_uri_tmpl') or self.DEFAULT_CLONE_URI
2062 'rhodecode_clone_uri_tmpl') or self.DEFAULT_CLONE_URI
2050
2063
2051 request = get_current_request()
2064 request = get_current_request()
2052 return get_clone_url(request=request,
2065 return get_clone_url(request=request,
2053 uri_tmpl=uri_tmpl,
2066 uri_tmpl=uri_tmpl,
2054 repo_name=self.repo_name,
2067 repo_name=self.repo_name,
2055 repo_id=self.repo_id, **override)
2068 repo_id=self.repo_id, **override)
2056
2069
2057 def set_state(self, state):
2070 def set_state(self, state):
2058 self.repo_state = state
2071 self.repo_state = state
2059 Session().add(self)
2072 Session().add(self)
2060 #==========================================================================
2073 #==========================================================================
2061 # SCM PROPERTIES
2074 # SCM PROPERTIES
2062 #==========================================================================
2075 #==========================================================================
2063
2076
2064 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
2077 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
2065 return get_commit_safe(
2078 return get_commit_safe(
2066 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
2079 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
2067
2080
2068 def get_changeset(self, rev=None, pre_load=None):
2081 def get_changeset(self, rev=None, pre_load=None):
2069 warnings.warn("Use get_commit", DeprecationWarning)
2082 warnings.warn("Use get_commit", DeprecationWarning)
2070 commit_id = None
2083 commit_id = None
2071 commit_idx = None
2084 commit_idx = None
2072 if isinstance(rev, basestring):
2085 if isinstance(rev, basestring):
2073 commit_id = rev
2086 commit_id = rev
2074 else:
2087 else:
2075 commit_idx = rev
2088 commit_idx = rev
2076 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
2089 return self.get_commit(commit_id=commit_id, commit_idx=commit_idx,
2077 pre_load=pre_load)
2090 pre_load=pre_load)
2078
2091
2079 def get_landing_commit(self):
2092 def get_landing_commit(self):
2080 """
2093 """
2081 Returns landing commit, or if that doesn't exist returns the tip
2094 Returns landing commit, or if that doesn't exist returns the tip
2082 """
2095 """
2083 _rev_type, _rev = self.landing_rev
2096 _rev_type, _rev = self.landing_rev
2084 commit = self.get_commit(_rev)
2097 commit = self.get_commit(_rev)
2085 if isinstance(commit, EmptyCommit):
2098 if isinstance(commit, EmptyCommit):
2086 return self.get_commit()
2099 return self.get_commit()
2087 return commit
2100 return commit
2088
2101
2089 def update_commit_cache(self, cs_cache=None, config=None):
2102 def update_commit_cache(self, cs_cache=None, config=None):
2090 """
2103 """
2091 Update cache of last changeset for repository, keys should be::
2104 Update cache of last changeset for repository, keys should be::
2092
2105
2093 short_id
2106 short_id
2094 raw_id
2107 raw_id
2095 revision
2108 revision
2096 parents
2109 parents
2097 message
2110 message
2098 date
2111 date
2099 author
2112 author
2100
2113
2101 :param cs_cache:
2114 :param cs_cache:
2102 """
2115 """
2103 from rhodecode.lib.vcs.backends.base import BaseChangeset
2116 from rhodecode.lib.vcs.backends.base import BaseChangeset
2104 if cs_cache is None:
2117 if cs_cache is None:
2105 # use no-cache version here
2118 # use no-cache version here
2106 scm_repo = self.scm_instance(cache=False, config=config)
2119 scm_repo = self.scm_instance(cache=False, config=config)
2107 if scm_repo:
2120 if scm_repo:
2108 cs_cache = scm_repo.get_commit(
2121 cs_cache = scm_repo.get_commit(
2109 pre_load=["author", "date", "message", "parents"])
2122 pre_load=["author", "date", "message", "parents"])
2110 else:
2123 else:
2111 cs_cache = EmptyCommit()
2124 cs_cache = EmptyCommit()
2112
2125
2113 if isinstance(cs_cache, BaseChangeset):
2126 if isinstance(cs_cache, BaseChangeset):
2114 cs_cache = cs_cache.__json__()
2127 cs_cache = cs_cache.__json__()
2115
2128
2116 def is_outdated(new_cs_cache):
2129 def is_outdated(new_cs_cache):
2117 if (new_cs_cache['raw_id'] != self.changeset_cache['raw_id'] or
2130 if (new_cs_cache['raw_id'] != self.changeset_cache['raw_id'] or
2118 new_cs_cache['revision'] != self.changeset_cache['revision']):
2131 new_cs_cache['revision'] != self.changeset_cache['revision']):
2119 return True
2132 return True
2120 return False
2133 return False
2121
2134
2122 # check if we have maybe already latest cached revision
2135 # check if we have maybe already latest cached revision
2123 if is_outdated(cs_cache) or not self.changeset_cache:
2136 if is_outdated(cs_cache) or not self.changeset_cache:
2124 _default = datetime.datetime.fromtimestamp(0)
2137 _default = datetime.datetime.fromtimestamp(0)
2125 last_change = cs_cache.get('date') or _default
2138 last_change = cs_cache.get('date') or _default
2126 log.debug('updated repo %s with new cs cache %s',
2139 log.debug('updated repo %s with new cs cache %s',
2127 self.repo_name, cs_cache)
2140 self.repo_name, cs_cache)
2128 self.updated_on = last_change
2141 self.updated_on = last_change
2129 self.changeset_cache = cs_cache
2142 self.changeset_cache = cs_cache
2130 Session().add(self)
2143 Session().add(self)
2131 Session().commit()
2144 Session().commit()
2132 else:
2145 else:
2133 log.debug('Skipping update_commit_cache for repo:`%s` '
2146 log.debug('Skipping update_commit_cache for repo:`%s` '
2134 'commit already with latest changes', self.repo_name)
2147 'commit already with latest changes', self.repo_name)
2135
2148
2136 @property
2149 @property
2137 def tip(self):
2150 def tip(self):
2138 return self.get_commit('tip')
2151 return self.get_commit('tip')
2139
2152
2140 @property
2153 @property
2141 def author(self):
2154 def author(self):
2142 return self.tip.author
2155 return self.tip.author
2143
2156
2144 @property
2157 @property
2145 def last_change(self):
2158 def last_change(self):
2146 return self.scm_instance().last_change
2159 return self.scm_instance().last_change
2147
2160
2148 def get_comments(self, revisions=None):
2161 def get_comments(self, revisions=None):
2149 """
2162 """
2150 Returns comments for this repository grouped by revisions
2163 Returns comments for this repository grouped by revisions
2151
2164
2152 :param revisions: filter query by revisions only
2165 :param revisions: filter query by revisions only
2153 """
2166 """
2154 cmts = ChangesetComment.query()\
2167 cmts = ChangesetComment.query()\
2155 .filter(ChangesetComment.repo == self)
2168 .filter(ChangesetComment.repo == self)
2156 if revisions:
2169 if revisions:
2157 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
2170 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
2158 grouped = collections.defaultdict(list)
2171 grouped = collections.defaultdict(list)
2159 for cmt in cmts.all():
2172 for cmt in cmts.all():
2160 grouped[cmt.revision].append(cmt)
2173 grouped[cmt.revision].append(cmt)
2161 return grouped
2174 return grouped
2162
2175
2163 def statuses(self, revisions=None):
2176 def statuses(self, revisions=None):
2164 """
2177 """
2165 Returns statuses for this repository
2178 Returns statuses for this repository
2166
2179
2167 :param revisions: list of revisions to get statuses for
2180 :param revisions: list of revisions to get statuses for
2168 """
2181 """
2169 statuses = ChangesetStatus.query()\
2182 statuses = ChangesetStatus.query()\
2170 .filter(ChangesetStatus.repo == self)\
2183 .filter(ChangesetStatus.repo == self)\
2171 .filter(ChangesetStatus.version == 0)
2184 .filter(ChangesetStatus.version == 0)
2172
2185
2173 if revisions:
2186 if revisions:
2174 # Try doing the filtering in chunks to avoid hitting limits
2187 # Try doing the filtering in chunks to avoid hitting limits
2175 size = 500
2188 size = 500
2176 status_results = []
2189 status_results = []
2177 for chunk in xrange(0, len(revisions), size):
2190 for chunk in xrange(0, len(revisions), size):
2178 status_results += statuses.filter(
2191 status_results += statuses.filter(
2179 ChangesetStatus.revision.in_(
2192 ChangesetStatus.revision.in_(
2180 revisions[chunk: chunk+size])
2193 revisions[chunk: chunk+size])
2181 ).all()
2194 ).all()
2182 else:
2195 else:
2183 status_results = statuses.all()
2196 status_results = statuses.all()
2184
2197
2185 grouped = {}
2198 grouped = {}
2186
2199
2187 # maybe we have open new pullrequest without a status?
2200 # maybe we have open new pullrequest without a status?
2188 stat = ChangesetStatus.STATUS_UNDER_REVIEW
2201 stat = ChangesetStatus.STATUS_UNDER_REVIEW
2189 status_lbl = ChangesetStatus.get_status_lbl(stat)
2202 status_lbl = ChangesetStatus.get_status_lbl(stat)
2190 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
2203 for pr in PullRequest.query().filter(PullRequest.source_repo == self).all():
2191 for rev in pr.revisions:
2204 for rev in pr.revisions:
2192 pr_id = pr.pull_request_id
2205 pr_id = pr.pull_request_id
2193 pr_repo = pr.target_repo.repo_name
2206 pr_repo = pr.target_repo.repo_name
2194 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
2207 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
2195
2208
2196 for stat in status_results:
2209 for stat in status_results:
2197 pr_id = pr_repo = None
2210 pr_id = pr_repo = None
2198 if stat.pull_request:
2211 if stat.pull_request:
2199 pr_id = stat.pull_request.pull_request_id
2212 pr_id = stat.pull_request.pull_request_id
2200 pr_repo = stat.pull_request.target_repo.repo_name
2213 pr_repo = stat.pull_request.target_repo.repo_name
2201 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
2214 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
2202 pr_id, pr_repo]
2215 pr_id, pr_repo]
2203 return grouped
2216 return grouped
2204
2217
2205 # ==========================================================================
2218 # ==========================================================================
2206 # SCM CACHE INSTANCE
2219 # SCM CACHE INSTANCE
2207 # ==========================================================================
2220 # ==========================================================================
2208
2221
2209 def scm_instance(self, **kwargs):
2222 def scm_instance(self, **kwargs):
2210 import rhodecode
2223 import rhodecode
2211
2224
2212 # Passing a config will not hit the cache currently only used
2225 # Passing a config will not hit the cache currently only used
2213 # for repo2dbmapper
2226 # for repo2dbmapper
2214 config = kwargs.pop('config', None)
2227 config = kwargs.pop('config', None)
2215 cache = kwargs.pop('cache', None)
2228 cache = kwargs.pop('cache', None)
2216 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
2229 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
2217 # if cache is NOT defined use default global, else we have a full
2230 # if cache is NOT defined use default global, else we have a full
2218 # control over cache behaviour
2231 # control over cache behaviour
2219 if cache is None and full_cache and not config:
2232 if cache is None and full_cache and not config:
2220 return self._get_instance_cached()
2233 return self._get_instance_cached()
2221 return self._get_instance(cache=bool(cache), config=config)
2234 return self._get_instance(cache=bool(cache), config=config)
2222
2235
2223 def _get_instance_cached(self):
2236 def _get_instance_cached(self):
2224 @cache_region('long_term')
2237 @cache_region('long_term')
2225 def _get_repo(cache_key):
2238 def _get_repo(cache_key):
2226 return self._get_instance()
2239 return self._get_instance()
2227
2240
2228 invalidator_context = CacheKey.repo_context_cache(
2241 invalidator_context = CacheKey.repo_context_cache(
2229 _get_repo, self.repo_name, None, thread_scoped=True)
2242 _get_repo, self.repo_name, None, thread_scoped=True)
2230
2243
2231 with invalidator_context as context:
2244 with invalidator_context as context:
2232 context.invalidate()
2245 context.invalidate()
2233 repo = context.compute()
2246 repo = context.compute()
2234
2247
2235 return repo
2248 return repo
2236
2249
2237 def _get_instance(self, cache=True, config=None):
2250 def _get_instance(self, cache=True, config=None):
2238 config = config or self._config
2251 config = config or self._config
2239 custom_wire = {
2252 custom_wire = {
2240 'cache': cache # controls the vcs.remote cache
2253 'cache': cache # controls the vcs.remote cache
2241 }
2254 }
2242 repo = get_vcs_instance(
2255 repo = get_vcs_instance(
2243 repo_path=safe_str(self.repo_full_path),
2256 repo_path=safe_str(self.repo_full_path),
2244 config=config,
2257 config=config,
2245 with_wire=custom_wire,
2258 with_wire=custom_wire,
2246 create=False,
2259 create=False,
2247 _vcs_alias=self.repo_type)
2260 _vcs_alias=self.repo_type)
2248
2261
2249 return repo
2262 return repo
2250
2263
2251 def __json__(self):
2264 def __json__(self):
2252 return {'landing_rev': self.landing_rev}
2265 return {'landing_rev': self.landing_rev}
2253
2266
2254 def get_dict(self):
2267 def get_dict(self):
2255
2268
2256 # Since we transformed `repo_name` to a hybrid property, we need to
2269 # Since we transformed `repo_name` to a hybrid property, we need to
2257 # keep compatibility with the code which uses `repo_name` field.
2270 # keep compatibility with the code which uses `repo_name` field.
2258
2271
2259 result = super(Repository, self).get_dict()
2272 result = super(Repository, self).get_dict()
2260 result['repo_name'] = result.pop('_repo_name', None)
2273 result['repo_name'] = result.pop('_repo_name', None)
2261 return result
2274 return result
2262
2275
2263
2276
2264 class RepoGroup(Base, BaseModel):
2277 class RepoGroup(Base, BaseModel):
2265 __tablename__ = 'groups'
2278 __tablename__ = 'groups'
2266 __table_args__ = (
2279 __table_args__ = (
2267 UniqueConstraint('group_name', 'group_parent_id'),
2280 UniqueConstraint('group_name', 'group_parent_id'),
2268 CheckConstraint('group_id != group_parent_id'),
2281 CheckConstraint('group_id != group_parent_id'),
2269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2282 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2270 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2283 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2271 )
2284 )
2272 __mapper_args__ = {'order_by': 'group_name'}
2285 __mapper_args__ = {'order_by': 'group_name'}
2273
2286
2274 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
2287 CHOICES_SEPARATOR = '/' # used to generate select2 choices for nested groups
2275
2288
2276 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2289 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2277 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
2290 group_name = Column("group_name", String(255), nullable=False, unique=True, default=None)
2278 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
2291 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
2279 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
2292 group_description = Column("group_description", String(10000), nullable=True, unique=None, default=None)
2280 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
2293 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
2281 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2294 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
2282 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2295 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2283 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2296 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
2284 personal = Column('personal', Boolean(), nullable=True, unique=None, default=None)
2297 personal = Column('personal', Boolean(), nullable=True, unique=None, default=None)
2285
2298
2286 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2299 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
2287 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2300 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
2288 parent_group = relationship('RepoGroup', remote_side=group_id)
2301 parent_group = relationship('RepoGroup', remote_side=group_id)
2289 user = relationship('User')
2302 user = relationship('User')
2290 integrations = relationship('Integration',
2303 integrations = relationship('Integration',
2291 cascade="all, delete, delete-orphan")
2304 cascade="all, delete, delete-orphan")
2292
2305
2293 def __init__(self, group_name='', parent_group=None):
2306 def __init__(self, group_name='', parent_group=None):
2294 self.group_name = group_name
2307 self.group_name = group_name
2295 self.parent_group = parent_group
2308 self.parent_group = parent_group
2296
2309
2297 def __unicode__(self):
2310 def __unicode__(self):
2298 return u"<%s('id:%s:%s')>" % (
2311 return u"<%s('id:%s:%s')>" % (
2299 self.__class__.__name__, self.group_id, self.group_name)
2312 self.__class__.__name__, self.group_id, self.group_name)
2300
2313
2301 @hybrid_property
2314 @hybrid_property
2302 def description_safe(self):
2315 def description_safe(self):
2303 from rhodecode.lib import helpers as h
2316 from rhodecode.lib import helpers as h
2304 return h.escape(self.group_description)
2317 return h.escape(self.group_description)
2305
2318
2306 @classmethod
2319 @classmethod
2307 def _generate_choice(cls, repo_group):
2320 def _generate_choice(cls, repo_group):
2308 from webhelpers.html import literal as _literal
2321 from webhelpers.html import literal as _literal
2309 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2322 _name = lambda k: _literal(cls.CHOICES_SEPARATOR.join(k))
2310 return repo_group.group_id, _name(repo_group.full_path_splitted)
2323 return repo_group.group_id, _name(repo_group.full_path_splitted)
2311
2324
2312 @classmethod
2325 @classmethod
2313 def groups_choices(cls, groups=None, show_empty_group=True):
2326 def groups_choices(cls, groups=None, show_empty_group=True):
2314 if not groups:
2327 if not groups:
2315 groups = cls.query().all()
2328 groups = cls.query().all()
2316
2329
2317 repo_groups = []
2330 repo_groups = []
2318 if show_empty_group:
2331 if show_empty_group:
2319 repo_groups = [(-1, u'-- %s --' % _('No parent'))]
2332 repo_groups = [(-1, u'-- %s --' % _('No parent'))]
2320
2333
2321 repo_groups.extend([cls._generate_choice(x) for x in groups])
2334 repo_groups.extend([cls._generate_choice(x) for x in groups])
2322
2335
2323 repo_groups = sorted(
2336 repo_groups = sorted(
2324 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2337 repo_groups, key=lambda t: t[1].split(cls.CHOICES_SEPARATOR)[0])
2325 return repo_groups
2338 return repo_groups
2326
2339
2327 @classmethod
2340 @classmethod
2328 def url_sep(cls):
2341 def url_sep(cls):
2329 return URL_SEP
2342 return URL_SEP
2330
2343
2331 @classmethod
2344 @classmethod
2332 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2345 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
2333 if case_insensitive:
2346 if case_insensitive:
2334 gr = cls.query().filter(func.lower(cls.group_name)
2347 gr = cls.query().filter(func.lower(cls.group_name)
2335 == func.lower(group_name))
2348 == func.lower(group_name))
2336 else:
2349 else:
2337 gr = cls.query().filter(cls.group_name == group_name)
2350 gr = cls.query().filter(cls.group_name == group_name)
2338 if cache:
2351 if cache:
2339 name_key = _hash_key(group_name)
2352 name_key = _hash_key(group_name)
2340 gr = gr.options(
2353 gr = gr.options(
2341 FromCache("sql_cache_short", "get_group_%s" % name_key))
2354 FromCache("sql_cache_short", "get_group_%s" % name_key))
2342 return gr.scalar()
2355 return gr.scalar()
2343
2356
2344 @classmethod
2357 @classmethod
2345 def get_user_personal_repo_group(cls, user_id):
2358 def get_user_personal_repo_group(cls, user_id):
2346 user = User.get(user_id)
2359 user = User.get(user_id)
2347 if user.username == User.DEFAULT_USER:
2360 if user.username == User.DEFAULT_USER:
2348 return None
2361 return None
2349
2362
2350 return cls.query()\
2363 return cls.query()\
2351 .filter(cls.personal == true()) \
2364 .filter(cls.personal == true()) \
2352 .filter(cls.user == user).scalar()
2365 .filter(cls.user == user).scalar()
2353
2366
2354 @classmethod
2367 @classmethod
2355 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2368 def get_all_repo_groups(cls, user_id=Optional(None), group_id=Optional(None),
2356 case_insensitive=True):
2369 case_insensitive=True):
2357 q = RepoGroup.query()
2370 q = RepoGroup.query()
2358
2371
2359 if not isinstance(user_id, Optional):
2372 if not isinstance(user_id, Optional):
2360 q = q.filter(RepoGroup.user_id == user_id)
2373 q = q.filter(RepoGroup.user_id == user_id)
2361
2374
2362 if not isinstance(group_id, Optional):
2375 if not isinstance(group_id, Optional):
2363 q = q.filter(RepoGroup.group_parent_id == group_id)
2376 q = q.filter(RepoGroup.group_parent_id == group_id)
2364
2377
2365 if case_insensitive:
2378 if case_insensitive:
2366 q = q.order_by(func.lower(RepoGroup.group_name))
2379 q = q.order_by(func.lower(RepoGroup.group_name))
2367 else:
2380 else:
2368 q = q.order_by(RepoGroup.group_name)
2381 q = q.order_by(RepoGroup.group_name)
2369 return q.all()
2382 return q.all()
2370
2383
2371 @property
2384 @property
2372 def parents(self):
2385 def parents(self):
2373 parents_recursion_limit = 10
2386 parents_recursion_limit = 10
2374 groups = []
2387 groups = []
2375 if self.parent_group is None:
2388 if self.parent_group is None:
2376 return groups
2389 return groups
2377 cur_gr = self.parent_group
2390 cur_gr = self.parent_group
2378 groups.insert(0, cur_gr)
2391 groups.insert(0, cur_gr)
2379 cnt = 0
2392 cnt = 0
2380 while 1:
2393 while 1:
2381 cnt += 1
2394 cnt += 1
2382 gr = getattr(cur_gr, 'parent_group', None)
2395 gr = getattr(cur_gr, 'parent_group', None)
2383 cur_gr = cur_gr.parent_group
2396 cur_gr = cur_gr.parent_group
2384 if gr is None:
2397 if gr is None:
2385 break
2398 break
2386 if cnt == parents_recursion_limit:
2399 if cnt == parents_recursion_limit:
2387 # this will prevent accidental infinit loops
2400 # this will prevent accidental infinit loops
2388 log.error(('more than %s parents found for group %s, stopping '
2401 log.error(('more than %s parents found for group %s, stopping '
2389 'recursive parent fetching' % (parents_recursion_limit, self)))
2402 'recursive parent fetching' % (parents_recursion_limit, self)))
2390 break
2403 break
2391
2404
2392 groups.insert(0, gr)
2405 groups.insert(0, gr)
2393 return groups
2406 return groups
2394
2407
2395 @property
2408 @property
2396 def last_db_change(self):
2409 def last_db_change(self):
2397 return self.updated_on
2410 return self.updated_on
2398
2411
2399 @property
2412 @property
2400 def children(self):
2413 def children(self):
2401 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2414 return RepoGroup.query().filter(RepoGroup.parent_group == self)
2402
2415
2403 @property
2416 @property
2404 def name(self):
2417 def name(self):
2405 return self.group_name.split(RepoGroup.url_sep())[-1]
2418 return self.group_name.split(RepoGroup.url_sep())[-1]
2406
2419
2407 @property
2420 @property
2408 def full_path(self):
2421 def full_path(self):
2409 return self.group_name
2422 return self.group_name
2410
2423
2411 @property
2424 @property
2412 def full_path_splitted(self):
2425 def full_path_splitted(self):
2413 return self.group_name.split(RepoGroup.url_sep())
2426 return self.group_name.split(RepoGroup.url_sep())
2414
2427
2415 @property
2428 @property
2416 def repositories(self):
2429 def repositories(self):
2417 return Repository.query()\
2430 return Repository.query()\
2418 .filter(Repository.group == self)\
2431 .filter(Repository.group == self)\
2419 .order_by(Repository.repo_name)
2432 .order_by(Repository.repo_name)
2420
2433
2421 @property
2434 @property
2422 def repositories_recursive_count(self):
2435 def repositories_recursive_count(self):
2423 cnt = self.repositories.count()
2436 cnt = self.repositories.count()
2424
2437
2425 def children_count(group):
2438 def children_count(group):
2426 cnt = 0
2439 cnt = 0
2427 for child in group.children:
2440 for child in group.children:
2428 cnt += child.repositories.count()
2441 cnt += child.repositories.count()
2429 cnt += children_count(child)
2442 cnt += children_count(child)
2430 return cnt
2443 return cnt
2431
2444
2432 return cnt + children_count(self)
2445 return cnt + children_count(self)
2433
2446
2434 def _recursive_objects(self, include_repos=True):
2447 def _recursive_objects(self, include_repos=True):
2435 all_ = []
2448 all_ = []
2436
2449
2437 def _get_members(root_gr):
2450 def _get_members(root_gr):
2438 if include_repos:
2451 if include_repos:
2439 for r in root_gr.repositories:
2452 for r in root_gr.repositories:
2440 all_.append(r)
2453 all_.append(r)
2441 childs = root_gr.children.all()
2454 childs = root_gr.children.all()
2442 if childs:
2455 if childs:
2443 for gr in childs:
2456 for gr in childs:
2444 all_.append(gr)
2457 all_.append(gr)
2445 _get_members(gr)
2458 _get_members(gr)
2446
2459
2447 _get_members(self)
2460 _get_members(self)
2448 return [self] + all_
2461 return [self] + all_
2449
2462
2450 def recursive_groups_and_repos(self):
2463 def recursive_groups_and_repos(self):
2451 """
2464 """
2452 Recursive return all groups, with repositories in those groups
2465 Recursive return all groups, with repositories in those groups
2453 """
2466 """
2454 return self._recursive_objects()
2467 return self._recursive_objects()
2455
2468
2456 def recursive_groups(self):
2469 def recursive_groups(self):
2457 """
2470 """
2458 Returns all children groups for this group including children of children
2471 Returns all children groups for this group including children of children
2459 """
2472 """
2460 return self._recursive_objects(include_repos=False)
2473 return self._recursive_objects(include_repos=False)
2461
2474
2462 def get_new_name(self, group_name):
2475 def get_new_name(self, group_name):
2463 """
2476 """
2464 returns new full group name based on parent and new name
2477 returns new full group name based on parent and new name
2465
2478
2466 :param group_name:
2479 :param group_name:
2467 """
2480 """
2468 path_prefix = (self.parent_group.full_path_splitted if
2481 path_prefix = (self.parent_group.full_path_splitted if
2469 self.parent_group else [])
2482 self.parent_group else [])
2470 return RepoGroup.url_sep().join(path_prefix + [group_name])
2483 return RepoGroup.url_sep().join(path_prefix + [group_name])
2471
2484
2472 def permissions(self, with_admins=True, with_owner=True):
2485 def permissions(self, with_admins=True, with_owner=True):
2473 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
2486 q = UserRepoGroupToPerm.query().filter(UserRepoGroupToPerm.group == self)
2474 q = q.options(joinedload(UserRepoGroupToPerm.group),
2487 q = q.options(joinedload(UserRepoGroupToPerm.group),
2475 joinedload(UserRepoGroupToPerm.user),
2488 joinedload(UserRepoGroupToPerm.user),
2476 joinedload(UserRepoGroupToPerm.permission),)
2489 joinedload(UserRepoGroupToPerm.permission),)
2477
2490
2478 # get owners and admins and permissions. We do a trick of re-writing
2491 # get owners and admins and permissions. We do a trick of re-writing
2479 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2492 # objects from sqlalchemy to named-tuples due to sqlalchemy session
2480 # has a global reference and changing one object propagates to all
2493 # has a global reference and changing one object propagates to all
2481 # others. This means if admin is also an owner admin_row that change
2494 # others. This means if admin is also an owner admin_row that change
2482 # would propagate to both objects
2495 # would propagate to both objects
2483 perm_rows = []
2496 perm_rows = []
2484 for _usr in q.all():
2497 for _usr in q.all():
2485 usr = AttributeDict(_usr.user.get_dict())
2498 usr = AttributeDict(_usr.user.get_dict())
2486 usr.permission = _usr.permission.permission_name
2499 usr.permission = _usr.permission.permission_name
2487 perm_rows.append(usr)
2500 perm_rows.append(usr)
2488
2501
2489 # filter the perm rows by 'default' first and then sort them by
2502 # filter the perm rows by 'default' first and then sort them by
2490 # admin,write,read,none permissions sorted again alphabetically in
2503 # admin,write,read,none permissions sorted again alphabetically in
2491 # each group
2504 # each group
2492 perm_rows = sorted(perm_rows, key=display_sort)
2505 perm_rows = sorted(perm_rows, key=display_user_sort)
2493
2506
2494 _admin_perm = 'group.admin'
2507 _admin_perm = 'group.admin'
2495 owner_row = []
2508 owner_row = []
2496 if with_owner:
2509 if with_owner:
2497 usr = AttributeDict(self.user.get_dict())
2510 usr = AttributeDict(self.user.get_dict())
2498 usr.owner_row = True
2511 usr.owner_row = True
2499 usr.permission = _admin_perm
2512 usr.permission = _admin_perm
2500 owner_row.append(usr)
2513 owner_row.append(usr)
2501
2514
2502 super_admin_rows = []
2515 super_admin_rows = []
2503 if with_admins:
2516 if with_admins:
2504 for usr in User.get_all_super_admins():
2517 for usr in User.get_all_super_admins():
2505 # if this admin is also owner, don't double the record
2518 # if this admin is also owner, don't double the record
2506 if usr.user_id == owner_row[0].user_id:
2519 if usr.user_id == owner_row[0].user_id:
2507 owner_row[0].admin_row = True
2520 owner_row[0].admin_row = True
2508 else:
2521 else:
2509 usr = AttributeDict(usr.get_dict())
2522 usr = AttributeDict(usr.get_dict())
2510 usr.admin_row = True
2523 usr.admin_row = True
2511 usr.permission = _admin_perm
2524 usr.permission = _admin_perm
2512 super_admin_rows.append(usr)
2525 super_admin_rows.append(usr)
2513
2526
2514 return super_admin_rows + owner_row + perm_rows
2527 return super_admin_rows + owner_row + perm_rows
2515
2528
2516 def permission_user_groups(self):
2529 def permission_user_groups(self):
2517 q = UserGroupRepoGroupToPerm.query().filter(UserGroupRepoGroupToPerm.group == self)
2530 q = UserGroupRepoGroupToPerm.query().filter(UserGroupRepoGroupToPerm.group == self)
2518 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
2531 q = q.options(joinedload(UserGroupRepoGroupToPerm.group),
2519 joinedload(UserGroupRepoGroupToPerm.users_group),
2532 joinedload(UserGroupRepoGroupToPerm.users_group),
2520 joinedload(UserGroupRepoGroupToPerm.permission),)
2533 joinedload(UserGroupRepoGroupToPerm.permission),)
2521
2534
2522 perm_rows = []
2535 perm_rows = []
2523 for _user_group in q.all():
2536 for _user_group in q.all():
2524 usr = AttributeDict(_user_group.users_group.get_dict())
2537 usr = AttributeDict(_user_group.users_group.get_dict())
2525 usr.permission = _user_group.permission.permission_name
2538 usr.permission = _user_group.permission.permission_name
2526 perm_rows.append(usr)
2539 perm_rows.append(usr)
2527
2540
2541 perm_rows = sorted(perm_rows, key=display_user_group_sort)
2528 return perm_rows
2542 return perm_rows
2529
2543
2530 def get_api_data(self):
2544 def get_api_data(self):
2531 """
2545 """
2532 Common function for generating api data
2546 Common function for generating api data
2533
2547
2534 """
2548 """
2535 group = self
2549 group = self
2536 data = {
2550 data = {
2537 'group_id': group.group_id,
2551 'group_id': group.group_id,
2538 'group_name': group.group_name,
2552 'group_name': group.group_name,
2539 'group_description': group.description_safe,
2553 'group_description': group.description_safe,
2540 'parent_group': group.parent_group.group_name if group.parent_group else None,
2554 'parent_group': group.parent_group.group_name if group.parent_group else None,
2541 'repositories': [x.repo_name for x in group.repositories],
2555 'repositories': [x.repo_name for x in group.repositories],
2542 'owner': group.user.username,
2556 'owner': group.user.username,
2543 }
2557 }
2544 return data
2558 return data
2545
2559
2546
2560
2547 class Permission(Base, BaseModel):
2561 class Permission(Base, BaseModel):
2548 __tablename__ = 'permissions'
2562 __tablename__ = 'permissions'
2549 __table_args__ = (
2563 __table_args__ = (
2550 Index('p_perm_name_idx', 'permission_name'),
2564 Index('p_perm_name_idx', 'permission_name'),
2551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2565 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2552 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2566 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
2553 )
2567 )
2554 PERMS = [
2568 PERMS = [
2555 ('hg.admin', _('RhodeCode Super Administrator')),
2569 ('hg.admin', _('RhodeCode Super Administrator')),
2556
2570
2557 ('repository.none', _('Repository no access')),
2571 ('repository.none', _('Repository no access')),
2558 ('repository.read', _('Repository read access')),
2572 ('repository.read', _('Repository read access')),
2559 ('repository.write', _('Repository write access')),
2573 ('repository.write', _('Repository write access')),
2560 ('repository.admin', _('Repository admin access')),
2574 ('repository.admin', _('Repository admin access')),
2561
2575
2562 ('group.none', _('Repository group no access')),
2576 ('group.none', _('Repository group no access')),
2563 ('group.read', _('Repository group read access')),
2577 ('group.read', _('Repository group read access')),
2564 ('group.write', _('Repository group write access')),
2578 ('group.write', _('Repository group write access')),
2565 ('group.admin', _('Repository group admin access')),
2579 ('group.admin', _('Repository group admin access')),
2566
2580
2567 ('usergroup.none', _('User group no access')),
2581 ('usergroup.none', _('User group no access')),
2568 ('usergroup.read', _('User group read access')),
2582 ('usergroup.read', _('User group read access')),
2569 ('usergroup.write', _('User group write access')),
2583 ('usergroup.write', _('User group write access')),
2570 ('usergroup.admin', _('User group admin access')),
2584 ('usergroup.admin', _('User group admin access')),
2571
2585
2572 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
2586 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
2573 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
2587 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
2574
2588
2575 ('hg.usergroup.create.false', _('User Group creation disabled')),
2589 ('hg.usergroup.create.false', _('User Group creation disabled')),
2576 ('hg.usergroup.create.true', _('User Group creation enabled')),
2590 ('hg.usergroup.create.true', _('User Group creation enabled')),
2577
2591
2578 ('hg.create.none', _('Repository creation disabled')),
2592 ('hg.create.none', _('Repository creation disabled')),
2579 ('hg.create.repository', _('Repository creation enabled')),
2593 ('hg.create.repository', _('Repository creation enabled')),
2580 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
2594 ('hg.create.write_on_repogroup.true', _('Repository creation enabled with write permission to a repository group')),
2581 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
2595 ('hg.create.write_on_repogroup.false', _('Repository creation disabled with write permission to a repository group')),
2582
2596
2583 ('hg.fork.none', _('Repository forking disabled')),
2597 ('hg.fork.none', _('Repository forking disabled')),
2584 ('hg.fork.repository', _('Repository forking enabled')),
2598 ('hg.fork.repository', _('Repository forking enabled')),
2585
2599
2586 ('hg.register.none', _('Registration disabled')),
2600 ('hg.register.none', _('Registration disabled')),
2587 ('hg.register.manual_activate', _('User Registration with manual account activation')),
2601 ('hg.register.manual_activate', _('User Registration with manual account activation')),
2588 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
2602 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
2589
2603
2590 ('hg.password_reset.enabled', _('Password reset enabled')),
2604 ('hg.password_reset.enabled', _('Password reset enabled')),
2591 ('hg.password_reset.hidden', _('Password reset hidden')),
2605 ('hg.password_reset.hidden', _('Password reset hidden')),
2592 ('hg.password_reset.disabled', _('Password reset disabled')),
2606 ('hg.password_reset.disabled', _('Password reset disabled')),
2593
2607
2594 ('hg.extern_activate.manual', _('Manual activation of external account')),
2608 ('hg.extern_activate.manual', _('Manual activation of external account')),
2595 ('hg.extern_activate.auto', _('Automatic activation of external account')),
2609 ('hg.extern_activate.auto', _('Automatic activation of external account')),
2596
2610
2597 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
2611 ('hg.inherit_default_perms.false', _('Inherit object permissions from default user disabled')),
2598 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
2612 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
2599 ]
2613 ]
2600
2614
2601 # definition of system default permissions for DEFAULT user
2615 # definition of system default permissions for DEFAULT user
2602 DEFAULT_USER_PERMISSIONS = [
2616 DEFAULT_USER_PERMISSIONS = [
2603 'repository.read',
2617 'repository.read',
2604 'group.read',
2618 'group.read',
2605 'usergroup.read',
2619 'usergroup.read',
2606 'hg.create.repository',
2620 'hg.create.repository',
2607 'hg.repogroup.create.false',
2621 'hg.repogroup.create.false',
2608 'hg.usergroup.create.false',
2622 'hg.usergroup.create.false',
2609 'hg.create.write_on_repogroup.true',
2623 'hg.create.write_on_repogroup.true',
2610 'hg.fork.repository',
2624 'hg.fork.repository',
2611 'hg.register.manual_activate',
2625 'hg.register.manual_activate',
2612 'hg.password_reset.enabled',
2626 'hg.password_reset.enabled',
2613 'hg.extern_activate.auto',
2627 'hg.extern_activate.auto',
2614 'hg.inherit_default_perms.true',
2628 'hg.inherit_default_perms.true',
2615 ]
2629 ]
2616
2630
2617 # defines which permissions are more important higher the more important
2631 # defines which permissions are more important higher the more important
2618 # Weight defines which permissions are more important.
2632 # Weight defines which permissions are more important.
2619 # The higher number the more important.
2633 # The higher number the more important.
2620 PERM_WEIGHTS = {
2634 PERM_WEIGHTS = {
2621 'repository.none': 0,
2635 'repository.none': 0,
2622 'repository.read': 1,
2636 'repository.read': 1,
2623 'repository.write': 3,
2637 'repository.write': 3,
2624 'repository.admin': 4,
2638 'repository.admin': 4,
2625
2639
2626 'group.none': 0,
2640 'group.none': 0,
2627 'group.read': 1,
2641 'group.read': 1,
2628 'group.write': 3,
2642 'group.write': 3,
2629 'group.admin': 4,
2643 'group.admin': 4,
2630
2644
2631 'usergroup.none': 0,
2645 'usergroup.none': 0,
2632 'usergroup.read': 1,
2646 'usergroup.read': 1,
2633 'usergroup.write': 3,
2647 'usergroup.write': 3,
2634 'usergroup.admin': 4,
2648 'usergroup.admin': 4,
2635
2649
2636 'hg.repogroup.create.false': 0,
2650 'hg.repogroup.create.false': 0,
2637 'hg.repogroup.create.true': 1,
2651 'hg.repogroup.create.true': 1,
2638
2652
2639 'hg.usergroup.create.false': 0,
2653 'hg.usergroup.create.false': 0,
2640 'hg.usergroup.create.true': 1,
2654 'hg.usergroup.create.true': 1,
2641
2655
2642 'hg.fork.none': 0,
2656 'hg.fork.none': 0,
2643 'hg.fork.repository': 1,
2657 'hg.fork.repository': 1,
2644 'hg.create.none': 0,
2658 'hg.create.none': 0,
2645 'hg.create.repository': 1
2659 'hg.create.repository': 1
2646 }
2660 }
2647
2661
2648 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2662 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2649 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
2663 permission_name = Column("permission_name", String(255), nullable=True, unique=None, default=None)
2650 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
2664 permission_longname = Column("permission_longname", String(255), nullable=True, unique=None, default=None)
2651
2665
2652 def __unicode__(self):
2666 def __unicode__(self):
2653 return u"<%s('%s:%s')>" % (
2667 return u"<%s('%s:%s')>" % (
2654 self.__class__.__name__, self.permission_id, self.permission_name
2668 self.__class__.__name__, self.permission_id, self.permission_name
2655 )
2669 )
2656
2670
2657 @classmethod
2671 @classmethod
2658 def get_by_key(cls, key):
2672 def get_by_key(cls, key):
2659 return cls.query().filter(cls.permission_name == key).scalar()
2673 return cls.query().filter(cls.permission_name == key).scalar()
2660
2674
2661 @classmethod
2675 @classmethod
2662 def get_default_repo_perms(cls, user_id, repo_id=None):
2676 def get_default_repo_perms(cls, user_id, repo_id=None):
2663 q = Session().query(UserRepoToPerm, Repository, Permission)\
2677 q = Session().query(UserRepoToPerm, Repository, Permission)\
2664 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
2678 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
2665 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
2679 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
2666 .filter(UserRepoToPerm.user_id == user_id)
2680 .filter(UserRepoToPerm.user_id == user_id)
2667 if repo_id:
2681 if repo_id:
2668 q = q.filter(UserRepoToPerm.repository_id == repo_id)
2682 q = q.filter(UserRepoToPerm.repository_id == repo_id)
2669 return q.all()
2683 return q.all()
2670
2684
2671 @classmethod
2685 @classmethod
2672 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
2686 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
2673 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
2687 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
2674 .join(
2688 .join(
2675 Permission,
2689 Permission,
2676 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
2690 UserGroupRepoToPerm.permission_id == Permission.permission_id)\
2677 .join(
2691 .join(
2678 Repository,
2692 Repository,
2679 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
2693 UserGroupRepoToPerm.repository_id == Repository.repo_id)\
2680 .join(
2694 .join(
2681 UserGroup,
2695 UserGroup,
2682 UserGroupRepoToPerm.users_group_id ==
2696 UserGroupRepoToPerm.users_group_id ==
2683 UserGroup.users_group_id)\
2697 UserGroup.users_group_id)\
2684 .join(
2698 .join(
2685 UserGroupMember,
2699 UserGroupMember,
2686 UserGroupRepoToPerm.users_group_id ==
2700 UserGroupRepoToPerm.users_group_id ==
2687 UserGroupMember.users_group_id)\
2701 UserGroupMember.users_group_id)\
2688 .filter(
2702 .filter(
2689 UserGroupMember.user_id == user_id,
2703 UserGroupMember.user_id == user_id,
2690 UserGroup.users_group_active == true())
2704 UserGroup.users_group_active == true())
2691 if repo_id:
2705 if repo_id:
2692 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
2706 q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
2693 return q.all()
2707 return q.all()
2694
2708
2695 @classmethod
2709 @classmethod
2696 def get_default_group_perms(cls, user_id, repo_group_id=None):
2710 def get_default_group_perms(cls, user_id, repo_group_id=None):
2697 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
2711 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
2698 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
2712 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
2699 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
2713 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
2700 .filter(UserRepoGroupToPerm.user_id == user_id)
2714 .filter(UserRepoGroupToPerm.user_id == user_id)
2701 if repo_group_id:
2715 if repo_group_id:
2702 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
2716 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
2703 return q.all()
2717 return q.all()
2704
2718
2705 @classmethod
2719 @classmethod
2706 def get_default_group_perms_from_user_group(
2720 def get_default_group_perms_from_user_group(
2707 cls, user_id, repo_group_id=None):
2721 cls, user_id, repo_group_id=None):
2708 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
2722 q = Session().query(UserGroupRepoGroupToPerm, RepoGroup, Permission)\
2709 .join(
2723 .join(
2710 Permission,
2724 Permission,
2711 UserGroupRepoGroupToPerm.permission_id ==
2725 UserGroupRepoGroupToPerm.permission_id ==
2712 Permission.permission_id)\
2726 Permission.permission_id)\
2713 .join(
2727 .join(
2714 RepoGroup,
2728 RepoGroup,
2715 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
2729 UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id)\
2716 .join(
2730 .join(
2717 UserGroup,
2731 UserGroup,
2718 UserGroupRepoGroupToPerm.users_group_id ==
2732 UserGroupRepoGroupToPerm.users_group_id ==
2719 UserGroup.users_group_id)\
2733 UserGroup.users_group_id)\
2720 .join(
2734 .join(
2721 UserGroupMember,
2735 UserGroupMember,
2722 UserGroupRepoGroupToPerm.users_group_id ==
2736 UserGroupRepoGroupToPerm.users_group_id ==
2723 UserGroupMember.users_group_id)\
2737 UserGroupMember.users_group_id)\
2724 .filter(
2738 .filter(
2725 UserGroupMember.user_id == user_id,
2739 UserGroupMember.user_id == user_id,
2726 UserGroup.users_group_active == true())
2740 UserGroup.users_group_active == true())
2727 if repo_group_id:
2741 if repo_group_id:
2728 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
2742 q = q.filter(UserGroupRepoGroupToPerm.group_id == repo_group_id)
2729 return q.all()
2743 return q.all()
2730
2744
2731 @classmethod
2745 @classmethod
2732 def get_default_user_group_perms(cls, user_id, user_group_id=None):
2746 def get_default_user_group_perms(cls, user_id, user_group_id=None):
2733 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
2747 q = Session().query(UserUserGroupToPerm, UserGroup, Permission)\
2734 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
2748 .join((Permission, UserUserGroupToPerm.permission_id == Permission.permission_id))\
2735 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
2749 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
2736 .filter(UserUserGroupToPerm.user_id == user_id)
2750 .filter(UserUserGroupToPerm.user_id == user_id)
2737 if user_group_id:
2751 if user_group_id:
2738 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
2752 q = q.filter(UserUserGroupToPerm.user_group_id == user_group_id)
2739 return q.all()
2753 return q.all()
2740
2754
2741 @classmethod
2755 @classmethod
2742 def get_default_user_group_perms_from_user_group(
2756 def get_default_user_group_perms_from_user_group(
2743 cls, user_id, user_group_id=None):
2757 cls, user_id, user_group_id=None):
2744 TargetUserGroup = aliased(UserGroup, name='target_user_group')
2758 TargetUserGroup = aliased(UserGroup, name='target_user_group')
2745 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
2759 q = Session().query(UserGroupUserGroupToPerm, UserGroup, Permission)\
2746 .join(
2760 .join(
2747 Permission,
2761 Permission,
2748 UserGroupUserGroupToPerm.permission_id ==
2762 UserGroupUserGroupToPerm.permission_id ==
2749 Permission.permission_id)\
2763 Permission.permission_id)\
2750 .join(
2764 .join(
2751 TargetUserGroup,
2765 TargetUserGroup,
2752 UserGroupUserGroupToPerm.target_user_group_id ==
2766 UserGroupUserGroupToPerm.target_user_group_id ==
2753 TargetUserGroup.users_group_id)\
2767 TargetUserGroup.users_group_id)\
2754 .join(
2768 .join(
2755 UserGroup,
2769 UserGroup,
2756 UserGroupUserGroupToPerm.user_group_id ==
2770 UserGroupUserGroupToPerm.user_group_id ==
2757 UserGroup.users_group_id)\
2771 UserGroup.users_group_id)\
2758 .join(
2772 .join(
2759 UserGroupMember,
2773 UserGroupMember,
2760 UserGroupUserGroupToPerm.user_group_id ==
2774 UserGroupUserGroupToPerm.user_group_id ==
2761 UserGroupMember.users_group_id)\
2775 UserGroupMember.users_group_id)\
2762 .filter(
2776 .filter(
2763 UserGroupMember.user_id == user_id,
2777 UserGroupMember.user_id == user_id,
2764 UserGroup.users_group_active == true())
2778 UserGroup.users_group_active == true())
2765 if user_group_id:
2779 if user_group_id:
2766 q = q.filter(
2780 q = q.filter(
2767 UserGroupUserGroupToPerm.user_group_id == user_group_id)
2781 UserGroupUserGroupToPerm.user_group_id == user_group_id)
2768
2782
2769 return q.all()
2783 return q.all()
2770
2784
2771
2785
2772 class UserRepoToPerm(Base, BaseModel):
2786 class UserRepoToPerm(Base, BaseModel):
2773 __tablename__ = 'repo_to_perm'
2787 __tablename__ = 'repo_to_perm'
2774 __table_args__ = (
2788 __table_args__ = (
2775 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
2789 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
2776 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2790 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2777 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2791 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2778 )
2792 )
2779 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2793 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2780 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2794 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2781 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2795 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2782 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2796 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2783
2797
2784 user = relationship('User')
2798 user = relationship('User')
2785 repository = relationship('Repository')
2799 repository = relationship('Repository')
2786 permission = relationship('Permission')
2800 permission = relationship('Permission')
2787
2801
2788 @classmethod
2802 @classmethod
2789 def create(cls, user, repository, permission):
2803 def create(cls, user, repository, permission):
2790 n = cls()
2804 n = cls()
2791 n.user = user
2805 n.user = user
2792 n.repository = repository
2806 n.repository = repository
2793 n.permission = permission
2807 n.permission = permission
2794 Session().add(n)
2808 Session().add(n)
2795 return n
2809 return n
2796
2810
2797 def __unicode__(self):
2811 def __unicode__(self):
2798 return u'<%s => %s >' % (self.user, self.repository)
2812 return u'<%s => %s >' % (self.user, self.repository)
2799
2813
2800
2814
2801 class UserUserGroupToPerm(Base, BaseModel):
2815 class UserUserGroupToPerm(Base, BaseModel):
2802 __tablename__ = 'user_user_group_to_perm'
2816 __tablename__ = 'user_user_group_to_perm'
2803 __table_args__ = (
2817 __table_args__ = (
2804 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
2818 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
2805 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2819 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2806 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2820 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2807 )
2821 )
2808 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2822 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2809 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2823 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2810 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2824 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2811 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2825 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2812
2826
2813 user = relationship('User')
2827 user = relationship('User')
2814 user_group = relationship('UserGroup')
2828 user_group = relationship('UserGroup')
2815 permission = relationship('Permission')
2829 permission = relationship('Permission')
2816
2830
2817 @classmethod
2831 @classmethod
2818 def create(cls, user, user_group, permission):
2832 def create(cls, user, user_group, permission):
2819 n = cls()
2833 n = cls()
2820 n.user = user
2834 n.user = user
2821 n.user_group = user_group
2835 n.user_group = user_group
2822 n.permission = permission
2836 n.permission = permission
2823 Session().add(n)
2837 Session().add(n)
2824 return n
2838 return n
2825
2839
2826 def __unicode__(self):
2840 def __unicode__(self):
2827 return u'<%s => %s >' % (self.user, self.user_group)
2841 return u'<%s => %s >' % (self.user, self.user_group)
2828
2842
2829
2843
2830 class UserToPerm(Base, BaseModel):
2844 class UserToPerm(Base, BaseModel):
2831 __tablename__ = 'user_to_perm'
2845 __tablename__ = 'user_to_perm'
2832 __table_args__ = (
2846 __table_args__ = (
2833 UniqueConstraint('user_id', 'permission_id'),
2847 UniqueConstraint('user_id', 'permission_id'),
2834 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2848 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2835 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2849 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2836 )
2850 )
2837 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2851 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2838 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2839 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2853 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2840
2854
2841 user = relationship('User')
2855 user = relationship('User')
2842 permission = relationship('Permission', lazy='joined')
2856 permission = relationship('Permission', lazy='joined')
2843
2857
2844 def __unicode__(self):
2858 def __unicode__(self):
2845 return u'<%s => %s >' % (self.user, self.permission)
2859 return u'<%s => %s >' % (self.user, self.permission)
2846
2860
2847
2861
2848 class UserGroupRepoToPerm(Base, BaseModel):
2862 class UserGroupRepoToPerm(Base, BaseModel):
2849 __tablename__ = 'users_group_repo_to_perm'
2863 __tablename__ = 'users_group_repo_to_perm'
2850 __table_args__ = (
2864 __table_args__ = (
2851 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
2865 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
2852 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2853 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2867 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2854 )
2868 )
2855 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2869 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2856 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2870 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2857 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2871 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2858 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2872 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
2859
2873
2860 users_group = relationship('UserGroup')
2874 users_group = relationship('UserGroup')
2861 permission = relationship('Permission')
2875 permission = relationship('Permission')
2862 repository = relationship('Repository')
2876 repository = relationship('Repository')
2863
2877
2864 @classmethod
2878 @classmethod
2865 def create(cls, users_group, repository, permission):
2879 def create(cls, users_group, repository, permission):
2866 n = cls()
2880 n = cls()
2867 n.users_group = users_group
2881 n.users_group = users_group
2868 n.repository = repository
2882 n.repository = repository
2869 n.permission = permission
2883 n.permission = permission
2870 Session().add(n)
2884 Session().add(n)
2871 return n
2885 return n
2872
2886
2873 def __unicode__(self):
2887 def __unicode__(self):
2874 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
2888 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
2875
2889
2876
2890
2877 class UserGroupUserGroupToPerm(Base, BaseModel):
2891 class UserGroupUserGroupToPerm(Base, BaseModel):
2878 __tablename__ = 'user_group_user_group_to_perm'
2892 __tablename__ = 'user_group_user_group_to_perm'
2879 __table_args__ = (
2893 __table_args__ = (
2880 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
2894 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
2881 CheckConstraint('target_user_group_id != user_group_id'),
2895 CheckConstraint('target_user_group_id != user_group_id'),
2882 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2896 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2883 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2897 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2884 )
2898 )
2885 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2899 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2886 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2900 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2887 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2901 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2888 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2902 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2889
2903
2890 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
2904 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
2891 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
2905 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
2892 permission = relationship('Permission')
2906 permission = relationship('Permission')
2893
2907
2894 @classmethod
2908 @classmethod
2895 def create(cls, target_user_group, user_group, permission):
2909 def create(cls, target_user_group, user_group, permission):
2896 n = cls()
2910 n = cls()
2897 n.target_user_group = target_user_group
2911 n.target_user_group = target_user_group
2898 n.user_group = user_group
2912 n.user_group = user_group
2899 n.permission = permission
2913 n.permission = permission
2900 Session().add(n)
2914 Session().add(n)
2901 return n
2915 return n
2902
2916
2903 def __unicode__(self):
2917 def __unicode__(self):
2904 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
2918 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
2905
2919
2906
2920
2907 class UserGroupToPerm(Base, BaseModel):
2921 class UserGroupToPerm(Base, BaseModel):
2908 __tablename__ = 'users_group_to_perm'
2922 __tablename__ = 'users_group_to_perm'
2909 __table_args__ = (
2923 __table_args__ = (
2910 UniqueConstraint('users_group_id', 'permission_id',),
2924 UniqueConstraint('users_group_id', 'permission_id',),
2911 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2925 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2912 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2926 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2913 )
2927 )
2914 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2928 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2915 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2929 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2916 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2930 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2917
2931
2918 users_group = relationship('UserGroup')
2932 users_group = relationship('UserGroup')
2919 permission = relationship('Permission')
2933 permission = relationship('Permission')
2920
2934
2921
2935
2922 class UserRepoGroupToPerm(Base, BaseModel):
2936 class UserRepoGroupToPerm(Base, BaseModel):
2923 __tablename__ = 'user_repo_group_to_perm'
2937 __tablename__ = 'user_repo_group_to_perm'
2924 __table_args__ = (
2938 __table_args__ = (
2925 UniqueConstraint('user_id', 'group_id', 'permission_id'),
2939 UniqueConstraint('user_id', 'group_id', 'permission_id'),
2926 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2940 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2927 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2941 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2928 )
2942 )
2929
2943
2930 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2944 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2931 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2945 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
2932 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2946 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2947 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2934
2948
2935 user = relationship('User')
2949 user = relationship('User')
2936 group = relationship('RepoGroup')
2950 group = relationship('RepoGroup')
2937 permission = relationship('Permission')
2951 permission = relationship('Permission')
2938
2952
2939 @classmethod
2953 @classmethod
2940 def create(cls, user, repository_group, permission):
2954 def create(cls, user, repository_group, permission):
2941 n = cls()
2955 n = cls()
2942 n.user = user
2956 n.user = user
2943 n.group = repository_group
2957 n.group = repository_group
2944 n.permission = permission
2958 n.permission = permission
2945 Session().add(n)
2959 Session().add(n)
2946 return n
2960 return n
2947
2961
2948
2962
2949 class UserGroupRepoGroupToPerm(Base, BaseModel):
2963 class UserGroupRepoGroupToPerm(Base, BaseModel):
2950 __tablename__ = 'users_group_repo_group_to_perm'
2964 __tablename__ = 'users_group_repo_group_to_perm'
2951 __table_args__ = (
2965 __table_args__ = (
2952 UniqueConstraint('users_group_id', 'group_id'),
2966 UniqueConstraint('users_group_id', 'group_id'),
2953 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2967 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2954 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2968 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2955 )
2969 )
2956
2970
2957 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2971 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2958 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2972 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
2959 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2973 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
2960 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2974 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
2961
2975
2962 users_group = relationship('UserGroup')
2976 users_group = relationship('UserGroup')
2963 permission = relationship('Permission')
2977 permission = relationship('Permission')
2964 group = relationship('RepoGroup')
2978 group = relationship('RepoGroup')
2965
2979
2966 @classmethod
2980 @classmethod
2967 def create(cls, user_group, repository_group, permission):
2981 def create(cls, user_group, repository_group, permission):
2968 n = cls()
2982 n = cls()
2969 n.users_group = user_group
2983 n.users_group = user_group
2970 n.group = repository_group
2984 n.group = repository_group
2971 n.permission = permission
2985 n.permission = permission
2972 Session().add(n)
2986 Session().add(n)
2973 return n
2987 return n
2974
2988
2975 def __unicode__(self):
2989 def __unicode__(self):
2976 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
2990 return u'<UserGroupRepoGroupToPerm:%s => %s >' % (self.users_group, self.group)
2977
2991
2978
2992
2979 class Statistics(Base, BaseModel):
2993 class Statistics(Base, BaseModel):
2980 __tablename__ = 'statistics'
2994 __tablename__ = 'statistics'
2981 __table_args__ = (
2995 __table_args__ = (
2982 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2996 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2983 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2997 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
2984 )
2998 )
2985 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2999 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
2986 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
3000 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
2987 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
3001 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
2988 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
3002 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
2989 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
3003 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
2990 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
3004 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
2991
3005
2992 repository = relationship('Repository', single_parent=True)
3006 repository = relationship('Repository', single_parent=True)
2993
3007
2994
3008
2995 class UserFollowing(Base, BaseModel):
3009 class UserFollowing(Base, BaseModel):
2996 __tablename__ = 'user_followings'
3010 __tablename__ = 'user_followings'
2997 __table_args__ = (
3011 __table_args__ = (
2998 UniqueConstraint('user_id', 'follows_repository_id'),
3012 UniqueConstraint('user_id', 'follows_repository_id'),
2999 UniqueConstraint('user_id', 'follows_user_id'),
3013 UniqueConstraint('user_id', 'follows_user_id'),
3000 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3014 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3001 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3015 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3002 )
3016 )
3003
3017
3004 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3018 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3005 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3019 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
3006 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
3020 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
3007 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
3021 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
3008 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
3022 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
3009
3023
3010 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
3024 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
3011
3025
3012 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
3026 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
3013 follows_repository = relationship('Repository', order_by='Repository.repo_name')
3027 follows_repository = relationship('Repository', order_by='Repository.repo_name')
3014
3028
3015 @classmethod
3029 @classmethod
3016 def get_repo_followers(cls, repo_id):
3030 def get_repo_followers(cls, repo_id):
3017 return cls.query().filter(cls.follows_repo_id == repo_id)
3031 return cls.query().filter(cls.follows_repo_id == repo_id)
3018
3032
3019
3033
3020 class CacheKey(Base, BaseModel):
3034 class CacheKey(Base, BaseModel):
3021 __tablename__ = 'cache_invalidation'
3035 __tablename__ = 'cache_invalidation'
3022 __table_args__ = (
3036 __table_args__ = (
3023 UniqueConstraint('cache_key'),
3037 UniqueConstraint('cache_key'),
3024 Index('key_idx', 'cache_key'),
3038 Index('key_idx', 'cache_key'),
3025 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3039 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3026 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3040 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3027 )
3041 )
3028 CACHE_TYPE_ATOM = 'ATOM'
3042 CACHE_TYPE_ATOM = 'ATOM'
3029 CACHE_TYPE_RSS = 'RSS'
3043 CACHE_TYPE_RSS = 'RSS'
3030 CACHE_TYPE_README = 'README'
3044 CACHE_TYPE_README = 'README'
3031
3045
3032 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3046 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
3033 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
3047 cache_key = Column("cache_key", String(255), nullable=True, unique=None, default=None)
3034 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
3048 cache_args = Column("cache_args", String(255), nullable=True, unique=None, default=None)
3035 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
3049 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
3036
3050
3037 def __init__(self, cache_key, cache_args=''):
3051 def __init__(self, cache_key, cache_args=''):
3038 self.cache_key = cache_key
3052 self.cache_key = cache_key
3039 self.cache_args = cache_args
3053 self.cache_args = cache_args
3040 self.cache_active = False
3054 self.cache_active = False
3041
3055
3042 def __unicode__(self):
3056 def __unicode__(self):
3043 return u"<%s('%s:%s[%s]')>" % (
3057 return u"<%s('%s:%s[%s]')>" % (
3044 self.__class__.__name__,
3058 self.__class__.__name__,
3045 self.cache_id, self.cache_key, self.cache_active)
3059 self.cache_id, self.cache_key, self.cache_active)
3046
3060
3047 def _cache_key_partition(self):
3061 def _cache_key_partition(self):
3048 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
3062 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
3049 return prefix, repo_name, suffix
3063 return prefix, repo_name, suffix
3050
3064
3051 def get_prefix(self):
3065 def get_prefix(self):
3052 """
3066 """
3053 Try to extract prefix from existing cache key. The key could consist
3067 Try to extract prefix from existing cache key. The key could consist
3054 of prefix, repo_name, suffix
3068 of prefix, repo_name, suffix
3055 """
3069 """
3056 # this returns prefix, repo_name, suffix
3070 # this returns prefix, repo_name, suffix
3057 return self._cache_key_partition()[0]
3071 return self._cache_key_partition()[0]
3058
3072
3059 def get_suffix(self):
3073 def get_suffix(self):
3060 """
3074 """
3061 get suffix that might have been used in _get_cache_key to
3075 get suffix that might have been used in _get_cache_key to
3062 generate self.cache_key. Only used for informational purposes
3076 generate self.cache_key. Only used for informational purposes
3063 in repo_edit.mako.
3077 in repo_edit.mako.
3064 """
3078 """
3065 # prefix, repo_name, suffix
3079 # prefix, repo_name, suffix
3066 return self._cache_key_partition()[2]
3080 return self._cache_key_partition()[2]
3067
3081
3068 @classmethod
3082 @classmethod
3069 def delete_all_cache(cls):
3083 def delete_all_cache(cls):
3070 """
3084 """
3071 Delete all cache keys from database.
3085 Delete all cache keys from database.
3072 Should only be run when all instances are down and all entries
3086 Should only be run when all instances are down and all entries
3073 thus stale.
3087 thus stale.
3074 """
3088 """
3075 cls.query().delete()
3089 cls.query().delete()
3076 Session().commit()
3090 Session().commit()
3077
3091
3078 @classmethod
3092 @classmethod
3079 def get_cache_key(cls, repo_name, cache_type):
3093 def get_cache_key(cls, repo_name, cache_type):
3080 """
3094 """
3081
3095
3082 Generate a cache key for this process of RhodeCode instance.
3096 Generate a cache key for this process of RhodeCode instance.
3083 Prefix most likely will be process id or maybe explicitly set
3097 Prefix most likely will be process id or maybe explicitly set
3084 instance_id from .ini file.
3098 instance_id from .ini file.
3085 """
3099 """
3086 import rhodecode
3100 import rhodecode
3087 prefix = safe_unicode(rhodecode.CONFIG.get('instance_id') or '')
3101 prefix = safe_unicode(rhodecode.CONFIG.get('instance_id') or '')
3088
3102
3089 repo_as_unicode = safe_unicode(repo_name)
3103 repo_as_unicode = safe_unicode(repo_name)
3090 key = u'{}_{}'.format(repo_as_unicode, cache_type) \
3104 key = u'{}_{}'.format(repo_as_unicode, cache_type) \
3091 if cache_type else repo_as_unicode
3105 if cache_type else repo_as_unicode
3092
3106
3093 return u'{}{}'.format(prefix, key)
3107 return u'{}{}'.format(prefix, key)
3094
3108
3095 @classmethod
3109 @classmethod
3096 def set_invalidate(cls, repo_name, delete=False):
3110 def set_invalidate(cls, repo_name, delete=False):
3097 """
3111 """
3098 Mark all caches of a repo as invalid in the database.
3112 Mark all caches of a repo as invalid in the database.
3099 """
3113 """
3100
3114
3101 try:
3115 try:
3102 qry = Session().query(cls).filter(cls.cache_args == repo_name)
3116 qry = Session().query(cls).filter(cls.cache_args == repo_name)
3103 if delete:
3117 if delete:
3104 log.debug('cache objects deleted for repo %s',
3118 log.debug('cache objects deleted for repo %s',
3105 safe_str(repo_name))
3119 safe_str(repo_name))
3106 qry.delete()
3120 qry.delete()
3107 else:
3121 else:
3108 log.debug('cache objects marked as invalid for repo %s',
3122 log.debug('cache objects marked as invalid for repo %s',
3109 safe_str(repo_name))
3123 safe_str(repo_name))
3110 qry.update({"cache_active": False})
3124 qry.update({"cache_active": False})
3111
3125
3112 Session().commit()
3126 Session().commit()
3113 except Exception:
3127 except Exception:
3114 log.exception(
3128 log.exception(
3115 'Cache key invalidation failed for repository %s',
3129 'Cache key invalidation failed for repository %s',
3116 safe_str(repo_name))
3130 safe_str(repo_name))
3117 Session().rollback()
3131 Session().rollback()
3118
3132
3119 @classmethod
3133 @classmethod
3120 def get_active_cache(cls, cache_key):
3134 def get_active_cache(cls, cache_key):
3121 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
3135 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
3122 if inv_obj:
3136 if inv_obj:
3123 return inv_obj
3137 return inv_obj
3124 return None
3138 return None
3125
3139
3126 @classmethod
3140 @classmethod
3127 def repo_context_cache(cls, compute_func, repo_name, cache_type,
3141 def repo_context_cache(cls, compute_func, repo_name, cache_type,
3128 thread_scoped=False):
3142 thread_scoped=False):
3129 """
3143 """
3130 @cache_region('long_term')
3144 @cache_region('long_term')
3131 def _heavy_calculation(cache_key):
3145 def _heavy_calculation(cache_key):
3132 return 'result'
3146 return 'result'
3133
3147
3134 cache_context = CacheKey.repo_context_cache(
3148 cache_context = CacheKey.repo_context_cache(
3135 _heavy_calculation, repo_name, cache_type)
3149 _heavy_calculation, repo_name, cache_type)
3136
3150
3137 with cache_context as context:
3151 with cache_context as context:
3138 context.invalidate()
3152 context.invalidate()
3139 computed = context.compute()
3153 computed = context.compute()
3140
3154
3141 assert computed == 'result'
3155 assert computed == 'result'
3142 """
3156 """
3143 from rhodecode.lib import caches
3157 from rhodecode.lib import caches
3144 return caches.InvalidationContext(
3158 return caches.InvalidationContext(
3145 compute_func, repo_name, cache_type, thread_scoped=thread_scoped)
3159 compute_func, repo_name, cache_type, thread_scoped=thread_scoped)
3146
3160
3147
3161
3148 class ChangesetComment(Base, BaseModel):
3162 class ChangesetComment(Base, BaseModel):
3149 __tablename__ = 'changeset_comments'
3163 __tablename__ = 'changeset_comments'
3150 __table_args__ = (
3164 __table_args__ = (
3151 Index('cc_revision_idx', 'revision'),
3165 Index('cc_revision_idx', 'revision'),
3152 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3166 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3153 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3167 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3154 )
3168 )
3155
3169
3156 COMMENT_OUTDATED = u'comment_outdated'
3170 COMMENT_OUTDATED = u'comment_outdated'
3157 COMMENT_TYPE_NOTE = u'note'
3171 COMMENT_TYPE_NOTE = u'note'
3158 COMMENT_TYPE_TODO = u'todo'
3172 COMMENT_TYPE_TODO = u'todo'
3159 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3173 COMMENT_TYPES = [COMMENT_TYPE_NOTE, COMMENT_TYPE_TODO]
3160
3174
3161 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3175 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
3162 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3176 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3163 revision = Column('revision', String(40), nullable=True)
3177 revision = Column('revision', String(40), nullable=True)
3164 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3178 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3165 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
3179 pull_request_version_id = Column("pull_request_version_id", Integer(), ForeignKey('pull_request_versions.pull_request_version_id'), nullable=True)
3166 line_no = Column('line_no', Unicode(10), nullable=True)
3180 line_no = Column('line_no', Unicode(10), nullable=True)
3167 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
3181 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
3168 f_path = Column('f_path', Unicode(1000), nullable=True)
3182 f_path = Column('f_path', Unicode(1000), nullable=True)
3169 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
3183 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
3170 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
3184 text = Column('text', UnicodeText().with_variant(UnicodeText(25000), 'mysql'), nullable=False)
3171 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3185 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3172 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3186 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3173 renderer = Column('renderer', Unicode(64), nullable=True)
3187 renderer = Column('renderer', Unicode(64), nullable=True)
3174 display_state = Column('display_state', Unicode(128), nullable=True)
3188 display_state = Column('display_state', Unicode(128), nullable=True)
3175
3189
3176 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3190 comment_type = Column('comment_type', Unicode(128), nullable=True, default=COMMENT_TYPE_NOTE)
3177 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
3191 resolved_comment_id = Column('resolved_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'), nullable=True)
3178 resolved_comment = relationship('ChangesetComment', remote_side=comment_id, backref='resolved_by')
3192 resolved_comment = relationship('ChangesetComment', remote_side=comment_id, backref='resolved_by')
3179 author = relationship('User', lazy='joined')
3193 author = relationship('User', lazy='joined')
3180 repo = relationship('Repository')
3194 repo = relationship('Repository')
3181 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan", lazy='joined')
3195 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan", lazy='joined')
3182 pull_request = relationship('PullRequest', lazy='joined')
3196 pull_request = relationship('PullRequest', lazy='joined')
3183 pull_request_version = relationship('PullRequestVersion')
3197 pull_request_version = relationship('PullRequestVersion')
3184
3198
3185 @classmethod
3199 @classmethod
3186 def get_users(cls, revision=None, pull_request_id=None):
3200 def get_users(cls, revision=None, pull_request_id=None):
3187 """
3201 """
3188 Returns user associated with this ChangesetComment. ie those
3202 Returns user associated with this ChangesetComment. ie those
3189 who actually commented
3203 who actually commented
3190
3204
3191 :param cls:
3205 :param cls:
3192 :param revision:
3206 :param revision:
3193 """
3207 """
3194 q = Session().query(User)\
3208 q = Session().query(User)\
3195 .join(ChangesetComment.author)
3209 .join(ChangesetComment.author)
3196 if revision:
3210 if revision:
3197 q = q.filter(cls.revision == revision)
3211 q = q.filter(cls.revision == revision)
3198 elif pull_request_id:
3212 elif pull_request_id:
3199 q = q.filter(cls.pull_request_id == pull_request_id)
3213 q = q.filter(cls.pull_request_id == pull_request_id)
3200 return q.all()
3214 return q.all()
3201
3215
3202 @classmethod
3216 @classmethod
3203 def get_index_from_version(cls, pr_version, versions):
3217 def get_index_from_version(cls, pr_version, versions):
3204 num_versions = [x.pull_request_version_id for x in versions]
3218 num_versions = [x.pull_request_version_id for x in versions]
3205 try:
3219 try:
3206 return num_versions.index(pr_version) +1
3220 return num_versions.index(pr_version) +1
3207 except (IndexError, ValueError):
3221 except (IndexError, ValueError):
3208 return
3222 return
3209
3223
3210 @property
3224 @property
3211 def outdated(self):
3225 def outdated(self):
3212 return self.display_state == self.COMMENT_OUTDATED
3226 return self.display_state == self.COMMENT_OUTDATED
3213
3227
3214 def outdated_at_version(self, version):
3228 def outdated_at_version(self, version):
3215 """
3229 """
3216 Checks if comment is outdated for given pull request version
3230 Checks if comment is outdated for given pull request version
3217 """
3231 """
3218 return self.outdated and self.pull_request_version_id != version
3232 return self.outdated and self.pull_request_version_id != version
3219
3233
3220 def older_than_version(self, version):
3234 def older_than_version(self, version):
3221 """
3235 """
3222 Checks if comment is made from previous version than given
3236 Checks if comment is made from previous version than given
3223 """
3237 """
3224 if version is None:
3238 if version is None:
3225 return self.pull_request_version_id is not None
3239 return self.pull_request_version_id is not None
3226
3240
3227 return self.pull_request_version_id < version
3241 return self.pull_request_version_id < version
3228
3242
3229 @property
3243 @property
3230 def resolved(self):
3244 def resolved(self):
3231 return self.resolved_by[0] if self.resolved_by else None
3245 return self.resolved_by[0] if self.resolved_by else None
3232
3246
3233 @property
3247 @property
3234 def is_todo(self):
3248 def is_todo(self):
3235 return self.comment_type == self.COMMENT_TYPE_TODO
3249 return self.comment_type == self.COMMENT_TYPE_TODO
3236
3250
3237 @property
3251 @property
3238 def is_inline(self):
3252 def is_inline(self):
3239 return self.line_no and self.f_path
3253 return self.line_no and self.f_path
3240
3254
3241 def get_index_version(self, versions):
3255 def get_index_version(self, versions):
3242 return self.get_index_from_version(
3256 return self.get_index_from_version(
3243 self.pull_request_version_id, versions)
3257 self.pull_request_version_id, versions)
3244
3258
3245 def __repr__(self):
3259 def __repr__(self):
3246 if self.comment_id:
3260 if self.comment_id:
3247 return '<DB:Comment #%s>' % self.comment_id
3261 return '<DB:Comment #%s>' % self.comment_id
3248 else:
3262 else:
3249 return '<DB:Comment at %#x>' % id(self)
3263 return '<DB:Comment at %#x>' % id(self)
3250
3264
3251 def get_api_data(self):
3265 def get_api_data(self):
3252 comment = self
3266 comment = self
3253 data = {
3267 data = {
3254 'comment_id': comment.comment_id,
3268 'comment_id': comment.comment_id,
3255 'comment_type': comment.comment_type,
3269 'comment_type': comment.comment_type,
3256 'comment_text': comment.text,
3270 'comment_text': comment.text,
3257 'comment_status': comment.status_change,
3271 'comment_status': comment.status_change,
3258 'comment_f_path': comment.f_path,
3272 'comment_f_path': comment.f_path,
3259 'comment_lineno': comment.line_no,
3273 'comment_lineno': comment.line_no,
3260 'comment_author': comment.author,
3274 'comment_author': comment.author,
3261 'comment_created_on': comment.created_on
3275 'comment_created_on': comment.created_on
3262 }
3276 }
3263 return data
3277 return data
3264
3278
3265 def __json__(self):
3279 def __json__(self):
3266 data = dict()
3280 data = dict()
3267 data.update(self.get_api_data())
3281 data.update(self.get_api_data())
3268 return data
3282 return data
3269
3283
3270
3284
3271 class ChangesetStatus(Base, BaseModel):
3285 class ChangesetStatus(Base, BaseModel):
3272 __tablename__ = 'changeset_statuses'
3286 __tablename__ = 'changeset_statuses'
3273 __table_args__ = (
3287 __table_args__ = (
3274 Index('cs_revision_idx', 'revision'),
3288 Index('cs_revision_idx', 'revision'),
3275 Index('cs_version_idx', 'version'),
3289 Index('cs_version_idx', 'version'),
3276 UniqueConstraint('repo_id', 'revision', 'version'),
3290 UniqueConstraint('repo_id', 'revision', 'version'),
3277 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3291 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3278 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3292 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3279 )
3293 )
3280 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
3294 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
3281 STATUS_APPROVED = 'approved'
3295 STATUS_APPROVED = 'approved'
3282 STATUS_REJECTED = 'rejected'
3296 STATUS_REJECTED = 'rejected'
3283 STATUS_UNDER_REVIEW = 'under_review'
3297 STATUS_UNDER_REVIEW = 'under_review'
3284
3298
3285 STATUSES = [
3299 STATUSES = [
3286 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
3300 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
3287 (STATUS_APPROVED, _("Approved")),
3301 (STATUS_APPROVED, _("Approved")),
3288 (STATUS_REJECTED, _("Rejected")),
3302 (STATUS_REJECTED, _("Rejected")),
3289 (STATUS_UNDER_REVIEW, _("Under Review")),
3303 (STATUS_UNDER_REVIEW, _("Under Review")),
3290 ]
3304 ]
3291
3305
3292 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
3306 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
3293 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3307 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
3294 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
3308 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
3295 revision = Column('revision', String(40), nullable=False)
3309 revision = Column('revision', String(40), nullable=False)
3296 status = Column('status', String(128), nullable=False, default=DEFAULT)
3310 status = Column('status', String(128), nullable=False, default=DEFAULT)
3297 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
3311 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
3298 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
3312 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
3299 version = Column('version', Integer(), nullable=False, default=0)
3313 version = Column('version', Integer(), nullable=False, default=0)
3300 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3314 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
3301
3315
3302 author = relationship('User', lazy='joined')
3316 author = relationship('User', lazy='joined')
3303 repo = relationship('Repository')
3317 repo = relationship('Repository')
3304 comment = relationship('ChangesetComment', lazy='joined')
3318 comment = relationship('ChangesetComment', lazy='joined')
3305 pull_request = relationship('PullRequest', lazy='joined')
3319 pull_request = relationship('PullRequest', lazy='joined')
3306
3320
3307 def __unicode__(self):
3321 def __unicode__(self):
3308 return u"<%s('%s[v%s]:%s')>" % (
3322 return u"<%s('%s[v%s]:%s')>" % (
3309 self.__class__.__name__,
3323 self.__class__.__name__,
3310 self.status, self.version, self.author
3324 self.status, self.version, self.author
3311 )
3325 )
3312
3326
3313 @classmethod
3327 @classmethod
3314 def get_status_lbl(cls, value):
3328 def get_status_lbl(cls, value):
3315 return dict(cls.STATUSES).get(value)
3329 return dict(cls.STATUSES).get(value)
3316
3330
3317 @property
3331 @property
3318 def status_lbl(self):
3332 def status_lbl(self):
3319 return ChangesetStatus.get_status_lbl(self.status)
3333 return ChangesetStatus.get_status_lbl(self.status)
3320
3334
3321 def get_api_data(self):
3335 def get_api_data(self):
3322 status = self
3336 status = self
3323 data = {
3337 data = {
3324 'status_id': status.changeset_status_id,
3338 'status_id': status.changeset_status_id,
3325 'status': status.status,
3339 'status': status.status,
3326 }
3340 }
3327 return data
3341 return data
3328
3342
3329 def __json__(self):
3343 def __json__(self):
3330 data = dict()
3344 data = dict()
3331 data.update(self.get_api_data())
3345 data.update(self.get_api_data())
3332 return data
3346 return data
3333
3347
3334
3348
3335 class _PullRequestBase(BaseModel):
3349 class _PullRequestBase(BaseModel):
3336 """
3350 """
3337 Common attributes of pull request and version entries.
3351 Common attributes of pull request and version entries.
3338 """
3352 """
3339
3353
3340 # .status values
3354 # .status values
3341 STATUS_NEW = u'new'
3355 STATUS_NEW = u'new'
3342 STATUS_OPEN = u'open'
3356 STATUS_OPEN = u'open'
3343 STATUS_CLOSED = u'closed'
3357 STATUS_CLOSED = u'closed'
3344
3358
3345 title = Column('title', Unicode(255), nullable=True)
3359 title = Column('title', Unicode(255), nullable=True)
3346 description = Column(
3360 description = Column(
3347 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
3361 'description', UnicodeText().with_variant(UnicodeText(10240), 'mysql'),
3348 nullable=True)
3362 nullable=True)
3349 # new/open/closed status of pull request (not approve/reject/etc)
3363 # new/open/closed status of pull request (not approve/reject/etc)
3350 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
3364 status = Column('status', Unicode(255), nullable=False, default=STATUS_NEW)
3351 created_on = Column(
3365 created_on = Column(
3352 'created_on', DateTime(timezone=False), nullable=False,
3366 'created_on', DateTime(timezone=False), nullable=False,
3353 default=datetime.datetime.now)
3367 default=datetime.datetime.now)
3354 updated_on = Column(
3368 updated_on = Column(
3355 'updated_on', DateTime(timezone=False), nullable=False,
3369 'updated_on', DateTime(timezone=False), nullable=False,
3356 default=datetime.datetime.now)
3370 default=datetime.datetime.now)
3357
3371
3358 @declared_attr
3372 @declared_attr
3359 def user_id(cls):
3373 def user_id(cls):
3360 return Column(
3374 return Column(
3361 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
3375 "user_id", Integer(), ForeignKey('users.user_id'), nullable=False,
3362 unique=None)
3376 unique=None)
3363
3377
3364 # 500 revisions max
3378 # 500 revisions max
3365 _revisions = Column(
3379 _revisions = Column(
3366 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3380 'revisions', UnicodeText().with_variant(UnicodeText(20500), 'mysql'))
3367
3381
3368 @declared_attr
3382 @declared_attr
3369 def source_repo_id(cls):
3383 def source_repo_id(cls):
3370 # TODO: dan: rename column to source_repo_id
3384 # TODO: dan: rename column to source_repo_id
3371 return Column(
3385 return Column(
3372 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3386 'org_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3373 nullable=False)
3387 nullable=False)
3374
3388
3375 source_ref = Column('org_ref', Unicode(255), nullable=False)
3389 source_ref = Column('org_ref', Unicode(255), nullable=False)
3376
3390
3377 @declared_attr
3391 @declared_attr
3378 def target_repo_id(cls):
3392 def target_repo_id(cls):
3379 # TODO: dan: rename column to target_repo_id
3393 # TODO: dan: rename column to target_repo_id
3380 return Column(
3394 return Column(
3381 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3395 'other_repo_id', Integer(), ForeignKey('repositories.repo_id'),
3382 nullable=False)
3396 nullable=False)
3383
3397
3384 target_ref = Column('other_ref', Unicode(255), nullable=False)
3398 target_ref = Column('other_ref', Unicode(255), nullable=False)
3385 _shadow_merge_ref = Column('shadow_merge_ref', Unicode(255), nullable=True)
3399 _shadow_merge_ref = Column('shadow_merge_ref', Unicode(255), nullable=True)
3386
3400
3387 # TODO: dan: rename column to last_merge_source_rev
3401 # TODO: dan: rename column to last_merge_source_rev
3388 _last_merge_source_rev = Column(
3402 _last_merge_source_rev = Column(
3389 'last_merge_org_rev', String(40), nullable=True)
3403 'last_merge_org_rev', String(40), nullable=True)
3390 # TODO: dan: rename column to last_merge_target_rev
3404 # TODO: dan: rename column to last_merge_target_rev
3391 _last_merge_target_rev = Column(
3405 _last_merge_target_rev = Column(
3392 'last_merge_other_rev', String(40), nullable=True)
3406 'last_merge_other_rev', String(40), nullable=True)
3393 _last_merge_status = Column('merge_status', Integer(), nullable=True)
3407 _last_merge_status = Column('merge_status', Integer(), nullable=True)
3394 merge_rev = Column('merge_rev', String(40), nullable=True)
3408 merge_rev = Column('merge_rev', String(40), nullable=True)
3395
3409
3396 reviewer_data = Column(
3410 reviewer_data = Column(
3397 'reviewer_data_json', MutationObj.as_mutable(
3411 'reviewer_data_json', MutationObj.as_mutable(
3398 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
3412 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
3399
3413
3400 @property
3414 @property
3401 def reviewer_data_json(self):
3415 def reviewer_data_json(self):
3402 return json.dumps(self.reviewer_data)
3416 return json.dumps(self.reviewer_data)
3403
3417
3404 @hybrid_property
3418 @hybrid_property
3405 def description_safe(self):
3419 def description_safe(self):
3406 from rhodecode.lib import helpers as h
3420 from rhodecode.lib import helpers as h
3407 return h.escape(self.description)
3421 return h.escape(self.description)
3408
3422
3409 @hybrid_property
3423 @hybrid_property
3410 def revisions(self):
3424 def revisions(self):
3411 return self._revisions.split(':') if self._revisions else []
3425 return self._revisions.split(':') if self._revisions else []
3412
3426
3413 @revisions.setter
3427 @revisions.setter
3414 def revisions(self, val):
3428 def revisions(self, val):
3415 self._revisions = ':'.join(val)
3429 self._revisions = ':'.join(val)
3416
3430
3417 @hybrid_property
3431 @hybrid_property
3418 def last_merge_status(self):
3432 def last_merge_status(self):
3419 return safe_int(self._last_merge_status)
3433 return safe_int(self._last_merge_status)
3420
3434
3421 @last_merge_status.setter
3435 @last_merge_status.setter
3422 def last_merge_status(self, val):
3436 def last_merge_status(self, val):
3423 self._last_merge_status = val
3437 self._last_merge_status = val
3424
3438
3425 @declared_attr
3439 @declared_attr
3426 def author(cls):
3440 def author(cls):
3427 return relationship('User', lazy='joined')
3441 return relationship('User', lazy='joined')
3428
3442
3429 @declared_attr
3443 @declared_attr
3430 def source_repo(cls):
3444 def source_repo(cls):
3431 return relationship(
3445 return relationship(
3432 'Repository',
3446 'Repository',
3433 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
3447 primaryjoin='%s.source_repo_id==Repository.repo_id' % cls.__name__)
3434
3448
3435 @property
3449 @property
3436 def source_ref_parts(self):
3450 def source_ref_parts(self):
3437 return self.unicode_to_reference(self.source_ref)
3451 return self.unicode_to_reference(self.source_ref)
3438
3452
3439 @declared_attr
3453 @declared_attr
3440 def target_repo(cls):
3454 def target_repo(cls):
3441 return relationship(
3455 return relationship(
3442 'Repository',
3456 'Repository',
3443 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
3457 primaryjoin='%s.target_repo_id==Repository.repo_id' % cls.__name__)
3444
3458
3445 @property
3459 @property
3446 def target_ref_parts(self):
3460 def target_ref_parts(self):
3447 return self.unicode_to_reference(self.target_ref)
3461 return self.unicode_to_reference(self.target_ref)
3448
3462
3449 @property
3463 @property
3450 def shadow_merge_ref(self):
3464 def shadow_merge_ref(self):
3451 return self.unicode_to_reference(self._shadow_merge_ref)
3465 return self.unicode_to_reference(self._shadow_merge_ref)
3452
3466
3453 @shadow_merge_ref.setter
3467 @shadow_merge_ref.setter
3454 def shadow_merge_ref(self, ref):
3468 def shadow_merge_ref(self, ref):
3455 self._shadow_merge_ref = self.reference_to_unicode(ref)
3469 self._shadow_merge_ref = self.reference_to_unicode(ref)
3456
3470
3457 def unicode_to_reference(self, raw):
3471 def unicode_to_reference(self, raw):
3458 """
3472 """
3459 Convert a unicode (or string) to a reference object.
3473 Convert a unicode (or string) to a reference object.
3460 If unicode evaluates to False it returns None.
3474 If unicode evaluates to False it returns None.
3461 """
3475 """
3462 if raw:
3476 if raw:
3463 refs = raw.split(':')
3477 refs = raw.split(':')
3464 return Reference(*refs)
3478 return Reference(*refs)
3465 else:
3479 else:
3466 return None
3480 return None
3467
3481
3468 def reference_to_unicode(self, ref):
3482 def reference_to_unicode(self, ref):
3469 """
3483 """
3470 Convert a reference object to unicode.
3484 Convert a reference object to unicode.
3471 If reference is None it returns None.
3485 If reference is None it returns None.
3472 """
3486 """
3473 if ref:
3487 if ref:
3474 return u':'.join(ref)
3488 return u':'.join(ref)
3475 else:
3489 else:
3476 return None
3490 return None
3477
3491
3478 def get_api_data(self, with_merge_state=True):
3492 def get_api_data(self, with_merge_state=True):
3479 from rhodecode.model.pull_request import PullRequestModel
3493 from rhodecode.model.pull_request import PullRequestModel
3480
3494
3481 pull_request = self
3495 pull_request = self
3482 if with_merge_state:
3496 if with_merge_state:
3483 merge_status = PullRequestModel().merge_status(pull_request)
3497 merge_status = PullRequestModel().merge_status(pull_request)
3484 merge_state = {
3498 merge_state = {
3485 'status': merge_status[0],
3499 'status': merge_status[0],
3486 'message': safe_unicode(merge_status[1]),
3500 'message': safe_unicode(merge_status[1]),
3487 }
3501 }
3488 else:
3502 else:
3489 merge_state = {'status': 'not_available',
3503 merge_state = {'status': 'not_available',
3490 'message': 'not_available'}
3504 'message': 'not_available'}
3491
3505
3492 merge_data = {
3506 merge_data = {
3493 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request),
3507 'clone_url': PullRequestModel().get_shadow_clone_url(pull_request),
3494 'reference': (
3508 'reference': (
3495 pull_request.shadow_merge_ref._asdict()
3509 pull_request.shadow_merge_ref._asdict()
3496 if pull_request.shadow_merge_ref else None),
3510 if pull_request.shadow_merge_ref else None),
3497 }
3511 }
3498
3512
3499 data = {
3513 data = {
3500 'pull_request_id': pull_request.pull_request_id,
3514 'pull_request_id': pull_request.pull_request_id,
3501 'url': PullRequestModel().get_url(pull_request),
3515 'url': PullRequestModel().get_url(pull_request),
3502 'title': pull_request.title,
3516 'title': pull_request.title,
3503 'description': pull_request.description,
3517 'description': pull_request.description,
3504 'status': pull_request.status,
3518 'status': pull_request.status,
3505 'created_on': pull_request.created_on,
3519 'created_on': pull_request.created_on,
3506 'updated_on': pull_request.updated_on,
3520 'updated_on': pull_request.updated_on,
3507 'commit_ids': pull_request.revisions,
3521 'commit_ids': pull_request.revisions,
3508 'review_status': pull_request.calculated_review_status(),
3522 'review_status': pull_request.calculated_review_status(),
3509 'mergeable': merge_state,
3523 'mergeable': merge_state,
3510 'source': {
3524 'source': {
3511 'clone_url': pull_request.source_repo.clone_url(),
3525 'clone_url': pull_request.source_repo.clone_url(),
3512 'repository': pull_request.source_repo.repo_name,
3526 'repository': pull_request.source_repo.repo_name,
3513 'reference': {
3527 'reference': {
3514 'name': pull_request.source_ref_parts.name,
3528 'name': pull_request.source_ref_parts.name,
3515 'type': pull_request.source_ref_parts.type,
3529 'type': pull_request.source_ref_parts.type,
3516 'commit_id': pull_request.source_ref_parts.commit_id,
3530 'commit_id': pull_request.source_ref_parts.commit_id,
3517 },
3531 },
3518 },
3532 },
3519 'target': {
3533 'target': {
3520 'clone_url': pull_request.target_repo.clone_url(),
3534 'clone_url': pull_request.target_repo.clone_url(),
3521 'repository': pull_request.target_repo.repo_name,
3535 'repository': pull_request.target_repo.repo_name,
3522 'reference': {
3536 'reference': {
3523 'name': pull_request.target_ref_parts.name,
3537 'name': pull_request.target_ref_parts.name,
3524 'type': pull_request.target_ref_parts.type,
3538 'type': pull_request.target_ref_parts.type,
3525 'commit_id': pull_request.target_ref_parts.commit_id,
3539 'commit_id': pull_request.target_ref_parts.commit_id,
3526 },
3540 },
3527 },
3541 },
3528 'merge': merge_data,
3542 'merge': merge_data,
3529 'author': pull_request.author.get_api_data(include_secrets=False,
3543 'author': pull_request.author.get_api_data(include_secrets=False,
3530 details='basic'),
3544 details='basic'),
3531 'reviewers': [
3545 'reviewers': [
3532 {
3546 {
3533 'user': reviewer.get_api_data(include_secrets=False,
3547 'user': reviewer.get_api_data(include_secrets=False,
3534 details='basic'),
3548 details='basic'),
3535 'reasons': reasons,
3549 'reasons': reasons,
3536 'review_status': st[0][1].status if st else 'not_reviewed',
3550 'review_status': st[0][1].status if st else 'not_reviewed',
3537 }
3551 }
3538 for reviewer, reasons, mandatory, st in
3552 for reviewer, reasons, mandatory, st in
3539 pull_request.reviewers_statuses()
3553 pull_request.reviewers_statuses()
3540 ]
3554 ]
3541 }
3555 }
3542
3556
3543 return data
3557 return data
3544
3558
3545
3559
3546 class PullRequest(Base, _PullRequestBase):
3560 class PullRequest(Base, _PullRequestBase):
3547 __tablename__ = 'pull_requests'
3561 __tablename__ = 'pull_requests'
3548 __table_args__ = (
3562 __table_args__ = (
3549 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3563 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3550 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3564 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3551 )
3565 )
3552
3566
3553 pull_request_id = Column(
3567 pull_request_id = Column(
3554 'pull_request_id', Integer(), nullable=False, primary_key=True)
3568 'pull_request_id', Integer(), nullable=False, primary_key=True)
3555
3569
3556 def __repr__(self):
3570 def __repr__(self):
3557 if self.pull_request_id:
3571 if self.pull_request_id:
3558 return '<DB:PullRequest #%s>' % self.pull_request_id
3572 return '<DB:PullRequest #%s>' % self.pull_request_id
3559 else:
3573 else:
3560 return '<DB:PullRequest at %#x>' % id(self)
3574 return '<DB:PullRequest at %#x>' % id(self)
3561
3575
3562 reviewers = relationship('PullRequestReviewers',
3576 reviewers = relationship('PullRequestReviewers',
3563 cascade="all, delete, delete-orphan")
3577 cascade="all, delete, delete-orphan")
3564 statuses = relationship('ChangesetStatus',
3578 statuses = relationship('ChangesetStatus',
3565 cascade="all, delete, delete-orphan")
3579 cascade="all, delete, delete-orphan")
3566 comments = relationship('ChangesetComment',
3580 comments = relationship('ChangesetComment',
3567 cascade="all, delete, delete-orphan")
3581 cascade="all, delete, delete-orphan")
3568 versions = relationship('PullRequestVersion',
3582 versions = relationship('PullRequestVersion',
3569 cascade="all, delete, delete-orphan",
3583 cascade="all, delete, delete-orphan",
3570 lazy='dynamic')
3584 lazy='dynamic')
3571
3585
3572 @classmethod
3586 @classmethod
3573 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
3587 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
3574 internal_methods=None):
3588 internal_methods=None):
3575
3589
3576 class PullRequestDisplay(object):
3590 class PullRequestDisplay(object):
3577 """
3591 """
3578 Special object wrapper for showing PullRequest data via Versions
3592 Special object wrapper for showing PullRequest data via Versions
3579 It mimics PR object as close as possible. This is read only object
3593 It mimics PR object as close as possible. This is read only object
3580 just for display
3594 just for display
3581 """
3595 """
3582
3596
3583 def __init__(self, attrs, internal=None):
3597 def __init__(self, attrs, internal=None):
3584 self.attrs = attrs
3598 self.attrs = attrs
3585 # internal have priority over the given ones via attrs
3599 # internal have priority over the given ones via attrs
3586 self.internal = internal or ['versions']
3600 self.internal = internal or ['versions']
3587
3601
3588 def __getattr__(self, item):
3602 def __getattr__(self, item):
3589 if item in self.internal:
3603 if item in self.internal:
3590 return getattr(self, item)
3604 return getattr(self, item)
3591 try:
3605 try:
3592 return self.attrs[item]
3606 return self.attrs[item]
3593 except KeyError:
3607 except KeyError:
3594 raise AttributeError(
3608 raise AttributeError(
3595 '%s object has no attribute %s' % (self, item))
3609 '%s object has no attribute %s' % (self, item))
3596
3610
3597 def __repr__(self):
3611 def __repr__(self):
3598 return '<DB:PullRequestDisplay #%s>' % self.attrs.get('pull_request_id')
3612 return '<DB:PullRequestDisplay #%s>' % self.attrs.get('pull_request_id')
3599
3613
3600 def versions(self):
3614 def versions(self):
3601 return pull_request_obj.versions.order_by(
3615 return pull_request_obj.versions.order_by(
3602 PullRequestVersion.pull_request_version_id).all()
3616 PullRequestVersion.pull_request_version_id).all()
3603
3617
3604 def is_closed(self):
3618 def is_closed(self):
3605 return pull_request_obj.is_closed()
3619 return pull_request_obj.is_closed()
3606
3620
3607 @property
3621 @property
3608 def pull_request_version_id(self):
3622 def pull_request_version_id(self):
3609 return getattr(pull_request_obj, 'pull_request_version_id', None)
3623 return getattr(pull_request_obj, 'pull_request_version_id', None)
3610
3624
3611 attrs = StrictAttributeDict(pull_request_obj.get_api_data())
3625 attrs = StrictAttributeDict(pull_request_obj.get_api_data())
3612
3626
3613 attrs.author = StrictAttributeDict(
3627 attrs.author = StrictAttributeDict(
3614 pull_request_obj.author.get_api_data())
3628 pull_request_obj.author.get_api_data())
3615 if pull_request_obj.target_repo:
3629 if pull_request_obj.target_repo:
3616 attrs.target_repo = StrictAttributeDict(
3630 attrs.target_repo = StrictAttributeDict(
3617 pull_request_obj.target_repo.get_api_data())
3631 pull_request_obj.target_repo.get_api_data())
3618 attrs.target_repo.clone_url = pull_request_obj.target_repo.clone_url
3632 attrs.target_repo.clone_url = pull_request_obj.target_repo.clone_url
3619
3633
3620 if pull_request_obj.source_repo:
3634 if pull_request_obj.source_repo:
3621 attrs.source_repo = StrictAttributeDict(
3635 attrs.source_repo = StrictAttributeDict(
3622 pull_request_obj.source_repo.get_api_data())
3636 pull_request_obj.source_repo.get_api_data())
3623 attrs.source_repo.clone_url = pull_request_obj.source_repo.clone_url
3637 attrs.source_repo.clone_url = pull_request_obj.source_repo.clone_url
3624
3638
3625 attrs.source_ref_parts = pull_request_obj.source_ref_parts
3639 attrs.source_ref_parts = pull_request_obj.source_ref_parts
3626 attrs.target_ref_parts = pull_request_obj.target_ref_parts
3640 attrs.target_ref_parts = pull_request_obj.target_ref_parts
3627 attrs.revisions = pull_request_obj.revisions
3641 attrs.revisions = pull_request_obj.revisions
3628
3642
3629 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
3643 attrs.shadow_merge_ref = org_pull_request_obj.shadow_merge_ref
3630 attrs.reviewer_data = org_pull_request_obj.reviewer_data
3644 attrs.reviewer_data = org_pull_request_obj.reviewer_data
3631 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
3645 attrs.reviewer_data_json = org_pull_request_obj.reviewer_data_json
3632
3646
3633 return PullRequestDisplay(attrs, internal=internal_methods)
3647 return PullRequestDisplay(attrs, internal=internal_methods)
3634
3648
3635 def is_closed(self):
3649 def is_closed(self):
3636 return self.status == self.STATUS_CLOSED
3650 return self.status == self.STATUS_CLOSED
3637
3651
3638 def __json__(self):
3652 def __json__(self):
3639 return {
3653 return {
3640 'revisions': self.revisions,
3654 'revisions': self.revisions,
3641 }
3655 }
3642
3656
3643 def calculated_review_status(self):
3657 def calculated_review_status(self):
3644 from rhodecode.model.changeset_status import ChangesetStatusModel
3658 from rhodecode.model.changeset_status import ChangesetStatusModel
3645 return ChangesetStatusModel().calculated_review_status(self)
3659 return ChangesetStatusModel().calculated_review_status(self)
3646
3660
3647 def reviewers_statuses(self):
3661 def reviewers_statuses(self):
3648 from rhodecode.model.changeset_status import ChangesetStatusModel
3662 from rhodecode.model.changeset_status import ChangesetStatusModel
3649 return ChangesetStatusModel().reviewers_statuses(self)
3663 return ChangesetStatusModel().reviewers_statuses(self)
3650
3664
3651 @property
3665 @property
3652 def workspace_id(self):
3666 def workspace_id(self):
3653 from rhodecode.model.pull_request import PullRequestModel
3667 from rhodecode.model.pull_request import PullRequestModel
3654 return PullRequestModel()._workspace_id(self)
3668 return PullRequestModel()._workspace_id(self)
3655
3669
3656 def get_shadow_repo(self):
3670 def get_shadow_repo(self):
3657 workspace_id = self.workspace_id
3671 workspace_id = self.workspace_id
3658 vcs_obj = self.target_repo.scm_instance()
3672 vcs_obj = self.target_repo.scm_instance()
3659 shadow_repository_path = vcs_obj._get_shadow_repository_path(
3673 shadow_repository_path = vcs_obj._get_shadow_repository_path(
3660 workspace_id)
3674 workspace_id)
3661 return vcs_obj._get_shadow_instance(shadow_repository_path)
3675 return vcs_obj._get_shadow_instance(shadow_repository_path)
3662
3676
3663
3677
3664 class PullRequestVersion(Base, _PullRequestBase):
3678 class PullRequestVersion(Base, _PullRequestBase):
3665 __tablename__ = 'pull_request_versions'
3679 __tablename__ = 'pull_request_versions'
3666 __table_args__ = (
3680 __table_args__ = (
3667 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3681 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3668 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3682 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3669 )
3683 )
3670
3684
3671 pull_request_version_id = Column(
3685 pull_request_version_id = Column(
3672 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
3686 'pull_request_version_id', Integer(), nullable=False, primary_key=True)
3673 pull_request_id = Column(
3687 pull_request_id = Column(
3674 'pull_request_id', Integer(),
3688 'pull_request_id', Integer(),
3675 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3689 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3676 pull_request = relationship('PullRequest')
3690 pull_request = relationship('PullRequest')
3677
3691
3678 def __repr__(self):
3692 def __repr__(self):
3679 if self.pull_request_version_id:
3693 if self.pull_request_version_id:
3680 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
3694 return '<DB:PullRequestVersion #%s>' % self.pull_request_version_id
3681 else:
3695 else:
3682 return '<DB:PullRequestVersion at %#x>' % id(self)
3696 return '<DB:PullRequestVersion at %#x>' % id(self)
3683
3697
3684 @property
3698 @property
3685 def reviewers(self):
3699 def reviewers(self):
3686 return self.pull_request.reviewers
3700 return self.pull_request.reviewers
3687
3701
3688 @property
3702 @property
3689 def versions(self):
3703 def versions(self):
3690 return self.pull_request.versions
3704 return self.pull_request.versions
3691
3705
3692 def is_closed(self):
3706 def is_closed(self):
3693 # calculate from original
3707 # calculate from original
3694 return self.pull_request.status == self.STATUS_CLOSED
3708 return self.pull_request.status == self.STATUS_CLOSED
3695
3709
3696 def calculated_review_status(self):
3710 def calculated_review_status(self):
3697 return self.pull_request.calculated_review_status()
3711 return self.pull_request.calculated_review_status()
3698
3712
3699 def reviewers_statuses(self):
3713 def reviewers_statuses(self):
3700 return self.pull_request.reviewers_statuses()
3714 return self.pull_request.reviewers_statuses()
3701
3715
3702
3716
3703 class PullRequestReviewers(Base, BaseModel):
3717 class PullRequestReviewers(Base, BaseModel):
3704 __tablename__ = 'pull_request_reviewers'
3718 __tablename__ = 'pull_request_reviewers'
3705 __table_args__ = (
3719 __table_args__ = (
3706 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3720 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3707 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3721 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3708 )
3722 )
3709
3723
3710 @hybrid_property
3724 @hybrid_property
3711 def reasons(self):
3725 def reasons(self):
3712 if not self._reasons:
3726 if not self._reasons:
3713 return []
3727 return []
3714 return self._reasons
3728 return self._reasons
3715
3729
3716 @reasons.setter
3730 @reasons.setter
3717 def reasons(self, val):
3731 def reasons(self, val):
3718 val = val or []
3732 val = val or []
3719 if any(not isinstance(x, basestring) for x in val):
3733 if any(not isinstance(x, basestring) for x in val):
3720 raise Exception('invalid reasons type, must be list of strings')
3734 raise Exception('invalid reasons type, must be list of strings')
3721 self._reasons = val
3735 self._reasons = val
3722
3736
3723 pull_requests_reviewers_id = Column(
3737 pull_requests_reviewers_id = Column(
3724 'pull_requests_reviewers_id', Integer(), nullable=False,
3738 'pull_requests_reviewers_id', Integer(), nullable=False,
3725 primary_key=True)
3739 primary_key=True)
3726 pull_request_id = Column(
3740 pull_request_id = Column(
3727 "pull_request_id", Integer(),
3741 "pull_request_id", Integer(),
3728 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3742 ForeignKey('pull_requests.pull_request_id'), nullable=False)
3729 user_id = Column(
3743 user_id = Column(
3730 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
3744 "user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
3731 _reasons = Column(
3745 _reasons = Column(
3732 'reason', MutationList.as_mutable(
3746 'reason', MutationList.as_mutable(
3733 JsonType('list', dialect_map=dict(mysql=UnicodeText(16384)))))
3747 JsonType('list', dialect_map=dict(mysql=UnicodeText(16384)))))
3734 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
3748 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
3735 user = relationship('User')
3749 user = relationship('User')
3736 pull_request = relationship('PullRequest')
3750 pull_request = relationship('PullRequest')
3737
3751
3738
3752
3739 class Notification(Base, BaseModel):
3753 class Notification(Base, BaseModel):
3740 __tablename__ = 'notifications'
3754 __tablename__ = 'notifications'
3741 __table_args__ = (
3755 __table_args__ = (
3742 Index('notification_type_idx', 'type'),
3756 Index('notification_type_idx', 'type'),
3743 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3757 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3744 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3758 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
3745 )
3759 )
3746
3760
3747 TYPE_CHANGESET_COMMENT = u'cs_comment'
3761 TYPE_CHANGESET_COMMENT = u'cs_comment'
3748 TYPE_MESSAGE = u'message'
3762 TYPE_MESSAGE = u'message'
3749 TYPE_MENTION = u'mention'
3763 TYPE_MENTION = u'mention'
3750 TYPE_REGISTRATION = u'registration'
3764 TYPE_REGISTRATION = u'registration'
3751 TYPE_PULL_REQUEST = u'pull_request'
3765 TYPE_PULL_REQUEST = u'pull_request'
3752 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
3766 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
3753
3767
3754 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
3768 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
3755 subject = Column('subject', Unicode(512), nullable=True)
3769 subject = Column('subject', Unicode(512), nullable=True)
3756 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
3770 body = Column('body', UnicodeText().with_variant(UnicodeText(50000), 'mysql'), nullable=True)
3757 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
3771 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
3758 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3772 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3759 type_ = Column('type', Unicode(255))
3773 type_ = Column('type', Unicode(255))
3760
3774
3761 created_by_user = relationship('User')
3775 created_by_user = relationship('User')
3762 notifications_to_users = relationship('UserNotification', lazy='joined',
3776 notifications_to_users = relationship('UserNotification', lazy='joined',
3763 cascade="all, delete, delete-orphan")
3777 cascade="all, delete, delete-orphan")
3764
3778
3765 @property
3779 @property
3766 def recipients(self):
3780 def recipients(self):
3767 return [x.user for x in UserNotification.query()\
3781 return [x.user for x in UserNotification.query()\
3768 .filter(UserNotification.notification == self)\
3782 .filter(UserNotification.notification == self)\
3769 .order_by(UserNotification.user_id.asc()).all()]
3783 .order_by(UserNotification.user_id.asc()).all()]
3770
3784
3771 @classmethod
3785 @classmethod
3772 def create(cls, created_by, subject, body, recipients, type_=None):
3786 def create(cls, created_by, subject, body, recipients, type_=None):
3773 if type_ is None:
3787 if type_ is None:
3774 type_ = Notification.TYPE_MESSAGE
3788 type_ = Notification.TYPE_MESSAGE
3775
3789
3776 notification = cls()
3790 notification = cls()
3777 notification.created_by_user = created_by
3791 notification.created_by_user = created_by
3778 notification.subject = subject
3792 notification.subject = subject
3779 notification.body = body
3793 notification.body = body
3780 notification.type_ = type_
3794 notification.type_ = type_
3781 notification.created_on = datetime.datetime.now()
3795 notification.created_on = datetime.datetime.now()
3782
3796
3783 for u in recipients:
3797 for u in recipients:
3784 assoc = UserNotification()
3798 assoc = UserNotification()
3785 assoc.notification = notification
3799 assoc.notification = notification
3786
3800
3787 # if created_by is inside recipients mark his notification
3801 # if created_by is inside recipients mark his notification
3788 # as read
3802 # as read
3789 if u.user_id == created_by.user_id:
3803 if u.user_id == created_by.user_id:
3790 assoc.read = True
3804 assoc.read = True
3791
3805
3792 u.notifications.append(assoc)
3806 u.notifications.append(assoc)
3793 Session().add(notification)
3807 Session().add(notification)
3794
3808
3795 return notification
3809 return notification
3796
3810
3797
3811
3798 class UserNotification(Base, BaseModel):
3812 class UserNotification(Base, BaseModel):
3799 __tablename__ = 'user_to_notification'
3813 __tablename__ = 'user_to_notification'
3800 __table_args__ = (
3814 __table_args__ = (
3801 UniqueConstraint('user_id', 'notification_id'),
3815 UniqueConstraint('user_id', 'notification_id'),
3802 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3816 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3803 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3817 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3804 )
3818 )
3805 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
3819 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
3806 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
3820 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
3807 read = Column('read', Boolean, default=False)
3821 read = Column('read', Boolean, default=False)
3808 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
3822 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
3809
3823
3810 user = relationship('User', lazy="joined")
3824 user = relationship('User', lazy="joined")
3811 notification = relationship('Notification', lazy="joined",
3825 notification = relationship('Notification', lazy="joined",
3812 order_by=lambda: Notification.created_on.desc(),)
3826 order_by=lambda: Notification.created_on.desc(),)
3813
3827
3814 def mark_as_read(self):
3828 def mark_as_read(self):
3815 self.read = True
3829 self.read = True
3816 Session().add(self)
3830 Session().add(self)
3817
3831
3818
3832
3819 class Gist(Base, BaseModel):
3833 class Gist(Base, BaseModel):
3820 __tablename__ = 'gists'
3834 __tablename__ = 'gists'
3821 __table_args__ = (
3835 __table_args__ = (
3822 Index('g_gist_access_id_idx', 'gist_access_id'),
3836 Index('g_gist_access_id_idx', 'gist_access_id'),
3823 Index('g_created_on_idx', 'created_on'),
3837 Index('g_created_on_idx', 'created_on'),
3824 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3838 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3825 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3839 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3826 )
3840 )
3827 GIST_PUBLIC = u'public'
3841 GIST_PUBLIC = u'public'
3828 GIST_PRIVATE = u'private'
3842 GIST_PRIVATE = u'private'
3829 DEFAULT_FILENAME = u'gistfile1.txt'
3843 DEFAULT_FILENAME = u'gistfile1.txt'
3830
3844
3831 ACL_LEVEL_PUBLIC = u'acl_public'
3845 ACL_LEVEL_PUBLIC = u'acl_public'
3832 ACL_LEVEL_PRIVATE = u'acl_private'
3846 ACL_LEVEL_PRIVATE = u'acl_private'
3833
3847
3834 gist_id = Column('gist_id', Integer(), primary_key=True)
3848 gist_id = Column('gist_id', Integer(), primary_key=True)
3835 gist_access_id = Column('gist_access_id', Unicode(250))
3849 gist_access_id = Column('gist_access_id', Unicode(250))
3836 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
3850 gist_description = Column('gist_description', UnicodeText().with_variant(UnicodeText(1024), 'mysql'))
3837 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
3851 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
3838 gist_expires = Column('gist_expires', Float(53), nullable=False)
3852 gist_expires = Column('gist_expires', Float(53), nullable=False)
3839 gist_type = Column('gist_type', Unicode(128), nullable=False)
3853 gist_type = Column('gist_type', Unicode(128), nullable=False)
3840 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3854 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3841 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3855 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
3842 acl_level = Column('acl_level', Unicode(128), nullable=True)
3856 acl_level = Column('acl_level', Unicode(128), nullable=True)
3843
3857
3844 owner = relationship('User')
3858 owner = relationship('User')
3845
3859
3846 def __repr__(self):
3860 def __repr__(self):
3847 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
3861 return '<Gist:[%s]%s>' % (self.gist_type, self.gist_access_id)
3848
3862
3849 @hybrid_property
3863 @hybrid_property
3850 def description_safe(self):
3864 def description_safe(self):
3851 from rhodecode.lib import helpers as h
3865 from rhodecode.lib import helpers as h
3852 return h.escape(self.gist_description)
3866 return h.escape(self.gist_description)
3853
3867
3854 @classmethod
3868 @classmethod
3855 def get_or_404(cls, id_):
3869 def get_or_404(cls, id_):
3856 from pyramid.httpexceptions import HTTPNotFound
3870 from pyramid.httpexceptions import HTTPNotFound
3857
3871
3858 res = cls.query().filter(cls.gist_access_id == id_).scalar()
3872 res = cls.query().filter(cls.gist_access_id == id_).scalar()
3859 if not res:
3873 if not res:
3860 raise HTTPNotFound()
3874 raise HTTPNotFound()
3861 return res
3875 return res
3862
3876
3863 @classmethod
3877 @classmethod
3864 def get_by_access_id(cls, gist_access_id):
3878 def get_by_access_id(cls, gist_access_id):
3865 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
3879 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
3866
3880
3867 def gist_url(self):
3881 def gist_url(self):
3868 from rhodecode.model.gist import GistModel
3882 from rhodecode.model.gist import GistModel
3869 return GistModel().get_url(self)
3883 return GistModel().get_url(self)
3870
3884
3871 @classmethod
3885 @classmethod
3872 def base_path(cls):
3886 def base_path(cls):
3873 """
3887 """
3874 Returns base path when all gists are stored
3888 Returns base path when all gists are stored
3875
3889
3876 :param cls:
3890 :param cls:
3877 """
3891 """
3878 from rhodecode.model.gist import GIST_STORE_LOC
3892 from rhodecode.model.gist import GIST_STORE_LOC
3879 q = Session().query(RhodeCodeUi)\
3893 q = Session().query(RhodeCodeUi)\
3880 .filter(RhodeCodeUi.ui_key == URL_SEP)
3894 .filter(RhodeCodeUi.ui_key == URL_SEP)
3881 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
3895 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
3882 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
3896 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
3883
3897
3884 def get_api_data(self):
3898 def get_api_data(self):
3885 """
3899 """
3886 Common function for generating gist related data for API
3900 Common function for generating gist related data for API
3887 """
3901 """
3888 gist = self
3902 gist = self
3889 data = {
3903 data = {
3890 'gist_id': gist.gist_id,
3904 'gist_id': gist.gist_id,
3891 'type': gist.gist_type,
3905 'type': gist.gist_type,
3892 'access_id': gist.gist_access_id,
3906 'access_id': gist.gist_access_id,
3893 'description': gist.gist_description,
3907 'description': gist.gist_description,
3894 'url': gist.gist_url(),
3908 'url': gist.gist_url(),
3895 'expires': gist.gist_expires,
3909 'expires': gist.gist_expires,
3896 'created_on': gist.created_on,
3910 'created_on': gist.created_on,
3897 'modified_at': gist.modified_at,
3911 'modified_at': gist.modified_at,
3898 'content': None,
3912 'content': None,
3899 'acl_level': gist.acl_level,
3913 'acl_level': gist.acl_level,
3900 }
3914 }
3901 return data
3915 return data
3902
3916
3903 def __json__(self):
3917 def __json__(self):
3904 data = dict(
3918 data = dict(
3905 )
3919 )
3906 data.update(self.get_api_data())
3920 data.update(self.get_api_data())
3907 return data
3921 return data
3908 # SCM functions
3922 # SCM functions
3909
3923
3910 def scm_instance(self, **kwargs):
3924 def scm_instance(self, **kwargs):
3911 full_repo_path = os.path.join(self.base_path(), self.gist_access_id)
3925 full_repo_path = os.path.join(self.base_path(), self.gist_access_id)
3912 return get_vcs_instance(
3926 return get_vcs_instance(
3913 repo_path=safe_str(full_repo_path), create=False)
3927 repo_path=safe_str(full_repo_path), create=False)
3914
3928
3915
3929
3916 class ExternalIdentity(Base, BaseModel):
3930 class ExternalIdentity(Base, BaseModel):
3917 __tablename__ = 'external_identities'
3931 __tablename__ = 'external_identities'
3918 __table_args__ = (
3932 __table_args__ = (
3919 Index('local_user_id_idx', 'local_user_id'),
3933 Index('local_user_id_idx', 'local_user_id'),
3920 Index('external_id_idx', 'external_id'),
3934 Index('external_id_idx', 'external_id'),
3921 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3935 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3922 'mysql_charset': 'utf8'})
3936 'mysql_charset': 'utf8'})
3923
3937
3924 external_id = Column('external_id', Unicode(255), default=u'',
3938 external_id = Column('external_id', Unicode(255), default=u'',
3925 primary_key=True)
3939 primary_key=True)
3926 external_username = Column('external_username', Unicode(1024), default=u'')
3940 external_username = Column('external_username', Unicode(1024), default=u'')
3927 local_user_id = Column('local_user_id', Integer(),
3941 local_user_id = Column('local_user_id', Integer(),
3928 ForeignKey('users.user_id'), primary_key=True)
3942 ForeignKey('users.user_id'), primary_key=True)
3929 provider_name = Column('provider_name', Unicode(255), default=u'',
3943 provider_name = Column('provider_name', Unicode(255), default=u'',
3930 primary_key=True)
3944 primary_key=True)
3931 access_token = Column('access_token', String(1024), default=u'')
3945 access_token = Column('access_token', String(1024), default=u'')
3932 alt_token = Column('alt_token', String(1024), default=u'')
3946 alt_token = Column('alt_token', String(1024), default=u'')
3933 token_secret = Column('token_secret', String(1024), default=u'')
3947 token_secret = Column('token_secret', String(1024), default=u'')
3934
3948
3935 @classmethod
3949 @classmethod
3936 def by_external_id_and_provider(cls, external_id, provider_name,
3950 def by_external_id_and_provider(cls, external_id, provider_name,
3937 local_user_id=None):
3951 local_user_id=None):
3938 """
3952 """
3939 Returns ExternalIdentity instance based on search params
3953 Returns ExternalIdentity instance based on search params
3940
3954
3941 :param external_id:
3955 :param external_id:
3942 :param provider_name:
3956 :param provider_name:
3943 :return: ExternalIdentity
3957 :return: ExternalIdentity
3944 """
3958 """
3945 query = cls.query()
3959 query = cls.query()
3946 query = query.filter(cls.external_id == external_id)
3960 query = query.filter(cls.external_id == external_id)
3947 query = query.filter(cls.provider_name == provider_name)
3961 query = query.filter(cls.provider_name == provider_name)
3948 if local_user_id:
3962 if local_user_id:
3949 query = query.filter(cls.local_user_id == local_user_id)
3963 query = query.filter(cls.local_user_id == local_user_id)
3950 return query.first()
3964 return query.first()
3951
3965
3952 @classmethod
3966 @classmethod
3953 def user_by_external_id_and_provider(cls, external_id, provider_name):
3967 def user_by_external_id_and_provider(cls, external_id, provider_name):
3954 """
3968 """
3955 Returns User instance based on search params
3969 Returns User instance based on search params
3956
3970
3957 :param external_id:
3971 :param external_id:
3958 :param provider_name:
3972 :param provider_name:
3959 :return: User
3973 :return: User
3960 """
3974 """
3961 query = User.query()
3975 query = User.query()
3962 query = query.filter(cls.external_id == external_id)
3976 query = query.filter(cls.external_id == external_id)
3963 query = query.filter(cls.provider_name == provider_name)
3977 query = query.filter(cls.provider_name == provider_name)
3964 query = query.filter(User.user_id == cls.local_user_id)
3978 query = query.filter(User.user_id == cls.local_user_id)
3965 return query.first()
3979 return query.first()
3966
3980
3967 @classmethod
3981 @classmethod
3968 def by_local_user_id(cls, local_user_id):
3982 def by_local_user_id(cls, local_user_id):
3969 """
3983 """
3970 Returns all tokens for user
3984 Returns all tokens for user
3971
3985
3972 :param local_user_id:
3986 :param local_user_id:
3973 :return: ExternalIdentity
3987 :return: ExternalIdentity
3974 """
3988 """
3975 query = cls.query()
3989 query = cls.query()
3976 query = query.filter(cls.local_user_id == local_user_id)
3990 query = query.filter(cls.local_user_id == local_user_id)
3977 return query
3991 return query
3978
3992
3979
3993
3980 class Integration(Base, BaseModel):
3994 class Integration(Base, BaseModel):
3981 __tablename__ = 'integrations'
3995 __tablename__ = 'integrations'
3982 __table_args__ = (
3996 __table_args__ = (
3983 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3997 {'extend_existing': True, 'mysql_engine': 'InnoDB',
3984 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3998 'mysql_charset': 'utf8', 'sqlite_autoincrement': True}
3985 )
3999 )
3986
4000
3987 integration_id = Column('integration_id', Integer(), primary_key=True)
4001 integration_id = Column('integration_id', Integer(), primary_key=True)
3988 integration_type = Column('integration_type', String(255))
4002 integration_type = Column('integration_type', String(255))
3989 enabled = Column('enabled', Boolean(), nullable=False)
4003 enabled = Column('enabled', Boolean(), nullable=False)
3990 name = Column('name', String(255), nullable=False)
4004 name = Column('name', String(255), nullable=False)
3991 child_repos_only = Column('child_repos_only', Boolean(), nullable=False,
4005 child_repos_only = Column('child_repos_only', Boolean(), nullable=False,
3992 default=False)
4006 default=False)
3993
4007
3994 settings = Column(
4008 settings = Column(
3995 'settings_json', MutationObj.as_mutable(
4009 'settings_json', MutationObj.as_mutable(
3996 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4010 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
3997 repo_id = Column(
4011 repo_id = Column(
3998 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
4012 'repo_id', Integer(), ForeignKey('repositories.repo_id'),
3999 nullable=True, unique=None, default=None)
4013 nullable=True, unique=None, default=None)
4000 repo = relationship('Repository', lazy='joined')
4014 repo = relationship('Repository', lazy='joined')
4001
4015
4002 repo_group_id = Column(
4016 repo_group_id = Column(
4003 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
4017 'repo_group_id', Integer(), ForeignKey('groups.group_id'),
4004 nullable=True, unique=None, default=None)
4018 nullable=True, unique=None, default=None)
4005 repo_group = relationship('RepoGroup', lazy='joined')
4019 repo_group = relationship('RepoGroup', lazy='joined')
4006
4020
4007 @property
4021 @property
4008 def scope(self):
4022 def scope(self):
4009 if self.repo:
4023 if self.repo:
4010 return repr(self.repo)
4024 return repr(self.repo)
4011 if self.repo_group:
4025 if self.repo_group:
4012 if self.child_repos_only:
4026 if self.child_repos_only:
4013 return repr(self.repo_group) + ' (child repos only)'
4027 return repr(self.repo_group) + ' (child repos only)'
4014 else:
4028 else:
4015 return repr(self.repo_group) + ' (recursive)'
4029 return repr(self.repo_group) + ' (recursive)'
4016 if self.child_repos_only:
4030 if self.child_repos_only:
4017 return 'root_repos'
4031 return 'root_repos'
4018 return 'global'
4032 return 'global'
4019
4033
4020 def __repr__(self):
4034 def __repr__(self):
4021 return '<Integration(%r, %r)>' % (self.integration_type, self.scope)
4035 return '<Integration(%r, %r)>' % (self.integration_type, self.scope)
4022
4036
4023
4037
4024 class RepoReviewRuleUser(Base, BaseModel):
4038 class RepoReviewRuleUser(Base, BaseModel):
4025 __tablename__ = 'repo_review_rules_users'
4039 __tablename__ = 'repo_review_rules_users'
4026 __table_args__ = (
4040 __table_args__ = (
4027 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4041 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4028 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4042 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4029 )
4043 )
4030 repo_review_rule_user_id = Column('repo_review_rule_user_id', Integer(), primary_key=True)
4044 repo_review_rule_user_id = Column('repo_review_rule_user_id', Integer(), primary_key=True)
4031 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4045 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4032 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False)
4046 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False)
4033 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4047 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4034 user = relationship('User')
4048 user = relationship('User')
4035
4049
4036 def rule_data(self):
4050 def rule_data(self):
4037 return {
4051 return {
4038 'mandatory': self.mandatory
4052 'mandatory': self.mandatory
4039 }
4053 }
4040
4054
4041
4055
4042 class RepoReviewRuleUserGroup(Base, BaseModel):
4056 class RepoReviewRuleUserGroup(Base, BaseModel):
4043 __tablename__ = 'repo_review_rules_users_groups'
4057 __tablename__ = 'repo_review_rules_users_groups'
4044 __table_args__ = (
4058 __table_args__ = (
4045 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4059 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4046 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4060 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4047 )
4061 )
4048 repo_review_rule_users_group_id = Column('repo_review_rule_users_group_id', Integer(), primary_key=True)
4062 repo_review_rule_users_group_id = Column('repo_review_rule_users_group_id', Integer(), primary_key=True)
4049 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4063 repo_review_rule_id = Column("repo_review_rule_id", Integer(), ForeignKey('repo_review_rules.repo_review_rule_id'))
4050 users_group_id = Column("users_group_id", Integer(),ForeignKey('users_groups.users_group_id'), nullable=False)
4064 users_group_id = Column("users_group_id", Integer(),ForeignKey('users_groups.users_group_id'), nullable=False)
4051 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4065 mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
4052 users_group = relationship('UserGroup')
4066 users_group = relationship('UserGroup')
4053
4067
4054 def rule_data(self):
4068 def rule_data(self):
4055 return {
4069 return {
4056 'mandatory': self.mandatory
4070 'mandatory': self.mandatory
4057 }
4071 }
4058
4072
4059
4073
4060 class RepoReviewRule(Base, BaseModel):
4074 class RepoReviewRule(Base, BaseModel):
4061 __tablename__ = 'repo_review_rules'
4075 __tablename__ = 'repo_review_rules'
4062 __table_args__ = (
4076 __table_args__ = (
4063 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4077 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4064 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4078 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4065 )
4079 )
4066
4080
4067 repo_review_rule_id = Column(
4081 repo_review_rule_id = Column(
4068 'repo_review_rule_id', Integer(), primary_key=True)
4082 'repo_review_rule_id', Integer(), primary_key=True)
4069 repo_id = Column(
4083 repo_id = Column(
4070 "repo_id", Integer(), ForeignKey('repositories.repo_id'))
4084 "repo_id", Integer(), ForeignKey('repositories.repo_id'))
4071 repo = relationship('Repository', backref='review_rules')
4085 repo = relationship('Repository', backref='review_rules')
4072
4086
4073 _branch_pattern = Column("branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4087 _branch_pattern = Column("branch_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4074 _file_pattern = Column("file_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4088 _file_pattern = Column("file_pattern", UnicodeText().with_variant(UnicodeText(255), 'mysql'), default=u'*') # glob
4075
4089
4076 use_authors_for_review = Column("use_authors_for_review", Boolean(), nullable=False, default=False)
4090 use_authors_for_review = Column("use_authors_for_review", Boolean(), nullable=False, default=False)
4077 forbid_author_to_review = Column("forbid_author_to_review", Boolean(), nullable=False, default=False)
4091 forbid_author_to_review = Column("forbid_author_to_review", Boolean(), nullable=False, default=False)
4078 forbid_commit_author_to_review = Column("forbid_commit_author_to_review", Boolean(), nullable=False, default=False)
4092 forbid_commit_author_to_review = Column("forbid_commit_author_to_review", Boolean(), nullable=False, default=False)
4079 forbid_adding_reviewers = Column("forbid_adding_reviewers", Boolean(), nullable=False, default=False)
4093 forbid_adding_reviewers = Column("forbid_adding_reviewers", Boolean(), nullable=False, default=False)
4080
4094
4081 rule_users = relationship('RepoReviewRuleUser')
4095 rule_users = relationship('RepoReviewRuleUser')
4082 rule_user_groups = relationship('RepoReviewRuleUserGroup')
4096 rule_user_groups = relationship('RepoReviewRuleUserGroup')
4083
4097
4084 @hybrid_property
4098 @hybrid_property
4085 def branch_pattern(self):
4099 def branch_pattern(self):
4086 return self._branch_pattern or '*'
4100 return self._branch_pattern or '*'
4087
4101
4088 def _validate_glob(self, value):
4102 def _validate_glob(self, value):
4089 re.compile('^' + glob2re(value) + '$')
4103 re.compile('^' + glob2re(value) + '$')
4090
4104
4091 @branch_pattern.setter
4105 @branch_pattern.setter
4092 def branch_pattern(self, value):
4106 def branch_pattern(self, value):
4093 self._validate_glob(value)
4107 self._validate_glob(value)
4094 self._branch_pattern = value or '*'
4108 self._branch_pattern = value or '*'
4095
4109
4096 @hybrid_property
4110 @hybrid_property
4097 def file_pattern(self):
4111 def file_pattern(self):
4098 return self._file_pattern or '*'
4112 return self._file_pattern or '*'
4099
4113
4100 @file_pattern.setter
4114 @file_pattern.setter
4101 def file_pattern(self, value):
4115 def file_pattern(self, value):
4102 self._validate_glob(value)
4116 self._validate_glob(value)
4103 self._file_pattern = value or '*'
4117 self._file_pattern = value or '*'
4104
4118
4105 def matches(self, branch, files_changed):
4119 def matches(self, branch, files_changed):
4106 """
4120 """
4107 Check if this review rule matches a branch/files in a pull request
4121 Check if this review rule matches a branch/files in a pull request
4108
4122
4109 :param branch: branch name for the commit
4123 :param branch: branch name for the commit
4110 :param files_changed: list of file paths changed in the pull request
4124 :param files_changed: list of file paths changed in the pull request
4111 """
4125 """
4112
4126
4113 branch = branch or ''
4127 branch = branch or ''
4114 files_changed = files_changed or []
4128 files_changed = files_changed or []
4115
4129
4116 branch_matches = True
4130 branch_matches = True
4117 if branch:
4131 if branch:
4118 branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
4132 branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
4119 branch_matches = bool(branch_regex.search(branch))
4133 branch_matches = bool(branch_regex.search(branch))
4120
4134
4121 files_matches = True
4135 files_matches = True
4122 if self.file_pattern != '*':
4136 if self.file_pattern != '*':
4123 files_matches = False
4137 files_matches = False
4124 file_regex = re.compile(glob2re(self.file_pattern))
4138 file_regex = re.compile(glob2re(self.file_pattern))
4125 for filename in files_changed:
4139 for filename in files_changed:
4126 if file_regex.search(filename):
4140 if file_regex.search(filename):
4127 files_matches = True
4141 files_matches = True
4128 break
4142 break
4129
4143
4130 return branch_matches and files_matches
4144 return branch_matches and files_matches
4131
4145
4132 @property
4146 @property
4133 def review_users(self):
4147 def review_users(self):
4134 """ Returns the users which this rule applies to """
4148 """ Returns the users which this rule applies to """
4135
4149
4136 users = collections.OrderedDict()
4150 users = collections.OrderedDict()
4137
4151
4138 for rule_user in self.rule_users:
4152 for rule_user in self.rule_users:
4139 if rule_user.user.active:
4153 if rule_user.user.active:
4140 if rule_user.user not in users:
4154 if rule_user.user not in users:
4141 users[rule_user.user.username] = {
4155 users[rule_user.user.username] = {
4142 'user': rule_user.user,
4156 'user': rule_user.user,
4143 'source': 'user',
4157 'source': 'user',
4144 'source_data': {},
4158 'source_data': {},
4145 'data': rule_user.rule_data()
4159 'data': rule_user.rule_data()
4146 }
4160 }
4147
4161
4148 for rule_user_group in self.rule_user_groups:
4162 for rule_user_group in self.rule_user_groups:
4149 source_data = {
4163 source_data = {
4150 'name': rule_user_group.users_group.users_group_name,
4164 'name': rule_user_group.users_group.users_group_name,
4151 'members': len(rule_user_group.users_group.members)
4165 'members': len(rule_user_group.users_group.members)
4152 }
4166 }
4153 for member in rule_user_group.users_group.members:
4167 for member in rule_user_group.users_group.members:
4154 if member.user.active:
4168 if member.user.active:
4155 users[member.user.username] = {
4169 users[member.user.username] = {
4156 'user': member.user,
4170 'user': member.user,
4157 'source': 'user_group',
4171 'source': 'user_group',
4158 'source_data': source_data,
4172 'source_data': source_data,
4159 'data': rule_user_group.rule_data()
4173 'data': rule_user_group.rule_data()
4160 }
4174 }
4161
4175
4162 return users
4176 return users
4163
4177
4164 def __repr__(self):
4178 def __repr__(self):
4165 return '<RepoReviewerRule(id=%r, repo=%r)>' % (
4179 return '<RepoReviewerRule(id=%r, repo=%r)>' % (
4166 self.repo_review_rule_id, self.repo)
4180 self.repo_review_rule_id, self.repo)
4167
4181
4168
4182
4169 class DbMigrateVersion(Base, BaseModel):
4183 class DbMigrateVersion(Base, BaseModel):
4170 __tablename__ = 'db_migrate_version'
4184 __tablename__ = 'db_migrate_version'
4171 __table_args__ = (
4185 __table_args__ = (
4172 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4186 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4173 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
4187 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
4174 )
4188 )
4175 repository_id = Column('repository_id', String(250), primary_key=True)
4189 repository_id = Column('repository_id', String(250), primary_key=True)
4176 repository_path = Column('repository_path', Text)
4190 repository_path = Column('repository_path', Text)
4177 version = Column('version', Integer)
4191 version = Column('version', Integer)
4178
4192
4179
4193
4180 class DbSession(Base, BaseModel):
4194 class DbSession(Base, BaseModel):
4181 __tablename__ = 'db_session'
4195 __tablename__ = 'db_session'
4182 __table_args__ = (
4196 __table_args__ = (
4183 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4197 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4184 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
4198 'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
4185 )
4199 )
4186
4200
4187 def __repr__(self):
4201 def __repr__(self):
4188 return '<DB:DbSession({})>'.format(self.id)
4202 return '<DB:DbSession({})>'.format(self.id)
4189
4203
4190 id = Column('id', Integer())
4204 id = Column('id', Integer())
4191 namespace = Column('namespace', String(255), primary_key=True)
4205 namespace = Column('namespace', String(255), primary_key=True)
4192 accessed = Column('accessed', DateTime, nullable=False)
4206 accessed = Column('accessed', DateTime, nullable=False)
4193 created = Column('created', DateTime, nullable=False)
4207 created = Column('created', DateTime, nullable=False)
4194 data = Column('data', PickleType, nullable=False)
4208 data = Column('data', PickleType, nullable=False)
General Comments 0
You need to be logged in to leave comments. Login now