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