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