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