##// END OF EJS Templates
implements #222 registration feedback...
marcink -
r1731:31e6eb2f beta
parent child Browse files
Show More
@@ -0,0 +1,9 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="main.html"/>
3
4 A new user have registered in RhodeCode
5
6 ${body}
7
8
9 View this user here :${registered_user_url} No newline at end of file
@@ -1,167 +1,169 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.login
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Login controller for rhodeocode
7 7
8 8 :created_on: Apr 22, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import formencode
28 28
29 29 from formencode import htmlfill
30 30
31 31 from pylons.i18n.translation import _
32 32 from pylons.controllers.util import abort, redirect
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34
35 35 import rhodecode.lib.helpers as h
36 36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.model.db import User
39 39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 40 from rhodecode.model.user import UserModel
41 from rhodecode.model.meta import Session
41 42
42 43
43 44 log = logging.getLogger(__name__)
44 45
45 46
46 47 class LoginController(BaseController):
47 48
48 49 def __before__(self):
49 50 super(LoginController, self).__before__()
50 51
51 52 def index(self):
52 53 # redirect if already logged in
53 54 c.came_from = request.GET.get('came_from', None)
54 55
55 56 if self.rhodecode_user.is_authenticated \
56 57 and self.rhodecode_user.username != 'default':
57 58
58 59 return redirect(url('home'))
59 60
60 61 if request.POST:
61 62 # import Login Form validator class
62 63 login_form = LoginForm()
63 64 try:
64 65 c.form_result = login_form.to_python(dict(request.POST))
65 66 # form checks for username/password, now we're authenticated
66 67 username = c.form_result['username']
67 68 user = User.get_by_username(username, case_insensitive=True)
68 69 auth_user = AuthUser(user.user_id)
69 70 auth_user.set_authenticated()
70 71 cs = auth_user.get_cookie_store()
71 72 session['rhodecode_user'] = cs
72 73 session.save()
73 74
74 75 log.info('user %s is now authenticated and stored in '
75 76 'session, session attrs %s' % (username, cs))
76 77 user.update_lastlogin()
77 78
78 79 if c.came_from:
79 80 return redirect(c.came_from)
80 81 else:
81 82 return redirect(url('home'))
82 83
83 84 except formencode.Invalid, errors:
84 85 return htmlfill.render(
85 86 render('/login.html'),
86 87 defaults=errors.value,
87 88 errors=errors.error_dict or {},
88 89 prefix_error=False,
89 90 encoding="UTF-8")
90 91
91 92 return render('/login.html')
92 93
93 94 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
94 95 'hg.register.manual_activate')
95 96 def register(self):
96 97 user_model = UserModel()
97 98 c.auto_active = False
98 99 for perm in User.get_by_username('default').user_perms:
99 100 if perm.permission.permission_name == 'hg.register.auto_activate':
100 101 c.auto_active = True
101 102 break
102 103
103 104 if request.POST:
104 105
105 106 register_form = RegisterForm()()
106 107 try:
107 108 form_result = register_form.to_python(dict(request.POST))
108 109 form_result['active'] = c.auto_active
109 110 user_model.create_registration(form_result)
110 111 h.flash(_('You have successfully registered into rhodecode'),
111 112 category='success')
113 Session().commit()
112 114 return redirect(url('login_home'))
113 115
114 116 except formencode.Invalid, errors:
115 117 return htmlfill.render(
116 118 render('/register.html'),
117 119 defaults=errors.value,
118 120 errors=errors.error_dict or {},
119 121 prefix_error=False,
120 122 encoding="UTF-8")
121 123
122 124 return render('/register.html')
123 125
124 126 def password_reset(self):
125 127 user_model = UserModel()
126 128 if request.POST:
127 129
128 130 password_reset_form = PasswordResetForm()()
129 131 try:
130 132 form_result = password_reset_form.to_python(dict(request.POST))
131 133 user_model.reset_password_link(form_result)
132 134 h.flash(_('Your password reset link was sent'),
133 135 category='success')
134 136 return redirect(url('login_home'))
135 137
136 138 except formencode.Invalid, errors:
137 139 return htmlfill.render(
138 140 render('/password_reset.html'),
139 141 defaults=errors.value,
140 142 errors=errors.error_dict or {},
141 143 prefix_error=False,
142 144 encoding="UTF-8")
143 145
144 146 return render('/password_reset.html')
145 147
146 148 def password_reset_confirmation(self):
147 149
148 150 if request.GET and request.GET.get('key'):
149 151 try:
150 152 user_model = UserModel()
151 153 user = User.get_by_api_key(request.GET.get('key'))
152 154 data = dict(email=user.email)
153 155 user_model.reset_password(data)
154 156 h.flash(_('Your password reset was successful, '
155 157 'new password has been sent to your email'),
156 158 category='success')
157 159 except Exception, e:
158 160 log.error(e)
159 161 return redirect(url('reset_password'))
160 162
161 163 return redirect(url('login_home'))
162 164
163 165 def logout(self):
164 166 del session['rhodecode_user']
165 167 session.save()
166 168 log.info('Logging out and setting user as Empty')
167 169 redirect(url('home'))
@@ -1,1238 +1,1243 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.ext.hybrid import hybrid_property
33 33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 34 from beaker.cache import cache_region, region_invalidate
35 35
36 36 from vcs import get_backend
37 37 from vcs.utils.helpers import get_scm
38 38 from vcs.exceptions import VCSError
39 39 from vcs.utils.lazy import LazyProperty
40 40
41 41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
43 43 from rhodecode.lib.compat import json
44 44 from rhodecode.lib.caching_query import FromCache
45 45
46 46 from rhodecode.model.meta import Base, Session
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50 #==============================================================================
51 51 # BASE CLASSES
52 52 #==============================================================================
53 53
54 54 class ModelSerializer(json.JSONEncoder):
55 55 """
56 56 Simple Serializer for JSON,
57 57
58 58 usage::
59 59
60 60 to make object customized for serialization implement a __json__
61 61 method that will return a dict for serialization into json
62 62
63 63 example::
64 64
65 65 class Task(object):
66 66
67 67 def __init__(self, name, value):
68 68 self.name = name
69 69 self.value = value
70 70
71 71 def __json__(self):
72 72 return dict(name=self.name,
73 73 value=self.value)
74 74
75 75 """
76 76
77 77 def default(self, obj):
78 78
79 79 if hasattr(obj, '__json__'):
80 80 return obj.__json__()
81 81 else:
82 82 return json.JSONEncoder.default(self, obj)
83 83
84 84 class BaseModel(object):
85 85 """Base Model for all classess
86 86
87 87 """
88 88
89 89 @classmethod
90 90 def _get_keys(cls):
91 91 """return column names for this model """
92 92 return class_mapper(cls).c.keys()
93 93
94 94 def get_dict(self):
95 95 """return dict with keys and values corresponding
96 96 to this model data """
97 97
98 98 d = {}
99 99 for k in self._get_keys():
100 100 d[k] = getattr(self, k)
101 101 return d
102 102
103 103 def get_appstruct(self):
104 104 """return list with keys and values tupples corresponding
105 105 to this model data """
106 106
107 107 l = []
108 108 for k in self._get_keys():
109 109 l.append((k, getattr(self, k),))
110 110 return l
111 111
112 112 def populate_obj(self, populate_dict):
113 113 """populate model with data from given populate_dict"""
114 114
115 115 for k in self._get_keys():
116 116 if k in populate_dict:
117 117 setattr(self, k, populate_dict[k])
118 118
119 119 @classmethod
120 120 def query(cls):
121 121 return Session().query(cls)
122 122
123 123 @classmethod
124 124 def get(cls, id_):
125 125 if id_:
126 126 return cls.query().get(id_)
127 127
128 128 @classmethod
129 129 def getAll(cls):
130 130 return cls.query().all()
131 131
132 132 @classmethod
133 133 def delete(cls, id_):
134 134 obj = cls.query().get(id_)
135 135 Session().delete(obj)
136 136
137 137
138 138 class RhodeCodeSetting(Base, BaseModel):
139 139 __tablename__ = 'rhodecode_settings'
140 140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 143 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144 144
145 145 def __init__(self, k='', v=''):
146 146 self.app_settings_name = k
147 147 self.app_settings_value = v
148 148
149 149
150 150 @validates('_app_settings_value')
151 151 def validate_settings_value(self, key, val):
152 152 assert type(val) == unicode
153 153 return val
154 154
155 155 @hybrid_property
156 156 def app_settings_value(self):
157 157 v = self._app_settings_value
158 158 if v == 'ldap_active':
159 159 v = str2bool(v)
160 160 return v
161 161
162 162 @app_settings_value.setter
163 163 def app_settings_value(self, val):
164 164 """
165 165 Setter that will always make sure we use unicode in app_settings_value
166 166
167 167 :param val:
168 168 """
169 169 self._app_settings_value = safe_unicode(val)
170 170
171 171 def __repr__(self):
172 172 return "<%s('%s:%s')>" % (self.__class__.__name__,
173 173 self.app_settings_name, self.app_settings_value)
174 174
175 175
176 176 @classmethod
177 177 def get_by_name(cls, ldap_key):
178 178 return cls.query()\
179 179 .filter(cls.app_settings_name == ldap_key).scalar()
180 180
181 181 @classmethod
182 182 def get_app_settings(cls, cache=False):
183 183
184 184 ret = cls.query()
185 185
186 186 if cache:
187 187 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
188 188
189 189 if not ret:
190 190 raise Exception('Could not get application settings !')
191 191 settings = {}
192 192 for each in ret:
193 193 settings['rhodecode_' + each.app_settings_name] = \
194 194 each.app_settings_value
195 195
196 196 return settings
197 197
198 198 @classmethod
199 199 def get_ldap_settings(cls, cache=False):
200 200 ret = cls.query()\
201 201 .filter(cls.app_settings_name.startswith('ldap_')).all()
202 202 fd = {}
203 203 for row in ret:
204 204 fd.update({row.app_settings_name:row.app_settings_value})
205 205
206 206 return fd
207 207
208 208
209 209 class RhodeCodeUi(Base, BaseModel):
210 210 __tablename__ = 'rhodecode_ui'
211 211 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
212 212
213 213 HOOK_UPDATE = 'changegroup.update'
214 214 HOOK_REPO_SIZE = 'changegroup.repo_size'
215 215 HOOK_PUSH = 'pretxnchangegroup.push_logger'
216 216 HOOK_PULL = 'preoutgoing.pull_logger'
217 217
218 218 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
219 219 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 220 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
221 221 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
222 222 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
223 223
224 224
225 225 @classmethod
226 226 def get_by_key(cls, key):
227 227 return cls.query().filter(cls.ui_key == key)
228 228
229 229
230 230 @classmethod
231 231 def get_builtin_hooks(cls):
232 232 q = cls.query()
233 233 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
234 234 cls.HOOK_REPO_SIZE,
235 235 cls.HOOK_PUSH, cls.HOOK_PULL]))
236 236 return q.all()
237 237
238 238 @classmethod
239 239 def get_custom_hooks(cls):
240 240 q = cls.query()
241 241 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
242 242 cls.HOOK_REPO_SIZE,
243 243 cls.HOOK_PUSH, cls.HOOK_PULL]))
244 244 q = q.filter(cls.ui_section == 'hooks')
245 245 return q.all()
246 246
247 247 @classmethod
248 248 def create_or_update_hook(cls, key, val):
249 249 new_ui = cls.get_by_key(key).scalar() or cls()
250 250 new_ui.ui_section = 'hooks'
251 251 new_ui.ui_active = True
252 252 new_ui.ui_key = key
253 253 new_ui.ui_value = val
254 254
255 255 Session().add(new_ui)
256 256 Session().commit()
257 257
258 258
259 259 class User(Base, BaseModel):
260 260 __tablename__ = 'users'
261 261 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
262 262 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 263 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 264 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 265 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
266 266 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
267 267 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 268 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 269 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 270 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
271 271 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 272 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 273
274 274 user_log = relationship('UserLog', cascade='all')
275 275 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
276 276
277 277 repositories = relationship('Repository')
278 278 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
279 279 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
280 280
281 281 group_member = relationship('UsersGroupMember', cascade='all')
282 282
283 283 notifications = relationship('UserNotification',)
284 284
285 285 @property
286 def full_name(self):
287 return '%s %s' % (self.name, self.lastname)
288
289 @property
286 290 def full_contact(self):
287 291 return '%s %s <%s>' % (self.name, self.lastname, self.email)
288 292
289 293 @property
290 294 def short_contact(self):
291 295 return '%s %s' % (self.name, self.lastname)
292 296
293 297 @property
294 298 def is_admin(self):
295 299 return self.admin
296 300
297 301 def __repr__(self):
298 302 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
299 303 self.user_id, self.username)
300 304
301 305
302 306 @classmethod
303 307 def get_by_username(cls, username, case_insensitive=False, cache=False):
304 308 if case_insensitive:
305 309 q = cls.query().filter(cls.username.ilike(username))
306 310 else:
307 311 q = cls.query().filter(cls.username == username)
308 312
309 313 if cache:
310 314 q = q.options(FromCache("sql_cache_short",
311 315 "get_user_%s" % username))
312 316 return q.scalar()
313 317
314 318 @classmethod
315 319 def get_by_api_key(cls, api_key, cache=False):
316 320 q = cls.query().filter(cls.api_key == api_key)
317 321
318 322 if cache:
319 323 q = q.options(FromCache("sql_cache_short",
320 324 "get_api_key_%s" % api_key))
321 325 return q.scalar()
322 326
323 327 @classmethod
324 328 def get_by_email(cls, email, cache=False):
325 329 q = cls.query().filter(cls.email == email)
326 330
327 331 if cache:
328 332 q = q.options(FromCache("sql_cache_short",
329 333 "get_api_key_%s" % email))
330 334 return q.scalar()
331 335
332 336 def update_lastlogin(self):
333 337 """Update user lastlogin"""
334 338
335 339 self.last_login = datetime.datetime.now()
336 340 Session().add(self)
337 341 Session().commit()
338 342 log.debug('updated user %s lastlogin', self.username)
339 343
340 344
341 345 class UserLog(Base, BaseModel):
342 346 __tablename__ = 'user_logs'
343 347 __table_args__ = {'extend_existing':True}
344 348 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
345 349 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
346 350 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
347 351 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 352 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
349 353 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
350 354 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
351 355
352 356 @property
353 357 def action_as_day(self):
354 358 return datetime.date(*self.action_date.timetuple()[:3])
355 359
356 360 user = relationship('User')
357 361 repository = relationship('Repository')
358 362
359 363
360 364 class UsersGroup(Base, BaseModel):
361 365 __tablename__ = 'users_groups'
362 366 __table_args__ = {'extend_existing':True}
363 367
364 368 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
365 369 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
366 370 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
367 371
368 372 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
369 373
370 374 def __repr__(self):
371 375 return '<userGroup(%s)>' % (self.users_group_name)
372 376
373 377 @classmethod
374 378 def get_by_group_name(cls, group_name, cache=False,
375 379 case_insensitive=False):
376 380 if case_insensitive:
377 381 q = cls.query().filter(cls.users_group_name.ilike(group_name))
378 382 else:
379 383 q = cls.query().filter(cls.users_group_name == group_name)
380 384 if cache:
381 385 q = q.options(FromCache("sql_cache_short",
382 386 "get_user_%s" % group_name))
383 387 return q.scalar()
384 388
385 389
386 390 @classmethod
387 391 def get(cls, users_group_id, cache=False):
388 392 users_group = cls.query()
389 393 if cache:
390 394 users_group = users_group.options(FromCache("sql_cache_short",
391 395 "get_users_group_%s" % users_group_id))
392 396 return users_group.get(users_group_id)
393 397
394 398 @classmethod
395 399 def create(cls, form_data):
396 400 try:
397 401 new_users_group = cls()
398 402 for k, v in form_data.items():
399 403 setattr(new_users_group, k, v)
400 404
401 405 Session().add(new_users_group)
402 406 Session().commit()
403 407 return new_users_group
404 408 except:
405 409 log.error(traceback.format_exc())
406 410 Session().rollback()
407 411 raise
408 412
409 413 @classmethod
410 414 def update(cls, users_group_id, form_data):
411 415
412 416 try:
413 417 users_group = cls.get(users_group_id, cache=False)
414 418
415 419 for k, v in form_data.items():
416 420 if k == 'users_group_members':
417 421 users_group.members = []
418 422 Session().flush()
419 423 members_list = []
420 424 if v:
421 425 v = [v] if isinstance(v, basestring) else v
422 426 for u_id in set(v):
423 427 member = UsersGroupMember(users_group_id, u_id)
424 428 members_list.append(member)
425 429 setattr(users_group, 'members', members_list)
426 430 setattr(users_group, k, v)
427 431
428 432 Session().add(users_group)
429 433 Session().commit()
430 434 except:
431 435 log.error(traceback.format_exc())
432 436 Session().rollback()
433 437 raise
434 438
435 439 @classmethod
436 440 def delete(cls, users_group_id):
437 441 try:
438 442
439 443 # check if this group is not assigned to repo
440 444 assigned_groups = UsersGroupRepoToPerm.query()\
441 445 .filter(UsersGroupRepoToPerm.users_group_id ==
442 446 users_group_id).all()
443 447
444 448 if assigned_groups:
445 449 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
446 450 assigned_groups)
447 451
448 452 users_group = cls.get(users_group_id, cache=False)
449 453 Session().delete(users_group)
450 454 Session().commit()
451 455 except:
452 456 log.error(traceback.format_exc())
453 457 Session().rollback()
454 458 raise
455 459
456 460 class UsersGroupMember(Base, BaseModel):
457 461 __tablename__ = 'users_groups_members'
458 462 __table_args__ = {'extend_existing':True}
459 463
460 464 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
461 465 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
462 466 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463 467
464 468 user = relationship('User', lazy='joined')
465 469 users_group = relationship('UsersGroup')
466 470
467 471 def __init__(self, gr_id='', u_id=''):
468 472 self.users_group_id = gr_id
469 473 self.user_id = u_id
470 474
471 475 @staticmethod
472 476 def add_user_to_group(group, user):
473 477 ugm = UsersGroupMember()
474 478 ugm.users_group = group
475 479 ugm.user = user
476 480 Session().add(ugm)
477 481 Session().commit()
478 482 return ugm
479 483
480 484 class Repository(Base, BaseModel):
481 485 __tablename__ = 'repositories'
482 486 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
483 487
484 488 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
485 489 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
486 490 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
487 491 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
488 492 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
489 493 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
490 494 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
491 495 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
492 496 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
493 497 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
494 498
495 499 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
496 500 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
497 501
498 502
499 503 user = relationship('User')
500 504 fork = relationship('Repository', remote_side=repo_id)
501 505 group = relationship('RepoGroup')
502 506 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
503 507 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
504 508 stats = relationship('Statistics', cascade='all', uselist=False)
505 509
506 510 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
507 511
508 512 logs = relationship('UserLog', cascade='all')
509 513
510 514 def __repr__(self):
511 515 return "<%s('%s:%s')>" % (self.__class__.__name__,
512 516 self.repo_id, self.repo_name)
513 517
514 518 @classmethod
515 519 def url_sep(cls):
516 520 return '/'
517 521
518 522 @classmethod
519 523 def get_by_repo_name(cls, repo_name):
520 524 q = Session().query(cls).filter(cls.repo_name == repo_name)
521 525 q = q.options(joinedload(Repository.fork))\
522 526 .options(joinedload(Repository.user))\
523 527 .options(joinedload(Repository.group))
524 528 return q.one()
525 529
526 530 @classmethod
527 531 def get_repo_forks(cls, repo_id):
528 532 return cls.query().filter(Repository.fork_id == repo_id)
529 533
530 534 @classmethod
531 535 def base_path(cls):
532 536 """
533 537 Returns base path when all repos are stored
534 538
535 539 :param cls:
536 540 """
537 541 q = Session().query(RhodeCodeUi)\
538 542 .filter(RhodeCodeUi.ui_key == cls.url_sep())
539 543 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
540 544 return q.one().ui_value
541 545
542 546 @property
543 547 def just_name(self):
544 548 return self.repo_name.split(Repository.url_sep())[-1]
545 549
546 550 @property
547 551 def groups_with_parents(self):
548 552 groups = []
549 553 if self.group is None:
550 554 return groups
551 555
552 556 cur_gr = self.group
553 557 groups.insert(0, cur_gr)
554 558 while 1:
555 559 gr = getattr(cur_gr, 'parent_group', None)
556 560 cur_gr = cur_gr.parent_group
557 561 if gr is None:
558 562 break
559 563 groups.insert(0, gr)
560 564
561 565 return groups
562 566
563 567 @property
564 568 def groups_and_repo(self):
565 569 return self.groups_with_parents, self.just_name
566 570
567 571 @LazyProperty
568 572 def repo_path(self):
569 573 """
570 574 Returns base full path for that repository means where it actually
571 575 exists on a filesystem
572 576 """
573 577 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
574 578 Repository.url_sep())
575 579 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
576 580 return q.one().ui_value
577 581
578 582 @property
579 583 def repo_full_path(self):
580 584 p = [self.repo_path]
581 585 # we need to split the name by / since this is how we store the
582 586 # names in the database, but that eventually needs to be converted
583 587 # into a valid system path
584 588 p += self.repo_name.split(Repository.url_sep())
585 589 return os.path.join(*p)
586 590
587 591 def get_new_name(self, repo_name):
588 592 """
589 593 returns new full repository name based on assigned group and new new
590 594
591 595 :param group_name:
592 596 """
593 597 path_prefix = self.group.full_path_splitted if self.group else []
594 598 return Repository.url_sep().join(path_prefix + [repo_name])
595 599
596 600 @property
597 601 def _ui(self):
598 602 """
599 603 Creates an db based ui object for this repository
600 604 """
601 605 from mercurial import ui
602 606 from mercurial import config
603 607 baseui = ui.ui()
604 608
605 609 #clean the baseui object
606 610 baseui._ocfg = config.config()
607 611 baseui._ucfg = config.config()
608 612 baseui._tcfg = config.config()
609 613
610 614
611 615 ret = RhodeCodeUi.query()\
612 616 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
613 617
614 618 hg_ui = ret
615 619 for ui_ in hg_ui:
616 620 if ui_.ui_active:
617 621 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
618 622 ui_.ui_key, ui_.ui_value)
619 623 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
620 624
621 625 return baseui
622 626
623 627 @classmethod
624 628 def is_valid(cls, repo_name):
625 629 """
626 630 returns True if given repo name is a valid filesystem repository
627 631
628 632 @param cls:
629 633 @param repo_name:
630 634 """
631 635 from rhodecode.lib.utils import is_valid_repo
632 636
633 637 return is_valid_repo(repo_name, cls.base_path())
634 638
635 639
636 640 #==========================================================================
637 641 # SCM PROPERTIES
638 642 #==========================================================================
639 643
640 644 def get_changeset(self, rev):
641 645 return get_changeset_safe(self.scm_instance, rev)
642 646
643 647 @property
644 648 def tip(self):
645 649 return self.get_changeset('tip')
646 650
647 651 @property
648 652 def author(self):
649 653 return self.tip.author
650 654
651 655 @property
652 656 def last_change(self):
653 657 return self.scm_instance.last_change
654 658
655 659 #==========================================================================
656 660 # SCM CACHE INSTANCE
657 661 #==========================================================================
658 662
659 663 @property
660 664 def invalidate(self):
661 665 return CacheInvalidation.invalidate(self.repo_name)
662 666
663 667 def set_invalidate(self):
664 668 """
665 669 set a cache for invalidation for this instance
666 670 """
667 671 CacheInvalidation.set_invalidate(self.repo_name)
668 672
669 673 @LazyProperty
670 674 def scm_instance(self):
671 675 return self.__get_instance()
672 676
673 677 @property
674 678 def scm_instance_cached(self):
675 679 @cache_region('long_term')
676 680 def _c(repo_name):
677 681 return self.__get_instance()
678 682 rn = self.repo_name
679 683
680 684 inv = self.invalidate
681 685 if inv is not None:
682 686 region_invalidate(_c, None, rn)
683 687 # update our cache
684 688 CacheInvalidation.set_valid(inv.cache_key)
685 689 return _c(rn)
686 690
687 691 def __get_instance(self):
688 692
689 693 repo_full_path = self.repo_full_path
690 694
691 695 try:
692 696 alias = get_scm(repo_full_path)[0]
693 697 log.debug('Creating instance of %s repository', alias)
694 698 backend = get_backend(alias)
695 699 except VCSError:
696 700 log.error(traceback.format_exc())
697 701 log.error('Perhaps this repository is in db and not in '
698 702 'filesystem run rescan repositories with '
699 703 '"destroy old data " option from admin panel')
700 704 return
701 705
702 706 if alias == 'hg':
703 707
704 708 repo = backend(safe_str(repo_full_path), create=False,
705 709 baseui=self._ui)
706 710 # skip hidden web repository
707 711 if repo._get_hidden():
708 712 return
709 713 else:
710 714 repo = backend(repo_full_path, create=False)
711 715
712 716 return repo
713 717
714 718
715 719 class RepoGroup(Base, BaseModel):
716 720 __tablename__ = 'groups'
717 721 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
718 722 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
719 723 __mapper_args__ = {'order_by':'group_name'}
720 724
721 725 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
722 726 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
723 727 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
724 728 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
725 729
726 730 parent_group = relationship('RepoGroup', remote_side=group_id)
727 731
728 732
729 733 def __init__(self, group_name='', parent_group=None):
730 734 self.group_name = group_name
731 735 self.parent_group = parent_group
732 736
733 737 def __repr__(self):
734 738 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
735 739 self.group_name)
736 740
737 741 @classmethod
738 742 def groups_choices(cls):
739 743 from webhelpers.html import literal as _literal
740 744 repo_groups = [('', '')]
741 745 sep = ' &raquo; '
742 746 _name = lambda k: _literal(sep.join(k))
743 747
744 748 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
745 749 for x in cls.query().all()])
746 750
747 751 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
748 752 return repo_groups
749 753
750 754 @classmethod
751 755 def url_sep(cls):
752 756 return '/'
753 757
754 758 @classmethod
755 759 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
756 760 if case_insensitive:
757 761 gr = cls.query()\
758 762 .filter(cls.group_name.ilike(group_name))
759 763 else:
760 764 gr = cls.query()\
761 765 .filter(cls.group_name == group_name)
762 766 if cache:
763 767 gr = gr.options(FromCache("sql_cache_short",
764 768 "get_group_%s" % group_name))
765 769 return gr.scalar()
766 770
767 771 @property
768 772 def parents(self):
769 773 parents_recursion_limit = 5
770 774 groups = []
771 775 if self.parent_group is None:
772 776 return groups
773 777 cur_gr = self.parent_group
774 778 groups.insert(0, cur_gr)
775 779 cnt = 0
776 780 while 1:
777 781 cnt += 1
778 782 gr = getattr(cur_gr, 'parent_group', None)
779 783 cur_gr = cur_gr.parent_group
780 784 if gr is None:
781 785 break
782 786 if cnt == parents_recursion_limit:
783 787 # this will prevent accidental infinit loops
784 788 log.error('group nested more than %s' %
785 789 parents_recursion_limit)
786 790 break
787 791
788 792 groups.insert(0, gr)
789 793 return groups
790 794
791 795 @property
792 796 def children(self):
793 797 return RepoGroup.query().filter(RepoGroup.parent_group == self)
794 798
795 799 @property
796 800 def name(self):
797 801 return self.group_name.split(RepoGroup.url_sep())[-1]
798 802
799 803 @property
800 804 def full_path(self):
801 805 return self.group_name
802 806
803 807 @property
804 808 def full_path_splitted(self):
805 809 return self.group_name.split(RepoGroup.url_sep())
806 810
807 811 @property
808 812 def repositories(self):
809 813 return Repository.query().filter(Repository.group == self)
810 814
811 815 @property
812 816 def repositories_recursive_count(self):
813 817 cnt = self.repositories.count()
814 818
815 819 def children_count(group):
816 820 cnt = 0
817 821 for child in group.children:
818 822 cnt += child.repositories.count()
819 823 cnt += children_count(child)
820 824 return cnt
821 825
822 826 return cnt + children_count(self)
823 827
824 828
825 829 def get_new_name(self, group_name):
826 830 """
827 831 returns new full group name based on parent and new name
828 832
829 833 :param group_name:
830 834 """
831 835 path_prefix = (self.parent_group.full_path_splitted if
832 836 self.parent_group else [])
833 837 return RepoGroup.url_sep().join(path_prefix + [group_name])
834 838
835 839
836 840 class Permission(Base, BaseModel):
837 841 __tablename__ = 'permissions'
838 842 __table_args__ = {'extend_existing':True}
839 843 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
840 844 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
841 845 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
842 846
843 847 def __repr__(self):
844 848 return "<%s('%s:%s')>" % (self.__class__.__name__,
845 849 self.permission_id, self.permission_name)
846 850
847 851 @classmethod
848 852 def get_by_key(cls, key):
849 853 return cls.query().filter(cls.permission_name == key).scalar()
850 854
851 855 @classmethod
852 856 def get_default_perms(cls, default_user_id, cache=True):
853 857 q = Session().query(UserRepoToPerm, Repository, cls)\
854 858 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
855 859 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
856 860 .filter(UserRepoToPerm.user_id == default_user_id)
857 861 if cache:
858 862 q = q.options(FromCache("sql_cache_short", "get_default_perms"))
859 863
860 864 return q.all()
861 865
862 866
863 867 class UserRepoToPerm(Base, BaseModel):
864 868 __tablename__ = 'repo_to_perm'
865 869 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
866 870 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
867 871 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
868 872 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
869 873 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
870 874
871 875 user = relationship('User')
872 876 permission = relationship('Permission')
873 877 repository = relationship('Repository')
874 878
875 879 @classmethod
876 880 def create(cls, user, repository, permission):
877 881 n = cls()
878 882 n.user = user
879 883 n.repository = repository
880 884 n.permission = permission
881 885 Session().add(n)
882 886 return n
883 887
884 888 def __repr__(self):
885 889 return '<user:%s => %s >' % (self.user, self.repository)
886 890
887 891 class UserToPerm(Base, BaseModel):
888 892 __tablename__ = 'user_to_perm'
889 893 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
890 894 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
891 895 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
892 896 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
893 897
894 898 user = relationship('User')
895 899 permission = relationship('Permission', lazy='joined')
896 900
897 901 @classmethod
898 902 def has_perm(cls, user_id, perm):
899 903 if not isinstance(perm, Permission):
900 904 raise Exception('perm needs to be an instance of Permission class')
901 905
902 906 return cls.query().filter(cls.user_id == user_id)\
903 907 .filter(cls.permission == perm).scalar() is not None
904 908
905 909 @classmethod
906 910 def grant_perm(cls, user_id, perm):
907 911 if not isinstance(perm, Permission):
908 912 raise Exception('perm needs to be an instance of Permission class')
909 913
910 914 new = cls()
911 915 new.user_id = user_id
912 916 new.permission = perm
913 917 try:
914 918 Session().add(new)
915 919 Session().commit()
916 920 except:
917 921 Session().rollback()
918 922
919 923
920 924 @classmethod
921 925 def revoke_perm(cls, user_id, perm):
922 926 if not isinstance(perm, Permission):
923 927 raise Exception('perm needs to be an instance of Permission class')
924 928
925 929 try:
926 930 obj = cls.query().filter(cls.user_id == user_id)\
927 931 .filter(cls.permission == perm).one()
928 932 Session().delete(obj)
929 933 Session().commit()
930 934 except:
931 935 Session().rollback()
932 936
933 937 class UsersGroupRepoToPerm(Base, BaseModel):
934 938 __tablename__ = 'users_group_repo_to_perm'
935 939 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
936 940 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
937 941 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
938 942 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
939 943 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
940 944
941 945 users_group = relationship('UsersGroup')
942 946 permission = relationship('Permission')
943 947 repository = relationship('Repository')
944 948
945 949 @classmethod
946 950 def create(cls, users_group, repository, permission):
947 951 n = cls()
948 952 n.users_group = users_group
949 953 n.repository = repository
950 954 n.permission = permission
951 955 Session().add(n)
952 956 return n
953 957
954 958 def __repr__(self):
955 959 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
956 960
957 961 class UsersGroupToPerm(Base, BaseModel):
958 962 __tablename__ = 'users_group_to_perm'
959 963 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
960 964 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
961 965 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
962 966
963 967 users_group = relationship('UsersGroup')
964 968 permission = relationship('Permission')
965 969
966 970
967 971 @classmethod
968 972 def has_perm(cls, users_group_id, perm):
969 973 if not isinstance(perm, Permission):
970 974 raise Exception('perm needs to be an instance of Permission class')
971 975
972 976 return cls.query().filter(cls.users_group_id ==
973 977 users_group_id)\
974 978 .filter(cls.permission == perm)\
975 979 .scalar() is not None
976 980
977 981 @classmethod
978 982 def grant_perm(cls, users_group_id, perm):
979 983 if not isinstance(perm, Permission):
980 984 raise Exception('perm needs to be an instance of Permission class')
981 985
982 986 new = cls()
983 987 new.users_group_id = users_group_id
984 988 new.permission = perm
985 989 try:
986 990 Session().add(new)
987 991 Session().commit()
988 992 except:
989 993 Session().rollback()
990 994
991 995
992 996 @classmethod
993 997 def revoke_perm(cls, users_group_id, perm):
994 998 if not isinstance(perm, Permission):
995 999 raise Exception('perm needs to be an instance of Permission class')
996 1000
997 1001 try:
998 1002 obj = cls.query().filter(cls.users_group_id == users_group_id)\
999 1003 .filter(cls.permission == perm).one()
1000 1004 Session().delete(obj)
1001 1005 Session().commit()
1002 1006 except:
1003 1007 Session().rollback()
1004 1008
1005 1009
1006 1010 class UserRepoGroupToPerm(Base, BaseModel):
1007 1011 __tablename__ = 'group_to_perm'
1008 1012 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
1009 1013
1010 1014 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1011 1015 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1012 1016 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1013 1017 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1014 1018
1015 1019 user = relationship('User')
1016 1020 permission = relationship('Permission')
1017 1021 group = relationship('RepoGroup')
1018 1022
1019 1023 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1020 1024 __tablename__ = 'users_group_repo_group_to_perm'
1021 1025 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
1022 1026
1023 1027 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)
1024 1028 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1025 1029 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1026 1030 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1027 1031
1028 1032 users_group = relationship('UsersGroup')
1029 1033 permission = relationship('Permission')
1030 1034 group = relationship('RepoGroup')
1031 1035
1032 1036 class Statistics(Base, BaseModel):
1033 1037 __tablename__ = 'statistics'
1034 1038 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
1035 1039 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1036 1040 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1037 1041 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1038 1042 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1039 1043 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1040 1044 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1041 1045
1042 1046 repository = relationship('Repository', single_parent=True)
1043 1047
1044 1048 class UserFollowing(Base, BaseModel):
1045 1049 __tablename__ = 'user_followings'
1046 1050 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1047 1051 UniqueConstraint('user_id', 'follows_user_id')
1048 1052 , {'extend_existing':True})
1049 1053
1050 1054 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1051 1055 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1052 1056 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1053 1057 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1054 1058 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1055 1059
1056 1060 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1057 1061
1058 1062 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1059 1063 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1060 1064
1061 1065
1062 1066 @classmethod
1063 1067 def get_repo_followers(cls, repo_id):
1064 1068 return cls.query().filter(cls.follows_repo_id == repo_id)
1065 1069
1066 1070 class CacheInvalidation(Base, BaseModel):
1067 1071 __tablename__ = 'cache_invalidation'
1068 1072 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1069 1073 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1070 1074 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1071 1075 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1072 1076 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1073 1077
1074 1078
1075 1079 def __init__(self, cache_key, cache_args=''):
1076 1080 self.cache_key = cache_key
1077 1081 self.cache_args = cache_args
1078 1082 self.cache_active = False
1079 1083
1080 1084 def __repr__(self):
1081 1085 return "<%s('%s:%s')>" % (self.__class__.__name__,
1082 1086 self.cache_id, self.cache_key)
1083 1087
1084 1088 @classmethod
1085 1089 def invalidate(cls, key):
1086 1090 """
1087 1091 Returns Invalidation object if this given key should be invalidated
1088 1092 None otherwise. `cache_active = False` means that this cache
1089 1093 state is not valid and needs to be invalidated
1090 1094
1091 1095 :param key:
1092 1096 """
1093 1097 return cls.query()\
1094 1098 .filter(CacheInvalidation.cache_key == key)\
1095 1099 .filter(CacheInvalidation.cache_active == False)\
1096 1100 .scalar()
1097 1101
1098 1102 @classmethod
1099 1103 def set_invalidate(cls, key):
1100 1104 """
1101 1105 Mark this Cache key for invalidation
1102 1106
1103 1107 :param key:
1104 1108 """
1105 1109
1106 1110 log.debug('marking %s for invalidation' % key)
1107 1111 inv_obj = Session().query(cls)\
1108 1112 .filter(cls.cache_key == key).scalar()
1109 1113 if inv_obj:
1110 1114 inv_obj.cache_active = False
1111 1115 else:
1112 1116 log.debug('cache key not found in invalidation db -> creating one')
1113 1117 inv_obj = CacheInvalidation(key)
1114 1118
1115 1119 try:
1116 1120 Session().add(inv_obj)
1117 1121 Session().commit()
1118 1122 except Exception:
1119 1123 log.error(traceback.format_exc())
1120 1124 Session().rollback()
1121 1125
1122 1126 @classmethod
1123 1127 def set_valid(cls, key):
1124 1128 """
1125 1129 Mark this cache key as active and currently cached
1126 1130
1127 1131 :param key:
1128 1132 """
1129 1133 inv_obj = CacheInvalidation.query()\
1130 1134 .filter(CacheInvalidation.cache_key == key).scalar()
1131 1135 inv_obj.cache_active = True
1132 1136 Session().add(inv_obj)
1133 1137 Session().commit()
1134 1138
1135 1139
1136 1140 class ChangesetComment(Base, BaseModel):
1137 1141 __tablename__ = 'changeset_comments'
1138 1142 __table_args__ = ({'extend_existing':True},)
1139 1143 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1140 1144 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1141 1145 revision = Column('revision', String(40), nullable=False)
1142 1146 line_no = Column('line_no', Unicode(10), nullable=True)
1143 1147 f_path = Column('f_path', Unicode(1000), nullable=True)
1144 1148 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1145 1149 text = Column('text', Unicode(25000), nullable=False)
1146 1150 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1147 1151
1148 1152 author = relationship('User', lazy='joined')
1149 1153 repo = relationship('Repository')
1150 1154
1151 1155
1152 1156 @classmethod
1153 1157 def get_users(cls, revision):
1154 1158 """
1155 1159 Returns user associated with this changesetComment. ie those
1156 1160 who actually commented
1157 1161
1158 1162 :param cls:
1159 1163 :param revision:
1160 1164 """
1161 1165 return Session().query(User)\
1162 1166 .filter(cls.revision == revision)\
1163 1167 .join(ChangesetComment.author).all()
1164 1168
1165 1169
1166 1170 class Notification(Base, BaseModel):
1167 1171 __tablename__ = 'notifications'
1168 1172 __table_args__ = ({'extend_existing':True})
1169 1173
1170 1174 TYPE_CHANGESET_COMMENT = u'cs_comment'
1171 1175 TYPE_MESSAGE = u'message'
1172 1176 TYPE_MENTION = u'mention'
1177 TYPE_REGISTRATION = u'registration'
1173 1178
1174 1179 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1175 1180 subject = Column('subject', Unicode(512), nullable=True)
1176 1181 body = Column('body', Unicode(50000), nullable=True)
1177 1182 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1178 1183 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1179 1184 type_ = Column('type', Unicode(256))
1180 1185
1181 1186 created_by_user = relationship('User')
1182 1187 notifications_to_users = relationship('UserNotification', lazy='joined',
1183 1188 cascade="all, delete, delete-orphan")
1184 1189
1185 1190 @property
1186 1191 def recipients(self):
1187 1192 return [x.user for x in UserNotification.query()\
1188 1193 .filter(UserNotification.notification == self).all()]
1189 1194
1190 1195 @classmethod
1191 1196 def create(cls, created_by, subject, body, recipients, type_=None):
1192 1197 if type_ is None:
1193 1198 type_ = Notification.TYPE_MESSAGE
1194 1199
1195 1200 notification = cls()
1196 1201 notification.created_by_user = created_by
1197 1202 notification.subject = subject
1198 1203 notification.body = body
1199 1204 notification.type_ = type_
1200 1205 notification.created_on = datetime.datetime.now()
1201 1206
1202 1207 for u in recipients:
1203 1208 assoc = UserNotification()
1204 1209 assoc.notification = notification
1205 1210 u.notifications.append(assoc)
1206 1211 Session().add(notification)
1207 1212 return notification
1208 1213
1209 1214 @property
1210 1215 def description(self):
1211 1216 from rhodecode.model.notification import NotificationModel
1212 1217 return NotificationModel().make_description(self)
1213 1218
1214 1219 class UserNotification(Base, BaseModel):
1215 1220 __tablename__ = 'user_to_notification'
1216 1221 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1217 1222 {'extend_existing':True})
1218 1223 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1219 1224 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1220 1225 read = Column('read', Boolean, default=False)
1221 1226 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1222 1227
1223 1228 user = relationship('User', lazy="joined")
1224 1229 notification = relationship('Notification', lazy="joined",
1225 1230 order_by=lambda:Notification.created_on.desc(),
1226 1231 cascade='all')
1227 1232
1228 1233 def mark_as_read(self):
1229 1234 self.read = True
1230 1235 Session().add(self)
1231 1236
1232 1237 class DbMigrateVersion(Base, BaseModel):
1233 1238 __tablename__ = 'db_migrate_version'
1234 1239 __table_args__ = {'extend_existing':True}
1235 1240 repository_id = Column('repository_id', String(250), primary_key=True)
1236 1241 repository_path = Column('repository_path', Text)
1237 1242 version = Column('version', Integer)
1238 1243
@@ -1,201 +1,215 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.notification
4 4 ~~~~~~~~~~~~~~
5 5
6 6 Model for notifications
7 7
8 8
9 9 :created_on: Nov 20, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30 import datetime
31 31
32 32 from pylons.i18n.translation import _
33 33
34 34 import rhodecode
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.model import BaseModel
37 37 from rhodecode.model.db import Notification, User, UserNotification
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class NotificationModel(BaseModel):
43 43
44 44 def __get_user(self, user):
45 45 if isinstance(user, basestring):
46 46 return User.get_by_username(username=user)
47 47 else:
48 48 return self._get_instance(User, user)
49 49
50 50 def __get_notification(self, notification):
51 51 if isinstance(notification, Notification):
52 52 return notification
53 53 elif isinstance(notification, int):
54 54 return Notification.get(notification)
55 55 else:
56 56 if notification:
57 57 raise Exception('notification must be int or Instance'
58 58 ' of Notification got %s' % type(notification))
59 59
60 def create(self, created_by, subject, body, recipients,
61 type_=Notification.TYPE_MESSAGE):
60 def create(self, created_by, subject, body, recipients=None,
61 type_=Notification.TYPE_MESSAGE, with_email=True,
62 email_kwargs={}):
62 63 """
63 64
64 65 Creates notification of given type
65 66
66 67 :param created_by: int, str or User instance. User who created this
67 68 notification
68 69 :param subject:
69 70 :param body:
70 :param recipients: list of int, str or User objects
71 :param recipients: list of int, str or User objects, when None
72 is given send to all admins
71 73 :param type_: type of notification
74 :param with_email: send email with this notification
75 :param email_kwargs: additional dict to pass as args to email template
72 76 """
73 77 from rhodecode.lib.celerylib import tasks, run_task
74 78
75 if not getattr(recipients, '__iter__', False):
79 if recipients and not getattr(recipients, '__iter__', False):
76 80 raise Exception('recipients must be a list of iterable')
77 81
78 82 created_by_obj = self.__get_user(created_by)
79 83
80 recipients_objs = []
81 for u in recipients:
82 obj = self.__get_user(u)
83 if obj:
84 recipients_objs.append(obj)
85 recipients_objs = set(recipients_objs)
84 if recipients:
85 recipients_objs = []
86 for u in recipients:
87 obj = self.__get_user(u)
88 if obj:
89 recipients_objs.append(obj)
90 recipients_objs = set(recipients_objs)
91 else:
92 # empty recipients means to all admins
93 recipients_objs = User.query().filter(User.admin == True).all()
86 94
87 95 notif = Notification.create(created_by=created_by_obj, subject=subject,
88 96 body=body, recipients=recipients_objs,
89 97 type_=type_)
90 98
99 if with_email is False:
100 return notif
101
91 102 # send email with notification
92 103 for rec in recipients_objs:
93 104 email_subject = NotificationModel().make_description(notif, False)
94 type_ = EmailNotificationModel.TYPE_CHANGESET_COMMENT
105 type_ = type_
95 106 email_body = body
107 kwargs = {'subject':subject, 'body':h.rst(body)}
108 kwargs.update(email_kwargs)
96 109 email_body_html = EmailNotificationModel()\
97 .get_email_tmpl(type_, **{'subject':subject,
98 'body':h.rst(body)})
110 .get_email_tmpl(type_, **kwargs)
99 111 run_task(tasks.send_email, rec.email, email_subject, email_body,
100 112 email_body_html)
101 113
102 114 return notif
103 115
104 116 def delete(self, user, notification):
105 117 # we don't want to remove actual notification just the assignment
106 118 try:
107 119 notification = self.__get_notification(notification)
108 120 user = self.__get_user(user)
109 121 if notification and user:
110 122 obj = UserNotification.query()\
111 123 .filter(UserNotification.user == user)\
112 124 .filter(UserNotification.notification
113 125 == notification)\
114 126 .one()
115 127 self.sa.delete(obj)
116 128 return True
117 129 except Exception:
118 130 log.error(traceback.format_exc())
119 131 raise
120 132
121 133 def get_for_user(self, user):
122 134 user = self.__get_user(user)
123 135 return user.notifications
124 136
125 137 def get_unread_cnt_for_user(self, user):
126 138 user = self.__get_user(user)
127 139 return UserNotification.query()\
128 140 .filter(UserNotification.read == False)\
129 141 .filter(UserNotification.user == user).count()
130 142
131 143 def get_unread_for_user(self, user):
132 144 user = self.__get_user(user)
133 145 return [x.notification for x in UserNotification.query()\
134 146 .filter(UserNotification.read == False)\
135 147 .filter(UserNotification.user == user).all()]
136 148
137 149 def get_user_notification(self, user, notification):
138 150 user = self.__get_user(user)
139 151 notification = self.__get_notification(notification)
140 152
141 153 return UserNotification.query()\
142 154 .filter(UserNotification.notification == notification)\
143 155 .filter(UserNotification.user == user).scalar()
144 156
145 157 def make_description(self, notification, show_age=True):
146 158 """
147 159 Creates a human readable description based on properties
148 160 of notification object
149 161 """
150 162
151 163 _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'),
152 164 notification.TYPE_MESSAGE:_('sent message'),
153 notification.TYPE_MENTION:_('mentioned you')}
165 notification.TYPE_MENTION:_('mentioned you'),
166 notification.TYPE_REGISTRATION:_('registered in RhodeCode')}
167
154 168 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
155 169
156 170 tmpl = "%(user)s %(action)s %(when)s"
157 171 if show_age:
158 172 when = h.age(notification.created_on)
159 173 else:
160 174 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
161 175 when = DTF(notification.created_on)
162 176 data = dict(user=notification.created_by_user.username,
163 177 action=_map[notification.type_],
164 178 when=when)
165 179 return tmpl % data
166 180
167 181
168 182 class EmailNotificationModel(BaseModel):
169 183
170 184 TYPE_CHANGESET_COMMENT = 'changeset_comment'
171 185 TYPE_PASSWORD_RESET = 'passoword_link'
172 186 TYPE_REGISTRATION = 'registration'
173 187 TYPE_DEFAULT = 'default'
174 188
175 189 def __init__(self):
176 190 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
177 191 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
178 192
179 193 self.email_types = {
180 194 self.TYPE_CHANGESET_COMMENT:'email_templates/changeset_comment.html',
181 195 self.TYPE_PASSWORD_RESET:'email_templates/password_reset.html',
182 196 self.TYPE_REGISTRATION:'email_templates/registration.html',
183 197 self.TYPE_DEFAULT:'email_templates/default.html'
184 198 }
185 199
186 200 def get_email_tmpl(self, type_, **kwargs):
187 201 """
188 202 return generated template for email based on given type
189 203
190 204 :param type_:
191 205 """
192 206
193 207 base = self.email_types.get(type_, self.TYPE_DEFAULT)
194 208 email_template = self._tmpl_lookup.get_template(base)
195 209 # translator inject
196 210 _kwargs = {'_':_}
197 211 _kwargs.update(kwargs)
198 212 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
199 213 return email_template.render(**_kwargs)
200 214
201 215
@@ -1,470 +1,482 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 from pylons import url
29 30 from pylons.i18n.translation import _
30 31
31 32 from rhodecode.lib import safe_unicode
32 33 from rhodecode.lib.caching_query import FromCache
33 34
34 35 from rhodecode.model import BaseModel
35 36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
36 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 Notification
37 39 from rhodecode.lib.exceptions import DefaultUserException, \
38 40 UserOwnsReposException
39 41
40 42 from sqlalchemy.exc import DatabaseError
41 43 from rhodecode.lib import generate_api_key
42 44 from sqlalchemy.orm import joinedload
43 45
44 46 log = logging.getLogger(__name__)
45 47
48
46 49 PERM_WEIGHTS = {'repository.none': 0,
47 50 'repository.read': 1,
48 51 'repository.write': 3,
49 52 'repository.admin': 3}
50 53
51 54
52 55 class UserModel(BaseModel):
53 56
54 57 def get(self, user_id, cache=False):
55 58 user = self.sa.query(User)
56 59 if cache:
57 60 user = user.options(FromCache("sql_cache_short",
58 61 "get_user_%s" % user_id))
59 62 return user.get(user_id)
60 63
61 64 def get_by_username(self, username, cache=False, case_insensitive=False):
62 65
63 66 if case_insensitive:
64 67 user = self.sa.query(User).filter(User.username.ilike(username))
65 68 else:
66 69 user = self.sa.query(User)\
67 70 .filter(User.username == username)
68 71 if cache:
69 72 user = user.options(FromCache("sql_cache_short",
70 73 "get_user_%s" % username))
71 74 return user.scalar()
72 75
73 76 def get_by_api_key(self, api_key, cache=False):
74 77 return User.get_by_api_key(api_key, cache)
75 78
76 79 def create(self, form_data):
77 80 try:
78 81 new_user = User()
79 82 for k, v in form_data.items():
80 83 setattr(new_user, k, v)
81 84
82 85 new_user.api_key = generate_api_key(form_data['username'])
83 86 self.sa.add(new_user)
84 87 self.sa.commit()
85 88 return new_user
86 89 except:
87 90 log.error(traceback.format_exc())
88 91 self.sa.rollback()
89 92 raise
90 93
91 94
92 95 def create_or_update(self, username, password, email, name, lastname,
93 96 active=True, admin=False, ldap_dn=None):
94 97 """
95 98 Creates a new instance if not found, or updates current one
96 99
97 100 :param username:
98 101 :param password:
99 102 :param email:
100 103 :param active:
101 104 :param name:
102 105 :param lastname:
103 106 :param active:
104 107 :param admin:
105 108 :param ldap_dn:
106 109 """
107 110
108 111 from rhodecode.lib.auth import get_crypt_password
109 112
110 113 log.debug('Checking for %s account in RhodeCode database', username)
111 114 user = User.get_by_username(username, case_insensitive=True)
112 115 if user is None:
113 116 log.debug('creating new user %s', username)
114 117 new_user = User()
115 118 else:
116 119 log.debug('updating user %s', username)
117 120 new_user = user
118 121
119 122 try:
120 123 new_user.username = username
121 124 new_user.admin = admin
122 125 new_user.password = get_crypt_password(password)
123 126 new_user.api_key = generate_api_key(username)
124 127 new_user.email = email
125 128 new_user.active = active
126 129 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
127 130 new_user.name = name
128 131 new_user.lastname = lastname
129 132
130 133 self.sa.add(new_user)
131 134 self.sa.commit()
132 135 return new_user
133 136 except (DatabaseError,):
134 137 log.error(traceback.format_exc())
135 138 self.sa.rollback()
136 139 raise
137 140
138 141
139 142 def create_for_container_auth(self, username, attrs):
140 143 """
141 144 Creates the given user if it's not already in the database
142 145
143 146 :param username:
144 147 :param attrs:
145 148 """
146 149 if self.get_by_username(username, case_insensitive=True) is None:
147 150
148 151 # autogenerate email for container account without one
149 152 generate_email = lambda usr: '%s@container_auth.account' % usr
150 153
151 154 try:
152 155 new_user = User()
153 156 new_user.username = username
154 157 new_user.password = None
155 158 new_user.api_key = generate_api_key(username)
156 159 new_user.email = attrs['email']
157 160 new_user.active = attrs.get('active', True)
158 161 new_user.name = attrs['name'] or generate_email(username)
159 162 new_user.lastname = attrs['lastname']
160 163
161 164 self.sa.add(new_user)
162 165 self.sa.commit()
163 166 return new_user
164 167 except (DatabaseError,):
165 168 log.error(traceback.format_exc())
166 169 self.sa.rollback()
167 170 raise
168 171 log.debug('User %s already exists. Skipping creation of account'
169 172 ' for container auth.', username)
170 173 return None
171 174
172 175 def create_ldap(self, username, password, user_dn, attrs):
173 176 """
174 177 Checks if user is in database, if not creates this user marked
175 178 as ldap user
176 179
177 180 :param username:
178 181 :param password:
179 182 :param user_dn:
180 183 :param attrs:
181 184 """
182 185 from rhodecode.lib.auth import get_crypt_password
183 186 log.debug('Checking for such ldap account in RhodeCode database')
184 187 if self.get_by_username(username, case_insensitive=True) is None:
185 188
186 189 # autogenerate email for ldap account without one
187 190 generate_email = lambda usr: '%s@ldap.account' % usr
188 191
189 192 try:
190 193 new_user = User()
191 194 username = username.lower()
192 195 # add ldap account always lowercase
193 196 new_user.username = username
194 197 new_user.password = get_crypt_password(password)
195 198 new_user.api_key = generate_api_key(username)
196 199 new_user.email = attrs['email'] or generate_email(username)
197 200 new_user.active = attrs.get('active', True)
198 201 new_user.ldap_dn = safe_unicode(user_dn)
199 202 new_user.name = attrs['name']
200 203 new_user.lastname = attrs['lastname']
201 204
202 205 self.sa.add(new_user)
203 206 self.sa.commit()
204 207 return new_user
205 208 except (DatabaseError,):
206 209 log.error(traceback.format_exc())
207 210 self.sa.rollback()
208 211 raise
209 212 log.debug('this %s user exists skipping creation of ldap account',
210 213 username)
211 214 return None
212 215
213 216 def create_registration(self, form_data):
214 from rhodecode.lib.celerylib import tasks, run_task
217 from rhodecode.model.notification import NotificationModel
218
215 219 try:
216 220 new_user = User()
217 221 for k, v in form_data.items():
218 222 if k != 'admin':
219 223 setattr(new_user, k, v)
220 224
221 225 self.sa.add(new_user)
222 self.sa.commit()
226 self.sa.flush()
227
228 # notification to admins
229 subject = _('new user registration')
223 230 body = ('New user registration\n'
224 'username: %s\n'
225 'email: %s\n')
226 body = body % (form_data['username'], form_data['email'])
231 '---------------------\n'
232 '- Username: %s\n'
233 '- Full Name: %s\n'
234 '- Email: %s\n')
235 body = body % (new_user.username, new_user.full_name,
236 new_user.email)
237 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
238 kw = {'registered_user_url':edit_url}
239 NotificationModel().create(created_by=new_user, subject=subject,
240 body=body, recipients=None,
241 type_=Notification.TYPE_REGISTRATION,
242 email_kwargs=kw)
227 243
228 run_task(tasks.send_email, None,
229 _('[RhodeCode] New User registration'),
230 body)
231 244 except:
232 245 log.error(traceback.format_exc())
233 self.sa.rollback()
234 246 raise
235 247
236 248 def update(self, user_id, form_data):
237 249 try:
238 250 user = self.get(user_id, cache=False)
239 251 if user.username == 'default':
240 252 raise DefaultUserException(
241 253 _("You can't Edit this user since it's"
242 254 " crucial for entire application"))
243 255
244 256 for k, v in form_data.items():
245 257 if k == 'new_password' and v != '':
246 258 user.password = v
247 259 user.api_key = generate_api_key(user.username)
248 260 else:
249 261 setattr(user, k, v)
250 262
251 263 self.sa.add(user)
252 264 self.sa.commit()
253 265 except:
254 266 log.error(traceback.format_exc())
255 267 self.sa.rollback()
256 268 raise
257 269
258 270 def update_my_account(self, user_id, form_data):
259 271 try:
260 272 user = self.get(user_id, cache=False)
261 273 if user.username == 'default':
262 274 raise DefaultUserException(
263 275 _("You can't Edit this user since it's"
264 276 " crucial for entire application"))
265 277 for k, v in form_data.items():
266 278 if k == 'new_password' and v != '':
267 279 user.password = v
268 280 user.api_key = generate_api_key(user.username)
269 281 else:
270 282 if k not in ['admin', 'active']:
271 283 setattr(user, k, v)
272 284
273 285 self.sa.add(user)
274 286 self.sa.commit()
275 287 except:
276 288 log.error(traceback.format_exc())
277 289 self.sa.rollback()
278 290 raise
279 291
280 292 def delete(self, user_id):
281 293 try:
282 294 user = self.get(user_id, cache=False)
283 295 if user.username == 'default':
284 296 raise DefaultUserException(
285 297 _("You can't remove this user since it's"
286 298 " crucial for entire application"))
287 299 if user.repositories:
288 300 raise UserOwnsReposException(_('This user still owns %s '
289 301 'repositories and cannot be '
290 302 'removed. Switch owners or '
291 303 'remove those repositories') \
292 304 % user.repositories)
293 305 self.sa.delete(user)
294 306 self.sa.commit()
295 307 except:
296 308 log.error(traceback.format_exc())
297 309 self.sa.rollback()
298 310 raise
299 311
300 312 def reset_password_link(self, data):
301 313 from rhodecode.lib.celerylib import tasks, run_task
302 314 run_task(tasks.send_password_link, data['email'])
303 315
304 316 def reset_password(self, data):
305 317 from rhodecode.lib.celerylib import tasks, run_task
306 318 run_task(tasks.reset_user_password, data['email'])
307 319
308 320 def fill_data(self, auth_user, user_id=None, api_key=None):
309 321 """
310 322 Fetches auth_user by user_id,or api_key if present.
311 323 Fills auth_user attributes with those taken from database.
312 324 Additionally set's is_authenitated if lookup fails
313 325 present in database
314 326
315 327 :param auth_user: instance of user to set attributes
316 328 :param user_id: user id to fetch by
317 329 :param api_key: api key to fetch by
318 330 """
319 331 if user_id is None and api_key is None:
320 332 raise Exception('You need to pass user_id or api_key')
321 333
322 334 try:
323 335 if api_key:
324 336 dbuser = self.get_by_api_key(api_key)
325 337 else:
326 338 dbuser = self.get(user_id)
327 339
328 340 if dbuser is not None and dbuser.active:
329 341 log.debug('filling %s data', dbuser)
330 342 for k, v in dbuser.get_dict().items():
331 343 setattr(auth_user, k, v)
332 344 else:
333 345 return False
334 346
335 347 except:
336 348 log.error(traceback.format_exc())
337 349 auth_user.is_authenticated = False
338 350 return False
339 351
340 352 return True
341 353
342 354 def fill_perms(self, user):
343 355 """
344 356 Fills user permission attribute with permissions taken from database
345 357 works for permissions given for repositories, and for permissions that
346 358 are granted to groups
347 359
348 360 :param user: user instance to fill his perms
349 361 """
350 362
351 363 user.permissions['repositories'] = {}
352 364 user.permissions['global'] = set()
353 365
354 366 #======================================================================
355 367 # fetch default permissions
356 368 #======================================================================
357 369 default_user = User.get_by_username('default', cache=True)
358 370 default_user_id = default_user.user_id
359 371
360 372 default_perms = Permission.get_default_perms(default_user_id)
361 373
362 374 if user.is_admin:
363 375 #==================================================================
364 376 # #admin have all default rights set to admin
365 377 #==================================================================
366 378 user.permissions['global'].add('hg.admin')
367 379
368 380 for perm in default_perms:
369 381 p = 'repository.admin'
370 382 user.permissions['repositories'][perm.UserRepoToPerm.
371 383 repository.repo_name] = p
372 384
373 385 else:
374 386 #==================================================================
375 387 # set default permissions
376 388 #==================================================================
377 389 uid = user.user_id
378 390
379 391 # default global
380 392 default_global_perms = self.sa.query(UserToPerm)\
381 393 .filter(UserToPerm.user_id == default_user_id)
382 394
383 395 for perm in default_global_perms:
384 396 user.permissions['global'].add(perm.permission.permission_name)
385 397
386 398 # default for repositories
387 399 for perm in default_perms:
388 400 if perm.Repository.private and not (perm.Repository.user_id ==
389 401 uid):
390 402 # disable defaults for private repos,
391 403 p = 'repository.none'
392 404 elif perm.Repository.user_id == uid:
393 405 # set admin if owner
394 406 p = 'repository.admin'
395 407 else:
396 408 p = perm.Permission.permission_name
397 409
398 410 user.permissions['repositories'][perm.UserRepoToPerm.
399 411 repository.repo_name] = p
400 412
401 413 #==================================================================
402 414 # overwrite default with user permissions if any
403 415 #==================================================================
404 416
405 417 # user global
406 418 user_perms = self.sa.query(UserToPerm)\
407 419 .options(joinedload(UserToPerm.permission))\
408 420 .filter(UserToPerm.user_id == uid).all()
409 421
410 422 for perm in user_perms:
411 423 user.permissions['global'].add(perm.permission.permission_name)
412 424
413 425 # user repositories
414 426 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
415 427 Repository)\
416 428 .join((Repository, UserRepoToPerm.repository_id ==
417 429 Repository.repo_id))\
418 430 .join((Permission, UserRepoToPerm.permission_id ==
419 431 Permission.permission_id))\
420 432 .filter(UserRepoToPerm.user_id == uid).all()
421 433
422 434 for perm in user_repo_perms:
423 435 # set admin if owner
424 436 if perm.Repository.user_id == uid:
425 437 p = 'repository.admin'
426 438 else:
427 439 p = perm.Permission.permission_name
428 440 user.permissions['repositories'][perm.UserRepoToPerm.
429 441 repository.repo_name] = p
430 442
431 443 #==================================================================
432 444 # check if user is part of groups for this repository and fill in
433 445 # (or replace with higher) permissions
434 446 #==================================================================
435 447
436 448 # users group global
437 449 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
438 450 .options(joinedload(UsersGroupToPerm.permission))\
439 451 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
440 452 UsersGroupMember.users_group_id))\
441 453 .filter(UsersGroupMember.user_id == uid).all()
442 454
443 455 for perm in user_perms_from_users_groups:
444 456 user.permissions['global'].add(perm.permission.permission_name)
445 457
446 458 # users group repositories
447 459 user_repo_perms_from_users_groups = self.sa.query(
448 460 UsersGroupRepoToPerm,
449 461 Permission, Repository,)\
450 462 .join((Repository, UsersGroupRepoToPerm.repository_id ==
451 463 Repository.repo_id))\
452 464 .join((Permission, UsersGroupRepoToPerm.permission_id ==
453 465 Permission.permission_id))\
454 466 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
455 467 UsersGroupMember.users_group_id))\
456 468 .filter(UsersGroupMember.user_id == uid).all()
457 469
458 470 for perm in user_repo_perms_from_users_groups:
459 471 p = perm.Permission.permission_name
460 472 cur_perm = user.permissions['repositories'][perm.
461 473 UsersGroupRepoToPerm.
462 474 repository.repo_name]
463 475 # overwrite permission only if it's greater than permission
464 476 # given from other sources
465 477 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
466 478 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
467 479 repository.repo_name] = p
468 480
469 481 return user
470 482
General Comments 0
You need to be logged in to leave comments. Login now