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