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