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