##// END OF EJS Templates
fixed repo_create permission by adding missing commit statements...
marcink -
r1758:a87aa385 beta
parent child Browse files
Show More
@@ -1,210 +1,210 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 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 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib.exceptions import DefaultUserException, \
35 from rhodecode.lib.exceptions import DefaultUserException, \
36 UserOwnsReposException
36 UserOwnsReposException
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40
40
41 from rhodecode.model.db import User, UserRepoToPerm, UserToPerm, Permission
41 from rhodecode.model.db import User, UserRepoToPerm, UserToPerm, Permission
42 from rhodecode.model.forms import UserForm
42 from rhodecode.model.forms import UserForm
43 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class UsersController(BaseController):
49 class UsersController(BaseController):
50 """REST Controller styled on the Atom Publishing Protocol"""
50 """REST Controller styled on the Atom Publishing Protocol"""
51 # To properly map this controller, ensure your config/routing.py
51 # To properly map this controller, ensure your config/routing.py
52 # file has a resource setup:
52 # file has a resource setup:
53 # map.resource('user', 'users')
53 # map.resource('user', 'users')
54
54
55 @LoginRequired()
55 @LoginRequired()
56 @HasPermissionAllDecorator('hg.admin')
56 @HasPermissionAllDecorator('hg.admin')
57 def __before__(self):
57 def __before__(self):
58 c.admin_user = session.get('admin_user')
58 c.admin_user = session.get('admin_user')
59 c.admin_username = session.get('admin_username')
59 c.admin_username = session.get('admin_username')
60 super(UsersController, self).__before__()
60 super(UsersController, self).__before__()
61 c.available_permissions = config['available_permissions']
61 c.available_permissions = config['available_permissions']
62
62
63 def index(self, format='html'):
63 def index(self, format='html'):
64 """GET /users: All items in the collection"""
64 """GET /users: All items in the collection"""
65 # url('users')
65 # url('users')
66
66
67 c.users_list = self.sa.query(User).all()
67 c.users_list = self.sa.query(User).all()
68 return render('admin/users/users.html')
68 return render('admin/users/users.html')
69
69
70 def create(self):
70 def create(self):
71 """POST /users: Create a new item"""
71 """POST /users: Create a new item"""
72 # url('users')
72 # url('users')
73
73
74 user_model = UserModel()
74 user_model = UserModel()
75 user_form = UserForm()()
75 user_form = UserForm()()
76 try:
76 try:
77 form_result = user_form.to_python(dict(request.POST))
77 form_result = user_form.to_python(dict(request.POST))
78 user_model.create(form_result)
78 user_model.create(form_result)
79 h.flash(_('created user %s') % form_result['username'],
79 h.flash(_('created user %s') % form_result['username'],
80 category='success')
80 category='success')
81 Session.commit()
81 Session.commit()
82 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
82 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
83 except formencode.Invalid, errors:
83 except formencode.Invalid, errors:
84 return htmlfill.render(
84 return htmlfill.render(
85 render('admin/users/user_add.html'),
85 render('admin/users/user_add.html'),
86 defaults=errors.value,
86 defaults=errors.value,
87 errors=errors.error_dict or {},
87 errors=errors.error_dict or {},
88 prefix_error=False,
88 prefix_error=False,
89 encoding="UTF-8")
89 encoding="UTF-8")
90 except Exception:
90 except Exception:
91 log.error(traceback.format_exc())
91 log.error(traceback.format_exc())
92 h.flash(_('error occurred during creation of user %s') \
92 h.flash(_('error occurred during creation of user %s') \
93 % request.POST.get('username'), category='error')
93 % request.POST.get('username'), category='error')
94 return redirect(url('users'))
94 return redirect(url('users'))
95
95
96 def new(self, format='html'):
96 def new(self, format='html'):
97 """GET /users/new: Form to create a new item"""
97 """GET /users/new: Form to create a new item"""
98 # url('new_user')
98 # url('new_user')
99 return render('admin/users/user_add.html')
99 return render('admin/users/user_add.html')
100
100
101 def update(self, id):
101 def update(self, id):
102 """PUT /users/id: Update an existing item"""
102 """PUT /users/id: Update an existing item"""
103 # Forms posted to this method should contain a hidden field:
103 # Forms posted to this method should contain a hidden field:
104 # <input type="hidden" name="_method" value="PUT" />
104 # <input type="hidden" name="_method" value="PUT" />
105 # Or using helpers:
105 # Or using helpers:
106 # h.form(url('update_user', id=ID),
106 # h.form(url('update_user', id=ID),
107 # method='put')
107 # method='put')
108 # url('user', id=ID)
108 # url('user', id=ID)
109 user_model = UserModel()
109 user_model = UserModel()
110 c.user = user_model.get(id)
110 c.user = user_model.get(id)
111
111
112 _form = UserForm(edit=True, old_data={'user_id': id,
112 _form = UserForm(edit=True, old_data={'user_id': id,
113 'email': c.user.email})()
113 'email': c.user.email})()
114 form_result = {}
114 form_result = {}
115 try:
115 try:
116 form_result = _form.to_python(dict(request.POST))
116 form_result = _form.to_python(dict(request.POST))
117 user_model.update(id, form_result)
117 user_model.update(id, form_result)
118 h.flash(_('User updated successfully'), category='success')
118 h.flash(_('User updated successfully'), category='success')
119 Session.commit()
119 Session.commit()
120 except formencode.Invalid, errors:
120 except formencode.Invalid, errors:
121 e = errors.error_dict or {}
121 e = errors.error_dict or {}
122 perm = Permission.get_by_key('hg.create.repository')
122 perm = Permission.get_by_key('hg.create.repository')
123 e.update({'create_repo_perm': user_model.has_perm(id, perm)})
123 e.update({'create_repo_perm': user_model.has_perm(id, perm)})
124 return htmlfill.render(
124 return htmlfill.render(
125 render('admin/users/user_edit.html'),
125 render('admin/users/user_edit.html'),
126 defaults=errors.value,
126 defaults=errors.value,
127 errors=e,
127 errors=e,
128 prefix_error=False,
128 prefix_error=False,
129 encoding="UTF-8")
129 encoding="UTF-8")
130 except Exception:
130 except Exception:
131 log.error(traceback.format_exc())
131 log.error(traceback.format_exc())
132 h.flash(_('error occurred during update of user %s') \
132 h.flash(_('error occurred during update of user %s') \
133 % form_result.get('username'), category='error')
133 % form_result.get('username'), category='error')
134
134
135 return redirect(url('users'))
135 return redirect(url('users'))
136
136
137 def delete(self, id):
137 def delete(self, id):
138 """DELETE /users/id: Delete an existing item"""
138 """DELETE /users/id: Delete an existing item"""
139 # Forms posted to this method should contain a hidden field:
139 # Forms posted to this method should contain a hidden field:
140 # <input type="hidden" name="_method" value="DELETE" />
140 # <input type="hidden" name="_method" value="DELETE" />
141 # Or using helpers:
141 # Or using helpers:
142 # h.form(url('delete_user', id=ID),
142 # h.form(url('delete_user', id=ID),
143 # method='delete')
143 # method='delete')
144 # url('user', id=ID)
144 # url('user', id=ID)
145 user_model = UserModel()
145 user_model = UserModel()
146 try:
146 try:
147 user_model.delete(id)
147 user_model.delete(id)
148 h.flash(_('successfully deleted user'), category='success')
148 h.flash(_('successfully deleted user'), category='success')
149 Session.commit()
149 Session.commit()
150 except (UserOwnsReposException, DefaultUserException), e:
150 except (UserOwnsReposException, DefaultUserException), e:
151 h.flash(str(e), category='warning')
151 h.flash(str(e), category='warning')
152 except Exception:
152 except Exception:
153 h.flash(_('An error occurred during deletion of user'),
153 h.flash(_('An error occurred during deletion of user'),
154 category='error')
154 category='error')
155 return redirect(url('users'))
155 return redirect(url('users'))
156
156
157 def show(self, id, format='html'):
157 def show(self, id, format='html'):
158 """GET /users/id: Show a specific item"""
158 """GET /users/id: Show a specific item"""
159 # url('user', id=ID)
159 # url('user', id=ID)
160
160
161 def edit(self, id, format='html'):
161 def edit(self, id, format='html'):
162 """GET /users/id/edit: Form to edit an existing item"""
162 """GET /users/id/edit: Form to edit an existing item"""
163 # url('edit_user', id=ID)
163 # url('edit_user', id=ID)
164 c.user = User.get(id)
164 c.user = User.get(id)
165 if not c.user:
165 if not c.user:
166 return redirect(url('users'))
166 return redirect(url('users'))
167 if c.user.username == 'default':
167 if c.user.username == 'default':
168 h.flash(_("You can't edit this user"), category='warning')
168 h.flash(_("You can't edit this user"), category='warning')
169 return redirect(url('users'))
169 return redirect(url('users'))
170 c.user.permissions = {}
170 c.user.permissions = {}
171 c.granted_permissions = UserModel().fill_perms(c.user)\
171 c.granted_permissions = UserModel().fill_perms(c.user)\
172 .permissions['global']
172 .permissions['global']
173
173
174 defaults = c.user.get_dict()
174 defaults = c.user.get_dict()
175 perm = Permission.get_by_key('hg.create.repository')
175 perm = Permission.get_by_key('hg.create.repository')
176 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
176 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
177
177
178 return htmlfill.render(
178 return htmlfill.render(
179 render('admin/users/user_edit.html'),
179 render('admin/users/user_edit.html'),
180 defaults=defaults,
180 defaults=defaults,
181 encoding="UTF-8",
181 encoding="UTF-8",
182 force_defaults=False
182 force_defaults=False
183 )
183 )
184
184
185 def update_perm(self, id):
185 def update_perm(self, id):
186 """PUT /users_perm/id: Update an existing item"""
186 """PUT /users_perm/id: Update an existing item"""
187 # url('user_perm', id=ID, method='put')
187 # url('user_perm', id=ID, method='put')
188
188
189 grant_perm = request.POST.get('create_repo_perm', False)
189 grant_perm = request.POST.get('create_repo_perm', False)
190 user_model = UserModel()
190 user_model = UserModel()
191
191
192 if grant_perm:
192 if grant_perm:
193 perm = Permission.get_by_key('hg.create.none')
193 perm = Permission.get_by_key('hg.create.none')
194 user_model.revoke_perm(id, perm)
194 user_model.revoke_perm(id, perm)
195
195
196 perm = Permission.get_by_key('hg.create.repository')
196 perm = Permission.get_by_key('hg.create.repository')
197 user_model.grant_perm(id, perm)
197 user_model.grant_perm(id, perm)
198 h.flash(_("Granted 'repository create' permission to user"),
198 h.flash(_("Granted 'repository create' permission to user"),
199 category='success')
199 category='success')
200
200 Session.commit()
201 else:
201 else:
202 perm = Permission.get_by_key('hg.create.repository')
202 perm = Permission.get_by_key('hg.create.repository')
203 user_model.revoke_perm(id, perm)
203 user_model.revoke_perm(id, perm)
204
204
205 perm = Permission.get_by_key('hg.create.none')
205 perm = Permission.get_by_key('hg.create.none')
206 user_model.grant_perm(id, perm)
206 user_model.grant_perm(id, perm)
207 h.flash(_("Revoked 'repository create' permission to user"),
207 h.flash(_("Revoked 'repository create' permission to user"),
208 category='success')
208 category='success')
209
209 Session.commit()
210 return redirect(url('edit_user', id=id))
210 return redirect(url('edit_user', id=id))
@@ -1,468 +1,476 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
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 sys
28 import sys
29 import uuid
29 import uuid
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from rhodecode import __dbversion__
33 from rhodecode import __dbversion__
34 from rhodecode.model import meta
34 from rhodecode.model import meta
35
35
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.lib.utils import ask_ok
37 from rhodecode.lib.utils import ask_ok
38 from rhodecode.model import init_model
38 from rhodecode.model import init_model
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion
41
41
42 from sqlalchemy.engine import create_engine
42 from sqlalchemy.engine import create_engine
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class DbManage(object):
47 class DbManage(object):
48 def __init__(self, log_sql, dbconf, root, tests=False):
48 def __init__(self, log_sql, dbconf, root, tests=False):
49 self.dbname = dbconf.split('/')[-1]
49 self.dbname = dbconf.split('/')[-1]
50 self.tests = tests
50 self.tests = tests
51 self.root = root
51 self.root = root
52 self.dburi = dbconf
52 self.dburi = dbconf
53 self.log_sql = log_sql
53 self.log_sql = log_sql
54 self.db_exists = False
54 self.db_exists = False
55 self.init_db()
55 self.init_db()
56
56
57 def init_db(self):
57 def init_db(self):
58 engine = create_engine(self.dburi, echo=self.log_sql)
58 engine = create_engine(self.dburi, echo=self.log_sql)
59 init_model(engine)
59 init_model(engine)
60 self.sa = meta.Session
60 self.sa = meta.Session
61
61
62 def create_tables(self, override=False):
62 def create_tables(self, override=False):
63 """Create a auth database
63 """Create a auth database
64 """
64 """
65
65
66 log.info("Any existing database is going to be destroyed")
66 log.info("Any existing database is going to be destroyed")
67 if self.tests:
67 if self.tests:
68 destroy = True
68 destroy = True
69 else:
69 else:
70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
71 if not destroy:
71 if not destroy:
72 sys.exit()
72 sys.exit()
73 if destroy:
73 if destroy:
74 meta.Base.metadata.drop_all()
74 meta.Base.metadata.drop_all()
75
75
76 checkfirst = not override
76 checkfirst = not override
77 meta.Base.metadata.create_all(checkfirst=checkfirst)
77 meta.Base.metadata.create_all(checkfirst=checkfirst)
78 log.info('Created tables for %s', self.dbname)
78 log.info('Created tables for %s', self.dbname)
79
79
80 def set_db_version(self):
80 def set_db_version(self):
81 ver = DbMigrateVersion()
81 ver = DbMigrateVersion()
82 ver.version = __dbversion__
82 ver.version = __dbversion__
83 ver.repository_id = 'rhodecode_db_migrations'
83 ver.repository_id = 'rhodecode_db_migrations'
84 ver.repository_path = 'versions'
84 ver.repository_path = 'versions'
85 self.sa.add(ver)
85 self.sa.add(ver)
86 log.info('db version set to: %s', __dbversion__)
86 log.info('db version set to: %s', __dbversion__)
87
87
88 def upgrade(self):
88 def upgrade(self):
89 """Upgrades given database schema to given revision following
89 """Upgrades given database schema to given revision following
90 all needed steps, to perform the upgrade
90 all needed steps, to perform the upgrade
91
91
92 """
92 """
93
93
94 from rhodecode.lib.dbmigrate.migrate.versioning import api
94 from rhodecode.lib.dbmigrate.migrate.versioning import api
95 from rhodecode.lib.dbmigrate.migrate.exceptions import \
95 from rhodecode.lib.dbmigrate.migrate.exceptions import \
96 DatabaseNotControlledError
96 DatabaseNotControlledError
97
97
98 upgrade = ask_ok('You are about to perform database upgrade, make '
98 upgrade = ask_ok('You are about to perform database upgrade, make '
99 'sure You backed up your database before. '
99 'sure You backed up your database before. '
100 'Continue ? [y/n]')
100 'Continue ? [y/n]')
101 if not upgrade:
101 if not upgrade:
102 sys.exit('Nothing done')
102 sys.exit('Nothing done')
103
103
104 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
104 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
105 'rhodecode/lib/dbmigrate')
105 'rhodecode/lib/dbmigrate')
106 db_uri = self.dburi
106 db_uri = self.dburi
107
107
108 try:
108 try:
109 curr_version = api.db_version(db_uri, repository_path)
109 curr_version = api.db_version(db_uri, repository_path)
110 msg = ('Found current database under version'
110 msg = ('Found current database under version'
111 ' control with version %s' % curr_version)
111 ' control with version %s' % curr_version)
112
112
113 except (RuntimeError, DatabaseNotControlledError):
113 except (RuntimeError, DatabaseNotControlledError):
114 curr_version = 1
114 curr_version = 1
115 msg = ('Current database is not under version control. Setting'
115 msg = ('Current database is not under version control. Setting'
116 ' as version %s' % curr_version)
116 ' as version %s' % curr_version)
117 api.version_control(db_uri, repository_path, curr_version)
117 api.version_control(db_uri, repository_path, curr_version)
118
118
119 print (msg)
119 print (msg)
120
120
121 if curr_version == __dbversion__:
121 if curr_version == __dbversion__:
122 sys.exit('This database is already at the newest version')
122 sys.exit('This database is already at the newest version')
123
123
124 #======================================================================
124 #======================================================================
125 # UPGRADE STEPS
125 # UPGRADE STEPS
126 #======================================================================
126 #======================================================================
127 class UpgradeSteps(object):
127 class UpgradeSteps(object):
128 """Those steps follow schema versions so for example schema
128 """Those steps follow schema versions so for example schema
129 for example schema with seq 002 == step_2 and so on.
129 for example schema with seq 002 == step_2 and so on.
130 """
130 """
131
131
132 def __init__(self, klass):
132 def __init__(self, klass):
133 self.klass = klass
133 self.klass = klass
134
134
135 def step_0(self):
135 def step_0(self):
136 #step 0 is the schema upgrade, and than follow proper upgrades
136 #step 0 is the schema upgrade, and than follow proper upgrades
137 print ('attempting to do database upgrade to version %s' \
137 print ('attempting to do database upgrade to version %s' \
138 % __dbversion__)
138 % __dbversion__)
139 api.upgrade(db_uri, repository_path, __dbversion__)
139 api.upgrade(db_uri, repository_path, __dbversion__)
140 print ('Schema upgrade completed')
140 print ('Schema upgrade completed')
141
141
142 def step_1(self):
142 def step_1(self):
143 pass
143 pass
144
144
145 def step_2(self):
145 def step_2(self):
146 print ('Patching repo paths for newer version of RhodeCode')
146 print ('Patching repo paths for newer version of RhodeCode')
147 self.klass.fix_repo_paths()
147 self.klass.fix_repo_paths()
148
148
149 print ('Patching default user of RhodeCode')
149 print ('Patching default user of RhodeCode')
150 self.klass.fix_default_user()
150 self.klass.fix_default_user()
151
151
152 log.info('Changing ui settings')
152 log.info('Changing ui settings')
153 self.klass.create_ui_settings()
153 self.klass.create_ui_settings()
154
154
155 def step_3(self):
155 def step_3(self):
156 print ('Adding additional settings into RhodeCode db')
156 print ('Adding additional settings into RhodeCode db')
157 self.klass.fix_settings()
157 self.klass.fix_settings()
158 print ('Adding ldap defaults')
158 print ('Adding ldap defaults')
159 self.klass.create_ldap_options(skip_existing=True)
159 self.klass.create_ldap_options(skip_existing=True)
160
160
161 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
161 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
162
162
163 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
163 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
164 for step in upgrade_steps:
164 for step in upgrade_steps:
165 print ('performing upgrade step %s' % step)
165 print ('performing upgrade step %s' % step)
166 getattr(UpgradeSteps(self), 'step_%s' % step)()
166 getattr(UpgradeSteps(self), 'step_%s' % step)()
167
167
168 def fix_repo_paths(self):
168 def fix_repo_paths(self):
169 """Fixes a old rhodecode version path into new one without a '*'
169 """Fixes a old rhodecode version path into new one without a '*'
170 """
170 """
171
171
172 paths = self.sa.query(RhodeCodeUi)\
172 paths = self.sa.query(RhodeCodeUi)\
173 .filter(RhodeCodeUi.ui_key == '/')\
173 .filter(RhodeCodeUi.ui_key == '/')\
174 .scalar()
174 .scalar()
175
175
176 paths.ui_value = paths.ui_value.replace('*', '')
176 paths.ui_value = paths.ui_value.replace('*', '')
177
177
178 try:
178 try:
179 self.sa.add(paths)
179 self.sa.add(paths)
180 self.sa.commit()
180 self.sa.commit()
181 except:
181 except:
182 self.sa.rollback()
182 self.sa.rollback()
183 raise
183 raise
184
184
185 def fix_default_user(self):
185 def fix_default_user(self):
186 """Fixes a old default user with some 'nicer' default values,
186 """Fixes a old default user with some 'nicer' default values,
187 used mostly for anonymous access
187 used mostly for anonymous access
188 """
188 """
189 def_user = self.sa.query(User)\
189 def_user = self.sa.query(User)\
190 .filter(User.username == 'default')\
190 .filter(User.username == 'default')\
191 .one()
191 .one()
192
192
193 def_user.name = 'Anonymous'
193 def_user.name = 'Anonymous'
194 def_user.lastname = 'User'
194 def_user.lastname = 'User'
195 def_user.email = 'anonymous@rhodecode.org'
195 def_user.email = 'anonymous@rhodecode.org'
196
196
197 try:
197 try:
198 self.sa.add(def_user)
198 self.sa.add(def_user)
199 self.sa.commit()
199 self.sa.commit()
200 except:
200 except:
201 self.sa.rollback()
201 self.sa.rollback()
202 raise
202 raise
203
203
204 def fix_settings(self):
204 def fix_settings(self):
205 """Fixes rhodecode settings adds ga_code key for google analytics
205 """Fixes rhodecode settings adds ga_code key for google analytics
206 """
206 """
207
207
208 hgsettings3 = RhodeCodeSetting('ga_code', '')
208 hgsettings3 = RhodeCodeSetting('ga_code', '')
209
209
210 try:
210 try:
211 self.sa.add(hgsettings3)
211 self.sa.add(hgsettings3)
212 self.sa.commit()
212 self.sa.commit()
213 except:
213 except:
214 self.sa.rollback()
214 self.sa.rollback()
215 raise
215 raise
216
216
217 def admin_prompt(self, second=False):
217 def admin_prompt(self, second=False):
218 if not self.tests:
218 if not self.tests:
219 import getpass
219 import getpass
220
220
221 def get_password():
221 def get_password():
222 password = getpass.getpass('Specify admin password '
222 password = getpass.getpass('Specify admin password '
223 '(min 6 chars):')
223 '(min 6 chars):')
224 confirm = getpass.getpass('Confirm password:')
224 confirm = getpass.getpass('Confirm password:')
225
225
226 if password != confirm:
226 if password != confirm:
227 log.error('passwords mismatch')
227 log.error('passwords mismatch')
228 return False
228 return False
229 if len(password) < 6:
229 if len(password) < 6:
230 log.error('password is to short use at least 6 characters')
230 log.error('password is to short use at least 6 characters')
231 return False
231 return False
232
232
233 return password
233 return password
234
234
235 username = raw_input('Specify admin username:')
235 username = raw_input('Specify admin username:')
236
236
237 password = get_password()
237 password = get_password()
238 if not password:
238 if not password:
239 #second try
239 #second try
240 password = get_password()
240 password = get_password()
241 if not password:
241 if not password:
242 sys.exit()
242 sys.exit()
243
243
244 email = raw_input('Specify admin email:')
244 email = raw_input('Specify admin email:')
245 self.create_user(username, password, email, True)
245 self.create_user(username, password, email, True)
246 else:
246 else:
247 log.info('creating admin and regular test users')
247 log.info('creating admin and regular test users')
248 self.create_user('test_admin', 'test12',
248 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
249 'test_admin@mail.com', True)
249 TEST_USER_ADMIN_PASS ,TEST_USER_ADMIN_EMAIL,TEST_USER_REGULAR_LOGIN,\
250 self.create_user('test_regular', 'test12',
250 TEST_USER_REGULAR_PASS,TEST_USER_REGULAR_EMAIL,\
251 'test_regular@mail.com', False)
251 TEST_USER_REGULAR2_LOGIN,TEST_USER_REGULAR2_PASS,\
252 self.create_user('test_regular2', 'test12',
252 TEST_USER_REGULAR2_EMAIL
253 'test_regular2@mail.com', False)
253
254 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
255 TEST_USER_ADMIN_EMAIL, True)
256
257 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
258 TEST_USER_REGULAR_EMAIL, False)
259
260 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
261 TEST_USER_REGULAR2_EMAIL, False)
254
262
255 def create_ui_settings(self):
263 def create_ui_settings(self):
256 """Creates ui settings, fills out hooks
264 """Creates ui settings, fills out hooks
257 and disables dotencode
265 and disables dotencode
258
266
259 """
267 """
260 #HOOKS
268 #HOOKS
261 hooks1_key = RhodeCodeUi.HOOK_UPDATE
269 hooks1_key = RhodeCodeUi.HOOK_UPDATE
262 hooks1_ = self.sa.query(RhodeCodeUi)\
270 hooks1_ = self.sa.query(RhodeCodeUi)\
263 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
271 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
264
272
265 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
273 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
266 hooks1.ui_section = 'hooks'
274 hooks1.ui_section = 'hooks'
267 hooks1.ui_key = hooks1_key
275 hooks1.ui_key = hooks1_key
268 hooks1.ui_value = 'hg update >&2'
276 hooks1.ui_value = 'hg update >&2'
269 hooks1.ui_active = False
277 hooks1.ui_active = False
270
278
271 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
279 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
272 hooks2_ = self.sa.query(RhodeCodeUi)\
280 hooks2_ = self.sa.query(RhodeCodeUi)\
273 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
281 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
274
282
275 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
283 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
276 hooks2.ui_section = 'hooks'
284 hooks2.ui_section = 'hooks'
277 hooks2.ui_key = hooks2_key
285 hooks2.ui_key = hooks2_key
278 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
286 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
279
287
280 hooks3 = RhodeCodeUi()
288 hooks3 = RhodeCodeUi()
281 hooks3.ui_section = 'hooks'
289 hooks3.ui_section = 'hooks'
282 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
290 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
283 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
291 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
284
292
285 hooks4 = RhodeCodeUi()
293 hooks4 = RhodeCodeUi()
286 hooks4.ui_section = 'hooks'
294 hooks4.ui_section = 'hooks'
287 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
295 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
288 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
296 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
289
297
290 # For mercurial 1.7 set backward comapatibility with format
298 # For mercurial 1.7 set backward comapatibility with format
291 dotencode_disable = RhodeCodeUi()
299 dotencode_disable = RhodeCodeUi()
292 dotencode_disable.ui_section = 'format'
300 dotencode_disable.ui_section = 'format'
293 dotencode_disable.ui_key = 'dotencode'
301 dotencode_disable.ui_key = 'dotencode'
294 dotencode_disable.ui_value = 'false'
302 dotencode_disable.ui_value = 'false'
295
303
296 # enable largefiles
304 # enable largefiles
297 largefiles = RhodeCodeUi()
305 largefiles = RhodeCodeUi()
298 largefiles.ui_section = 'extensions'
306 largefiles.ui_section = 'extensions'
299 largefiles.ui_key = 'largefiles'
307 largefiles.ui_key = 'largefiles'
300 largefiles.ui_value = '1'
308 largefiles.ui_value = '1'
301
309
302 self.sa.add(hooks1)
310 self.sa.add(hooks1)
303 self.sa.add(hooks2)
311 self.sa.add(hooks2)
304 self.sa.add(hooks3)
312 self.sa.add(hooks3)
305 self.sa.add(hooks4)
313 self.sa.add(hooks4)
306 self.sa.add(largefiles)
314 self.sa.add(largefiles)
307
315
308 def create_ldap_options(self, skip_existing=False):
316 def create_ldap_options(self, skip_existing=False):
309 """Creates ldap settings"""
317 """Creates ldap settings"""
310
318
311 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
319 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
312 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
320 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
313 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
321 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
314 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
322 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
315 ('ldap_filter', ''), ('ldap_search_scope', ''),
323 ('ldap_filter', ''), ('ldap_search_scope', ''),
316 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
324 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
317 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
325 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
318
326
319 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
327 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
320 log.debug('Skipping option %s' % k)
328 log.debug('Skipping option %s' % k)
321 continue
329 continue
322 setting = RhodeCodeSetting(k, v)
330 setting = RhodeCodeSetting(k, v)
323 self.sa.add(setting)
331 self.sa.add(setting)
324
332
325 def config_prompt(self, test_repo_path='', retries=3):
333 def config_prompt(self, test_repo_path='', retries=3):
326 if retries == 3:
334 if retries == 3:
327 log.info('Setting up repositories config')
335 log.info('Setting up repositories config')
328
336
329 if not self.tests and not test_repo_path:
337 if not self.tests and not test_repo_path:
330 path = raw_input('Specify valid full path to your repositories'
338 path = raw_input('Specify valid full path to your repositories'
331 ' you can change this later in application settings:')
339 ' you can change this later in application settings:')
332 else:
340 else:
333 path = test_repo_path
341 path = test_repo_path
334 path_ok = True
342 path_ok = True
335
343
336 #check proper dir
344 #check proper dir
337 if not os.path.isdir(path):
345 if not os.path.isdir(path):
338 path_ok = False
346 path_ok = False
339 log.error('Given path %s is not a valid directory', path)
347 log.error('Given path %s is not a valid directory', path)
340
348
341 #check write access
349 #check write access
342 if not os.access(path, os.W_OK) and path_ok:
350 if not os.access(path, os.W_OK) and path_ok:
343 path_ok = False
351 path_ok = False
344 log.error('No write permission to given path %s', path)
352 log.error('No write permission to given path %s', path)
345
353
346
354
347 if retries == 0:
355 if retries == 0:
348 sys.exit('max retries reached')
356 sys.exit('max retries reached')
349 if path_ok is False:
357 if path_ok is False:
350 retries -= 1
358 retries -= 1
351 return self.config_prompt(test_repo_path, retries)
359 return self.config_prompt(test_repo_path, retries)
352
360
353 return path
361 return path
354
362
355 def create_settings(self, path):
363 def create_settings(self, path):
356
364
357 self.create_ui_settings()
365 self.create_ui_settings()
358
366
359 #HG UI OPTIONS
367 #HG UI OPTIONS
360 web1 = RhodeCodeUi()
368 web1 = RhodeCodeUi()
361 web1.ui_section = 'web'
369 web1.ui_section = 'web'
362 web1.ui_key = 'push_ssl'
370 web1.ui_key = 'push_ssl'
363 web1.ui_value = 'false'
371 web1.ui_value = 'false'
364
372
365 web2 = RhodeCodeUi()
373 web2 = RhodeCodeUi()
366 web2.ui_section = 'web'
374 web2.ui_section = 'web'
367 web2.ui_key = 'allow_archive'
375 web2.ui_key = 'allow_archive'
368 web2.ui_value = 'gz zip bz2'
376 web2.ui_value = 'gz zip bz2'
369
377
370 web3 = RhodeCodeUi()
378 web3 = RhodeCodeUi()
371 web3.ui_section = 'web'
379 web3.ui_section = 'web'
372 web3.ui_key = 'allow_push'
380 web3.ui_key = 'allow_push'
373 web3.ui_value = '*'
381 web3.ui_value = '*'
374
382
375 web4 = RhodeCodeUi()
383 web4 = RhodeCodeUi()
376 web4.ui_section = 'web'
384 web4.ui_section = 'web'
377 web4.ui_key = 'baseurl'
385 web4.ui_key = 'baseurl'
378 web4.ui_value = '/'
386 web4.ui_value = '/'
379
387
380 paths = RhodeCodeUi()
388 paths = RhodeCodeUi()
381 paths.ui_section = 'paths'
389 paths.ui_section = 'paths'
382 paths.ui_key = '/'
390 paths.ui_key = '/'
383 paths.ui_value = path
391 paths.ui_value = path
384
392
385 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
393 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
386 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
394 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
387 hgsettings3 = RhodeCodeSetting('ga_code', '')
395 hgsettings3 = RhodeCodeSetting('ga_code', '')
388
396
389 self.sa.add(web1)
397 self.sa.add(web1)
390 self.sa.add(web2)
398 self.sa.add(web2)
391 self.sa.add(web3)
399 self.sa.add(web3)
392 self.sa.add(web4)
400 self.sa.add(web4)
393 self.sa.add(paths)
401 self.sa.add(paths)
394 self.sa.add(hgsettings1)
402 self.sa.add(hgsettings1)
395 self.sa.add(hgsettings2)
403 self.sa.add(hgsettings2)
396 self.sa.add(hgsettings3)
404 self.sa.add(hgsettings3)
397
405
398 self.create_ldap_options()
406 self.create_ldap_options()
399
407
400 log.info('created ui config')
408 log.info('created ui config')
401
409
402 def create_user(self, username, password, email='', admin=False):
410 def create_user(self, username, password, email='', admin=False):
403 log.info('creating user %s', username)
411 log.info('creating user %s', username)
404 UserModel().create_or_update(username, password, email,
412 UserModel().create_or_update(username, password, email,
405 name='RhodeCode', lastname='Admin',
413 name='RhodeCode', lastname='Admin',
406 active=True, admin=admin)
414 active=True, admin=admin)
407
415
408 def create_default_user(self):
416 def create_default_user(self):
409 log.info('creating default user')
417 log.info('creating default user')
410 # create default user for handling default permissions.
418 # create default user for handling default permissions.
411 UserModel().create_or_update(username='default',
419 UserModel().create_or_update(username='default',
412 password=str(uuid.uuid1())[:8],
420 password=str(uuid.uuid1())[:8],
413 email='anonymous@rhodecode.org',
421 email='anonymous@rhodecode.org',
414 name='Anonymous', lastname='User')
422 name='Anonymous', lastname='User')
415
423
416 def create_permissions(self):
424 def create_permissions(self):
417 #module.(access|create|change|delete)_[name]
425 #module.(access|create|change|delete)_[name]
418 #module.(read|write|owner)
426 #module.(read|write|owner)
419 perms = [('repository.none', 'Repository no access'),
427 perms = [('repository.none', 'Repository no access'),
420 ('repository.read', 'Repository read access'),
428 ('repository.read', 'Repository read access'),
421 ('repository.write', 'Repository write access'),
429 ('repository.write', 'Repository write access'),
422 ('repository.admin', 'Repository admin access'),
430 ('repository.admin', 'Repository admin access'),
423 ('hg.admin', 'Hg Administrator'),
431 ('hg.admin', 'Hg Administrator'),
424 ('hg.create.repository', 'Repository create'),
432 ('hg.create.repository', 'Repository create'),
425 ('hg.create.none', 'Repository creation disabled'),
433 ('hg.create.none', 'Repository creation disabled'),
426 ('hg.register.none', 'Register disabled'),
434 ('hg.register.none', 'Register disabled'),
427 ('hg.register.manual_activate', 'Register new user with '
435 ('hg.register.manual_activate', 'Register new user with '
428 'RhodeCode without manual'
436 'RhodeCode without manual'
429 'activation'),
437 'activation'),
430
438
431 ('hg.register.auto_activate', 'Register new user with '
439 ('hg.register.auto_activate', 'Register new user with '
432 'RhodeCode without auto '
440 'RhodeCode without auto '
433 'activation'),
441 'activation'),
434 ]
442 ]
435
443
436 for p in perms:
444 for p in perms:
437 new_perm = Permission()
445 new_perm = Permission()
438 new_perm.permission_name = p[0]
446 new_perm.permission_name = p[0]
439 new_perm.permission_longname = p[1]
447 new_perm.permission_longname = p[1]
440 self.sa.add(new_perm)
448 self.sa.add(new_perm)
441
449
442 def populate_default_permissions(self):
450 def populate_default_permissions(self):
443 log.info('creating default user permissions')
451 log.info('creating default user permissions')
444
452
445 default_user = self.sa.query(User)\
453 default_user = self.sa.query(User)\
446 .filter(User.username == 'default').scalar()
454 .filter(User.username == 'default').scalar()
447
455
448 reg_perm = UserToPerm()
456 reg_perm = UserToPerm()
449 reg_perm.user = default_user
457 reg_perm.user = default_user
450 reg_perm.permission = self.sa.query(Permission)\
458 reg_perm.permission = self.sa.query(Permission)\
451 .filter(Permission.permission_name == 'hg.register.manual_activate')\
459 .filter(Permission.permission_name == 'hg.register.manual_activate')\
452 .scalar()
460 .scalar()
453
461
454 create_repo_perm = UserToPerm()
462 create_repo_perm = UserToPerm()
455 create_repo_perm.user = default_user
463 create_repo_perm.user = default_user
456 create_repo_perm.permission = self.sa.query(Permission)\
464 create_repo_perm.permission = self.sa.query(Permission)\
457 .filter(Permission.permission_name == 'hg.create.repository')\
465 .filter(Permission.permission_name == 'hg.create.repository')\
458 .scalar()
466 .scalar()
459
467
460 default_repo_perm = UserToPerm()
468 default_repo_perm = UserToPerm()
461 default_repo_perm.user = default_user
469 default_repo_perm.user = default_user
462 default_repo_perm.permission = self.sa.query(Permission)\
470 default_repo_perm.permission = self.sa.query(Permission)\
463 .filter(Permission.permission_name == 'repository.read')\
471 .filter(Permission.permission_name == 'repository.read')\
464 .scalar()
472 .scalar()
465
473
466 self.sa.add(reg_perm)
474 self.sa.add(reg_perm)
467 self.sa.add(create_repo_perm)
475 self.sa.add(create_repo_perm)
468 self.sa.add(default_repo_perm)
476 self.sa.add(default_repo_perm)
@@ -1,1108 +1,1120 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 """
86
86 Base Model for all classess
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, serialized=False):
95 """return dict with keys and values corresponding
95 """
96 to this model data """
96 return dict with keys and values corresponding
97 to this model data
98 """
97
99
98 d = {}
100 d = {}
99 for k in self._get_keys():
101 for k in self._get_keys():
100 d[k] = getattr(self, k)
102 d[k] = getattr(self, k)
103
104 # also use __json__() if present to get additional fields
105 if hasattr(self, '__json__'):
106 for k,val in self.__json__().iteritems():
107 d[k] = val
101 return d
108 return d
102
109
103 def get_appstruct(self):
110 def get_appstruct(self):
104 """return list with keys and values tupples corresponding
111 """return list with keys and values tupples corresponding
105 to this model data """
112 to this model data """
106
113
107 l = []
114 l = []
108 for k in self._get_keys():
115 for k in self._get_keys():
109 l.append((k, getattr(self, k),))
116 l.append((k, getattr(self, k),))
110 return l
117 return l
111
118
112 def populate_obj(self, populate_dict):
119 def populate_obj(self, populate_dict):
113 """populate model with data from given populate_dict"""
120 """populate model with data from given populate_dict"""
114
121
115 for k in self._get_keys():
122 for k in self._get_keys():
116 if k in populate_dict:
123 if k in populate_dict:
117 setattr(self, k, populate_dict[k])
124 setattr(self, k, populate_dict[k])
118
125
119 @classmethod
126 @classmethod
120 def query(cls):
127 def query(cls):
121 return Session.query(cls)
128 return Session.query(cls)
122
129
123 @classmethod
130 @classmethod
124 def get(cls, id_):
131 def get(cls, id_):
125 if id_:
132 if id_:
126 return cls.query().get(id_)
133 return cls.query().get(id_)
127
134
128 @classmethod
135 @classmethod
129 def getAll(cls):
136 def getAll(cls):
130 return cls.query().all()
137 return cls.query().all()
131
138
132 @classmethod
139 @classmethod
133 def delete(cls, id_):
140 def delete(cls, id_):
134 obj = cls.query().get(id_)
141 obj = cls.query().get(id_)
135 Session.delete(obj)
142 Session.delete(obj)
136
143
137
144
138 class RhodeCodeSetting(Base, BaseModel):
145 class RhodeCodeSetting(Base, BaseModel):
139 __tablename__ = 'rhodecode_settings'
146 __tablename__ = 'rhodecode_settings'
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
147 __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)
148 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)
149 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)
150 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144
151
145 def __init__(self, k='', v=''):
152 def __init__(self, k='', v=''):
146 self.app_settings_name = k
153 self.app_settings_name = k
147 self.app_settings_value = v
154 self.app_settings_value = v
148
155
149
156
150 @validates('_app_settings_value')
157 @validates('_app_settings_value')
151 def validate_settings_value(self, key, val):
158 def validate_settings_value(self, key, val):
152 assert type(val) == unicode
159 assert type(val) == unicode
153 return val
160 return val
154
161
155 @hybrid_property
162 @hybrid_property
156 def app_settings_value(self):
163 def app_settings_value(self):
157 v = self._app_settings_value
164 v = self._app_settings_value
158 if v == 'ldap_active':
165 if v == 'ldap_active':
159 v = str2bool(v)
166 v = str2bool(v)
160 return v
167 return v
161
168
162 @app_settings_value.setter
169 @app_settings_value.setter
163 def app_settings_value(self, val):
170 def app_settings_value(self, val):
164 """
171 """
165 Setter that will always make sure we use unicode in app_settings_value
172 Setter that will always make sure we use unicode in app_settings_value
166
173
167 :param val:
174 :param val:
168 """
175 """
169 self._app_settings_value = safe_unicode(val)
176 self._app_settings_value = safe_unicode(val)
170
177
171 def __repr__(self):
178 def __repr__(self):
172 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 return "<%s('%s:%s')>" % (self.__class__.__name__,
173 self.app_settings_name, self.app_settings_value)
180 self.app_settings_name, self.app_settings_value)
174
181
175
182
176 @classmethod
183 @classmethod
177 def get_by_name(cls, ldap_key):
184 def get_by_name(cls, ldap_key):
178 return cls.query()\
185 return cls.query()\
179 .filter(cls.app_settings_name == ldap_key).scalar()
186 .filter(cls.app_settings_name == ldap_key).scalar()
180
187
181 @classmethod
188 @classmethod
182 def get_app_settings(cls, cache=False):
189 def get_app_settings(cls, cache=False):
183
190
184 ret = cls.query()
191 ret = cls.query()
185
192
186 if cache:
193 if cache:
187 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
194 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
188
195
189 if not ret:
196 if not ret:
190 raise Exception('Could not get application settings !')
197 raise Exception('Could not get application settings !')
191 settings = {}
198 settings = {}
192 for each in ret:
199 for each in ret:
193 settings['rhodecode_' + each.app_settings_name] = \
200 settings['rhodecode_' + each.app_settings_name] = \
194 each.app_settings_value
201 each.app_settings_value
195
202
196 return settings
203 return settings
197
204
198 @classmethod
205 @classmethod
199 def get_ldap_settings(cls, cache=False):
206 def get_ldap_settings(cls, cache=False):
200 ret = cls.query()\
207 ret = cls.query()\
201 .filter(cls.app_settings_name.startswith('ldap_')).all()
208 .filter(cls.app_settings_name.startswith('ldap_')).all()
202 fd = {}
209 fd = {}
203 for row in ret:
210 for row in ret:
204 fd.update({row.app_settings_name:row.app_settings_value})
211 fd.update({row.app_settings_name:row.app_settings_value})
205
212
206 return fd
213 return fd
207
214
208
215
209 class RhodeCodeUi(Base, BaseModel):
216 class RhodeCodeUi(Base, BaseModel):
210 __tablename__ = 'rhodecode_ui'
217 __tablename__ = 'rhodecode_ui'
211 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
218 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
212
219
213 HOOK_UPDATE = 'changegroup.update'
220 HOOK_UPDATE = 'changegroup.update'
214 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 HOOK_REPO_SIZE = 'changegroup.repo_size'
215 HOOK_PUSH = 'pretxnchangegroup.push_logger'
222 HOOK_PUSH = 'pretxnchangegroup.push_logger'
216 HOOK_PULL = 'preoutgoing.pull_logger'
223 HOOK_PULL = 'preoutgoing.pull_logger'
217
224
218 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
225 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)
226 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)
227 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)
228 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)
229 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
223
230
224
231
225 @classmethod
232 @classmethod
226 def get_by_key(cls, key):
233 def get_by_key(cls, key):
227 return cls.query().filter(cls.ui_key == key)
234 return cls.query().filter(cls.ui_key == key)
228
235
229
236
230 @classmethod
237 @classmethod
231 def get_builtin_hooks(cls):
238 def get_builtin_hooks(cls):
232 q = cls.query()
239 q = cls.query()
233 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
240 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
234 cls.HOOK_REPO_SIZE,
241 cls.HOOK_REPO_SIZE,
235 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 cls.HOOK_PUSH, cls.HOOK_PULL]))
236 return q.all()
243 return q.all()
237
244
238 @classmethod
245 @classmethod
239 def get_custom_hooks(cls):
246 def get_custom_hooks(cls):
240 q = cls.query()
247 q = cls.query()
241 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
248 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
242 cls.HOOK_REPO_SIZE,
249 cls.HOOK_REPO_SIZE,
243 cls.HOOK_PUSH, cls.HOOK_PULL]))
250 cls.HOOK_PUSH, cls.HOOK_PULL]))
244 q = q.filter(cls.ui_section == 'hooks')
251 q = q.filter(cls.ui_section == 'hooks')
245 return q.all()
252 return q.all()
246
253
247 @classmethod
254 @classmethod
248 def create_or_update_hook(cls, key, val):
255 def create_or_update_hook(cls, key, val):
249 new_ui = cls.get_by_key(key).scalar() or cls()
256 new_ui = cls.get_by_key(key).scalar() or cls()
250 new_ui.ui_section = 'hooks'
257 new_ui.ui_section = 'hooks'
251 new_ui.ui_active = True
258 new_ui.ui_active = True
252 new_ui.ui_key = key
259 new_ui.ui_key = key
253 new_ui.ui_value = val
260 new_ui.ui_value = val
254
261
255 Session.add(new_ui)
262 Session.add(new_ui)
256
263
257
264
258 class User(Base, BaseModel):
265 class User(Base, BaseModel):
259 __tablename__ = 'users'
266 __tablename__ = 'users'
260 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
261 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
265 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
266 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
270 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272
279
273 user_log = relationship('UserLog', cascade='all')
280 user_log = relationship('UserLog', cascade='all')
274 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
275
282
276 repositories = relationship('Repository')
283 repositories = relationship('Repository')
277 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
278 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279
286
280 group_member = relationship('UsersGroupMember', cascade='all')
287 group_member = relationship('UsersGroupMember', cascade='all')
281
288
282 notifications = relationship('UserNotification',)
289 notifications = relationship('UserNotification',)
283
290
284 @hybrid_property
291 @hybrid_property
285 def email(self):
292 def email(self):
286 return self._email
293 return self._email
287
294
288 @email.setter
295 @email.setter
289 def email(self, val):
296 def email(self, val):
290 self._email = val.lower() if val else None
297 self._email = val.lower() if val else None
291
298
292 @property
299 @property
293 def full_name(self):
300 def full_name(self):
294 return '%s %s' % (self.name, self.lastname)
301 return '%s %s' % (self.name, self.lastname)
295
302
296 @property
303 @property
297 def full_contact(self):
304 def full_contact(self):
298 return '%s %s <%s>' % (self.name, self.lastname, self.email)
305 return '%s %s <%s>' % (self.name, self.lastname, self.email)
299
306
300 @property
307 @property
301 def short_contact(self):
308 def short_contact(self):
302 return '%s %s' % (self.name, self.lastname)
309 return '%s %s' % (self.name, self.lastname)
303
310
304 @property
311 @property
305 def is_admin(self):
312 def is_admin(self):
306 return self.admin
313 return self.admin
307
314
308 def __repr__(self):
315 def __repr__(self):
309 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
316 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
310 self.user_id, self.username)
317 self.user_id, self.username)
311
318
312
319
313 @classmethod
320 @classmethod
314 def get_by_username(cls, username, case_insensitive=False, cache=False):
321 def get_by_username(cls, username, case_insensitive=False, cache=False):
315 if case_insensitive:
322 if case_insensitive:
316 q = cls.query().filter(cls.username.ilike(username))
323 q = cls.query().filter(cls.username.ilike(username))
317 else:
324 else:
318 q = cls.query().filter(cls.username == username)
325 q = cls.query().filter(cls.username == username)
319
326
320 if cache:
327 if cache:
321 q = q.options(FromCache("sql_cache_short",
328 q = q.options(FromCache("sql_cache_short",
322 "get_user_%s" % username))
329 "get_user_%s" % username))
323 return q.scalar()
330 return q.scalar()
324
331
325 @classmethod
332 @classmethod
326 def get_by_api_key(cls, api_key, cache=False):
333 def get_by_api_key(cls, api_key, cache=False):
327 q = cls.query().filter(cls.api_key == api_key)
334 q = cls.query().filter(cls.api_key == api_key)
328
335
329 if cache:
336 if cache:
330 q = q.options(FromCache("sql_cache_short",
337 q = q.options(FromCache("sql_cache_short",
331 "get_api_key_%s" % api_key))
338 "get_api_key_%s" % api_key))
332 return q.scalar()
339 return q.scalar()
333
340
334 @classmethod
341 @classmethod
335 def get_by_email(cls, email, case_insensitive=False, cache=False):
342 def get_by_email(cls, email, case_insensitive=False, cache=False):
336 if case_insensitive:
343 if case_insensitive:
337 q = cls.query().filter(cls.email.ilike(email))
344 q = cls.query().filter(cls.email.ilike(email))
338 else:
345 else:
339 q = cls.query().filter(cls.email == email)
346 q = cls.query().filter(cls.email == email)
340
347
341 if cache:
348 if cache:
342 q = q.options(FromCache("sql_cache_short",
349 q = q.options(FromCache("sql_cache_short",
343 "get_api_key_%s" % email))
350 "get_api_key_%s" % email))
344 return q.scalar()
351 return q.scalar()
345
352
346 def update_lastlogin(self):
353 def update_lastlogin(self):
347 """Update user lastlogin"""
354 """Update user lastlogin"""
348 self.last_login = datetime.datetime.now()
355 self.last_login = datetime.datetime.now()
349 Session.add(self)
356 Session.add(self)
350 log.debug('updated user %s lastlogin', self.username)
357 log.debug('updated user %s lastlogin', self.username)
351
358
352
359
360 def __json__(self):
361 return dict(email=self.email,
362 full_name=self.full_name)
363
364
353 class UserLog(Base, BaseModel):
365 class UserLog(Base, BaseModel):
354 __tablename__ = 'user_logs'
366 __tablename__ = 'user_logs'
355 __table_args__ = {'extend_existing':True}
367 __table_args__ = {'extend_existing':True}
356 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
368 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
357 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
369 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
358 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
370 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
359 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
371 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
360 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
372 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
361 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
373 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
362 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
374 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
363
375
364 @property
376 @property
365 def action_as_day(self):
377 def action_as_day(self):
366 return datetime.date(*self.action_date.timetuple()[:3])
378 return datetime.date(*self.action_date.timetuple()[:3])
367
379
368 user = relationship('User')
380 user = relationship('User')
369 repository = relationship('Repository',cascade='')
381 repository = relationship('Repository',cascade='')
370
382
371
383
372 class UsersGroup(Base, BaseModel):
384 class UsersGroup(Base, BaseModel):
373 __tablename__ = 'users_groups'
385 __tablename__ = 'users_groups'
374 __table_args__ = {'extend_existing':True}
386 __table_args__ = {'extend_existing':True}
375
387
376 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
388 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
377 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
389 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
378 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
390 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
379
391
380 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
392 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
381
393
382 def __repr__(self):
394 def __repr__(self):
383 return '<userGroup(%s)>' % (self.users_group_name)
395 return '<userGroup(%s)>' % (self.users_group_name)
384
396
385 @classmethod
397 @classmethod
386 def get_by_group_name(cls, group_name, cache=False,
398 def get_by_group_name(cls, group_name, cache=False,
387 case_insensitive=False):
399 case_insensitive=False):
388 if case_insensitive:
400 if case_insensitive:
389 q = cls.query().filter(cls.users_group_name.ilike(group_name))
401 q = cls.query().filter(cls.users_group_name.ilike(group_name))
390 else:
402 else:
391 q = cls.query().filter(cls.users_group_name == group_name)
403 q = cls.query().filter(cls.users_group_name == group_name)
392 if cache:
404 if cache:
393 q = q.options(FromCache("sql_cache_short",
405 q = q.options(FromCache("sql_cache_short",
394 "get_user_%s" % group_name))
406 "get_user_%s" % group_name))
395 return q.scalar()
407 return q.scalar()
396
408
397 @classmethod
409 @classmethod
398 def get(cls, users_group_id, cache=False):
410 def get(cls, users_group_id, cache=False):
399 users_group = cls.query()
411 users_group = cls.query()
400 if cache:
412 if cache:
401 users_group = users_group.options(FromCache("sql_cache_short",
413 users_group = users_group.options(FromCache("sql_cache_short",
402 "get_users_group_%s" % users_group_id))
414 "get_users_group_%s" % users_group_id))
403 return users_group.get(users_group_id)
415 return users_group.get(users_group_id)
404
416
405 class UsersGroupMember(Base, BaseModel):
417 class UsersGroupMember(Base, BaseModel):
406 __tablename__ = 'users_groups_members'
418 __tablename__ = 'users_groups_members'
407 __table_args__ = {'extend_existing':True}
419 __table_args__ = {'extend_existing':True}
408
420
409 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
421 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
422 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
423 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412
424
413 user = relationship('User', lazy='joined')
425 user = relationship('User', lazy='joined')
414 users_group = relationship('UsersGroup')
426 users_group = relationship('UsersGroup')
415
427
416 def __init__(self, gr_id='', u_id=''):
428 def __init__(self, gr_id='', u_id=''):
417 self.users_group_id = gr_id
429 self.users_group_id = gr_id
418 self.user_id = u_id
430 self.user_id = u_id
419
431
420 @staticmethod
432 @staticmethod
421 def add_user_to_group(group, user):
433 def add_user_to_group(group, user):
422 ugm = UsersGroupMember()
434 ugm = UsersGroupMember()
423 ugm.users_group = group
435 ugm.users_group = group
424 ugm.user = user
436 ugm.user = user
425 Session.add(ugm)
437 Session.add(ugm)
426 Session.commit()
438 Session.commit()
427 return ugm
439 return ugm
428
440
429 class Repository(Base, BaseModel):
441 class Repository(Base, BaseModel):
430 __tablename__ = 'repositories'
442 __tablename__ = 'repositories'
431 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
443 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
432
444
433 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
445 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
446 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
447 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
436 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
448 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
449 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
438 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
450 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
439 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
451 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
440 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
452 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
441 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
453 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
442 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
454 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
443
455
444 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
456 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
445 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
457 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
446
458
447
459
448 user = relationship('User')
460 user = relationship('User')
449 fork = relationship('Repository', remote_side=repo_id)
461 fork = relationship('Repository', remote_side=repo_id)
450 group = relationship('RepoGroup')
462 group = relationship('RepoGroup')
451 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
463 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
452 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
464 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
453 stats = relationship('Statistics', cascade='all', uselist=False)
465 stats = relationship('Statistics', cascade='all', uselist=False)
454
466
455 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
467 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
456
468
457 logs = relationship('UserLog')
469 logs = relationship('UserLog')
458
470
459 def __repr__(self):
471 def __repr__(self):
460 return "<%s('%s:%s')>" % (self.__class__.__name__,
472 return "<%s('%s:%s')>" % (self.__class__.__name__,
461 self.repo_id, self.repo_name)
473 self.repo_id, self.repo_name)
462
474
463 @classmethod
475 @classmethod
464 def url_sep(cls):
476 def url_sep(cls):
465 return '/'
477 return '/'
466
478
467 @classmethod
479 @classmethod
468 def get_by_repo_name(cls, repo_name):
480 def get_by_repo_name(cls, repo_name):
469 q = Session.query(cls).filter(cls.repo_name == repo_name)
481 q = Session.query(cls).filter(cls.repo_name == repo_name)
470 q = q.options(joinedload(Repository.fork))\
482 q = q.options(joinedload(Repository.fork))\
471 .options(joinedload(Repository.user))\
483 .options(joinedload(Repository.user))\
472 .options(joinedload(Repository.group))
484 .options(joinedload(Repository.group))
473 return q.scalar()
485 return q.scalar()
474
486
475 @classmethod
487 @classmethod
476 def get_repo_forks(cls, repo_id):
488 def get_repo_forks(cls, repo_id):
477 return cls.query().filter(Repository.fork_id == repo_id)
489 return cls.query().filter(Repository.fork_id == repo_id)
478
490
479 @classmethod
491 @classmethod
480 def base_path(cls):
492 def base_path(cls):
481 """
493 """
482 Returns base path when all repos are stored
494 Returns base path when all repos are stored
483
495
484 :param cls:
496 :param cls:
485 """
497 """
486 q = Session.query(RhodeCodeUi)\
498 q = Session.query(RhodeCodeUi)\
487 .filter(RhodeCodeUi.ui_key == cls.url_sep())
499 .filter(RhodeCodeUi.ui_key == cls.url_sep())
488 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
500 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
489 return q.one().ui_value
501 return q.one().ui_value
490
502
491 @property
503 @property
492 def just_name(self):
504 def just_name(self):
493 return self.repo_name.split(Repository.url_sep())[-1]
505 return self.repo_name.split(Repository.url_sep())[-1]
494
506
495 @property
507 @property
496 def groups_with_parents(self):
508 def groups_with_parents(self):
497 groups = []
509 groups = []
498 if self.group is None:
510 if self.group is None:
499 return groups
511 return groups
500
512
501 cur_gr = self.group
513 cur_gr = self.group
502 groups.insert(0, cur_gr)
514 groups.insert(0, cur_gr)
503 while 1:
515 while 1:
504 gr = getattr(cur_gr, 'parent_group', None)
516 gr = getattr(cur_gr, 'parent_group', None)
505 cur_gr = cur_gr.parent_group
517 cur_gr = cur_gr.parent_group
506 if gr is None:
518 if gr is None:
507 break
519 break
508 groups.insert(0, gr)
520 groups.insert(0, gr)
509
521
510 return groups
522 return groups
511
523
512 @property
524 @property
513 def groups_and_repo(self):
525 def groups_and_repo(self):
514 return self.groups_with_parents, self.just_name
526 return self.groups_with_parents, self.just_name
515
527
516 @LazyProperty
528 @LazyProperty
517 def repo_path(self):
529 def repo_path(self):
518 """
530 """
519 Returns base full path for that repository means where it actually
531 Returns base full path for that repository means where it actually
520 exists on a filesystem
532 exists on a filesystem
521 """
533 """
522 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
534 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
523 Repository.url_sep())
535 Repository.url_sep())
524 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
536 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
525 return q.one().ui_value
537 return q.one().ui_value
526
538
527 @property
539 @property
528 def repo_full_path(self):
540 def repo_full_path(self):
529 p = [self.repo_path]
541 p = [self.repo_path]
530 # we need to split the name by / since this is how we store the
542 # we need to split the name by / since this is how we store the
531 # names in the database, but that eventually needs to be converted
543 # names in the database, but that eventually needs to be converted
532 # into a valid system path
544 # into a valid system path
533 p += self.repo_name.split(Repository.url_sep())
545 p += self.repo_name.split(Repository.url_sep())
534 return os.path.join(*p)
546 return os.path.join(*p)
535
547
536 def get_new_name(self, repo_name):
548 def get_new_name(self, repo_name):
537 """
549 """
538 returns new full repository name based on assigned group and new new
550 returns new full repository name based on assigned group and new new
539
551
540 :param group_name:
552 :param group_name:
541 """
553 """
542 path_prefix = self.group.full_path_splitted if self.group else []
554 path_prefix = self.group.full_path_splitted if self.group else []
543 return Repository.url_sep().join(path_prefix + [repo_name])
555 return Repository.url_sep().join(path_prefix + [repo_name])
544
556
545 @property
557 @property
546 def _ui(self):
558 def _ui(self):
547 """
559 """
548 Creates an db based ui object for this repository
560 Creates an db based ui object for this repository
549 """
561 """
550 from mercurial import ui
562 from mercurial import ui
551 from mercurial import config
563 from mercurial import config
552 baseui = ui.ui()
564 baseui = ui.ui()
553
565
554 #clean the baseui object
566 #clean the baseui object
555 baseui._ocfg = config.config()
567 baseui._ocfg = config.config()
556 baseui._ucfg = config.config()
568 baseui._ucfg = config.config()
557 baseui._tcfg = config.config()
569 baseui._tcfg = config.config()
558
570
559
571
560 ret = RhodeCodeUi.query()\
572 ret = RhodeCodeUi.query()\
561 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
573 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
562
574
563 hg_ui = ret
575 hg_ui = ret
564 for ui_ in hg_ui:
576 for ui_ in hg_ui:
565 if ui_.ui_active:
577 if ui_.ui_active:
566 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
578 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
567 ui_.ui_key, ui_.ui_value)
579 ui_.ui_key, ui_.ui_value)
568 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
580 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
569
581
570 return baseui
582 return baseui
571
583
572 @classmethod
584 @classmethod
573 def is_valid(cls, repo_name):
585 def is_valid(cls, repo_name):
574 """
586 """
575 returns True if given repo name is a valid filesystem repository
587 returns True if given repo name is a valid filesystem repository
576
588
577 @param cls:
589 @param cls:
578 @param repo_name:
590 @param repo_name:
579 """
591 """
580 from rhodecode.lib.utils import is_valid_repo
592 from rhodecode.lib.utils import is_valid_repo
581
593
582 return is_valid_repo(repo_name, cls.base_path())
594 return is_valid_repo(repo_name, cls.base_path())
583
595
584
596
585 #==========================================================================
597 #==========================================================================
586 # SCM PROPERTIES
598 # SCM PROPERTIES
587 #==========================================================================
599 #==========================================================================
588
600
589 def get_changeset(self, rev):
601 def get_changeset(self, rev):
590 return get_changeset_safe(self.scm_instance, rev)
602 return get_changeset_safe(self.scm_instance, rev)
591
603
592 @property
604 @property
593 def tip(self):
605 def tip(self):
594 return self.get_changeset('tip')
606 return self.get_changeset('tip')
595
607
596 @property
608 @property
597 def author(self):
609 def author(self):
598 return self.tip.author
610 return self.tip.author
599
611
600 @property
612 @property
601 def last_change(self):
613 def last_change(self):
602 return self.scm_instance.last_change
614 return self.scm_instance.last_change
603
615
604 #==========================================================================
616 #==========================================================================
605 # SCM CACHE INSTANCE
617 # SCM CACHE INSTANCE
606 #==========================================================================
618 #==========================================================================
607
619
608 @property
620 @property
609 def invalidate(self):
621 def invalidate(self):
610 return CacheInvalidation.invalidate(self.repo_name)
622 return CacheInvalidation.invalidate(self.repo_name)
611
623
612 def set_invalidate(self):
624 def set_invalidate(self):
613 """
625 """
614 set a cache for invalidation for this instance
626 set a cache for invalidation for this instance
615 """
627 """
616 CacheInvalidation.set_invalidate(self.repo_name)
628 CacheInvalidation.set_invalidate(self.repo_name)
617
629
618 @LazyProperty
630 @LazyProperty
619 def scm_instance(self):
631 def scm_instance(self):
620 return self.__get_instance()
632 return self.__get_instance()
621
633
622 @property
634 @property
623 def scm_instance_cached(self):
635 def scm_instance_cached(self):
624 @cache_region('long_term')
636 @cache_region('long_term')
625 def _c(repo_name):
637 def _c(repo_name):
626 return self.__get_instance()
638 return self.__get_instance()
627 rn = self.repo_name
639 rn = self.repo_name
628 log.debug('Getting cached instance of repo')
640 log.debug('Getting cached instance of repo')
629 inv = self.invalidate
641 inv = self.invalidate
630 if inv is not None:
642 if inv is not None:
631 region_invalidate(_c, None, rn)
643 region_invalidate(_c, None, rn)
632 # update our cache
644 # update our cache
633 CacheInvalidation.set_valid(inv.cache_key)
645 CacheInvalidation.set_valid(inv.cache_key)
634 return _c(rn)
646 return _c(rn)
635
647
636 def __get_instance(self):
648 def __get_instance(self):
637 repo_full_path = self.repo_full_path
649 repo_full_path = self.repo_full_path
638 try:
650 try:
639 alias = get_scm(repo_full_path)[0]
651 alias = get_scm(repo_full_path)[0]
640 log.debug('Creating instance of %s repository', alias)
652 log.debug('Creating instance of %s repository', alias)
641 backend = get_backend(alias)
653 backend = get_backend(alias)
642 except VCSError:
654 except VCSError:
643 log.error(traceback.format_exc())
655 log.error(traceback.format_exc())
644 log.error('Perhaps this repository is in db and not in '
656 log.error('Perhaps this repository is in db and not in '
645 'filesystem run rescan repositories with '
657 'filesystem run rescan repositories with '
646 '"destroy old data " option from admin panel')
658 '"destroy old data " option from admin panel')
647 return
659 return
648
660
649 if alias == 'hg':
661 if alias == 'hg':
650 repo = backend(safe_str(repo_full_path), create=False,
662 repo = backend(safe_str(repo_full_path), create=False,
651 baseui=self._ui)
663 baseui=self._ui)
652 # skip hidden web repository
664 # skip hidden web repository
653 if repo._get_hidden():
665 if repo._get_hidden():
654 return
666 return
655 else:
667 else:
656 repo = backend(repo_full_path, create=False)
668 repo = backend(repo_full_path, create=False)
657
669
658 return repo
670 return repo
659
671
660
672
661 class RepoGroup(Base, BaseModel):
673 class RepoGroup(Base, BaseModel):
662 __tablename__ = 'groups'
674 __tablename__ = 'groups'
663 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
675 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
664 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
676 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
665 __mapper_args__ = {'order_by':'group_name'}
677 __mapper_args__ = {'order_by':'group_name'}
666
678
667 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
679 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
668 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
680 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
669 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
681 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
670 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
682 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
671
683
672 parent_group = relationship('RepoGroup', remote_side=group_id)
684 parent_group = relationship('RepoGroup', remote_side=group_id)
673
685
674
686
675 def __init__(self, group_name='', parent_group=None):
687 def __init__(self, group_name='', parent_group=None):
676 self.group_name = group_name
688 self.group_name = group_name
677 self.parent_group = parent_group
689 self.parent_group = parent_group
678
690
679 def __repr__(self):
691 def __repr__(self):
680 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
692 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
681 self.group_name)
693 self.group_name)
682
694
683 @classmethod
695 @classmethod
684 def groups_choices(cls):
696 def groups_choices(cls):
685 from webhelpers.html import literal as _literal
697 from webhelpers.html import literal as _literal
686 repo_groups = [('', '')]
698 repo_groups = [('', '')]
687 sep = ' &raquo; '
699 sep = ' &raquo; '
688 _name = lambda k: _literal(sep.join(k))
700 _name = lambda k: _literal(sep.join(k))
689
701
690 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
702 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
691 for x in cls.query().all()])
703 for x in cls.query().all()])
692
704
693 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
705 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
694 return repo_groups
706 return repo_groups
695
707
696 @classmethod
708 @classmethod
697 def url_sep(cls):
709 def url_sep(cls):
698 return '/'
710 return '/'
699
711
700 @classmethod
712 @classmethod
701 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
713 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
702 if case_insensitive:
714 if case_insensitive:
703 gr = cls.query()\
715 gr = cls.query()\
704 .filter(cls.group_name.ilike(group_name))
716 .filter(cls.group_name.ilike(group_name))
705 else:
717 else:
706 gr = cls.query()\
718 gr = cls.query()\
707 .filter(cls.group_name == group_name)
719 .filter(cls.group_name == group_name)
708 if cache:
720 if cache:
709 gr = gr.options(FromCache("sql_cache_short",
721 gr = gr.options(FromCache("sql_cache_short",
710 "get_group_%s" % group_name))
722 "get_group_%s" % group_name))
711 return gr.scalar()
723 return gr.scalar()
712
724
713 @property
725 @property
714 def parents(self):
726 def parents(self):
715 parents_recursion_limit = 5
727 parents_recursion_limit = 5
716 groups = []
728 groups = []
717 if self.parent_group is None:
729 if self.parent_group is None:
718 return groups
730 return groups
719 cur_gr = self.parent_group
731 cur_gr = self.parent_group
720 groups.insert(0, cur_gr)
732 groups.insert(0, cur_gr)
721 cnt = 0
733 cnt = 0
722 while 1:
734 while 1:
723 cnt += 1
735 cnt += 1
724 gr = getattr(cur_gr, 'parent_group', None)
736 gr = getattr(cur_gr, 'parent_group', None)
725 cur_gr = cur_gr.parent_group
737 cur_gr = cur_gr.parent_group
726 if gr is None:
738 if gr is None:
727 break
739 break
728 if cnt == parents_recursion_limit:
740 if cnt == parents_recursion_limit:
729 # this will prevent accidental infinit loops
741 # this will prevent accidental infinit loops
730 log.error('group nested more than %s' %
742 log.error('group nested more than %s' %
731 parents_recursion_limit)
743 parents_recursion_limit)
732 break
744 break
733
745
734 groups.insert(0, gr)
746 groups.insert(0, gr)
735 return groups
747 return groups
736
748
737 @property
749 @property
738 def children(self):
750 def children(self):
739 return RepoGroup.query().filter(RepoGroup.parent_group == self)
751 return RepoGroup.query().filter(RepoGroup.parent_group == self)
740
752
741 @property
753 @property
742 def name(self):
754 def name(self):
743 return self.group_name.split(RepoGroup.url_sep())[-1]
755 return self.group_name.split(RepoGroup.url_sep())[-1]
744
756
745 @property
757 @property
746 def full_path(self):
758 def full_path(self):
747 return self.group_name
759 return self.group_name
748
760
749 @property
761 @property
750 def full_path_splitted(self):
762 def full_path_splitted(self):
751 return self.group_name.split(RepoGroup.url_sep())
763 return self.group_name.split(RepoGroup.url_sep())
752
764
753 @property
765 @property
754 def repositories(self):
766 def repositories(self):
755 return Repository.query().filter(Repository.group == self)
767 return Repository.query().filter(Repository.group == self)
756
768
757 @property
769 @property
758 def repositories_recursive_count(self):
770 def repositories_recursive_count(self):
759 cnt = self.repositories.count()
771 cnt = self.repositories.count()
760
772
761 def children_count(group):
773 def children_count(group):
762 cnt = 0
774 cnt = 0
763 for child in group.children:
775 for child in group.children:
764 cnt += child.repositories.count()
776 cnt += child.repositories.count()
765 cnt += children_count(child)
777 cnt += children_count(child)
766 return cnt
778 return cnt
767
779
768 return cnt + children_count(self)
780 return cnt + children_count(self)
769
781
770
782
771 def get_new_name(self, group_name):
783 def get_new_name(self, group_name):
772 """
784 """
773 returns new full group name based on parent and new name
785 returns new full group name based on parent and new name
774
786
775 :param group_name:
787 :param group_name:
776 """
788 """
777 path_prefix = (self.parent_group.full_path_splitted if
789 path_prefix = (self.parent_group.full_path_splitted if
778 self.parent_group else [])
790 self.parent_group else [])
779 return RepoGroup.url_sep().join(path_prefix + [group_name])
791 return RepoGroup.url_sep().join(path_prefix + [group_name])
780
792
781
793
782 class Permission(Base, BaseModel):
794 class Permission(Base, BaseModel):
783 __tablename__ = 'permissions'
795 __tablename__ = 'permissions'
784 __table_args__ = {'extend_existing':True}
796 __table_args__ = {'extend_existing':True}
785 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
797 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
786 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
798 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
787 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
799 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
788
800
789 def __repr__(self):
801 def __repr__(self):
790 return "<%s('%s:%s')>" % (self.__class__.__name__,
802 return "<%s('%s:%s')>" % (self.__class__.__name__,
791 self.permission_id, self.permission_name)
803 self.permission_id, self.permission_name)
792
804
793 @classmethod
805 @classmethod
794 def get_by_key(cls, key):
806 def get_by_key(cls, key):
795 return cls.query().filter(cls.permission_name == key).scalar()
807 return cls.query().filter(cls.permission_name == key).scalar()
796
808
797 @classmethod
809 @classmethod
798 def get_default_perms(cls, default_user_id):
810 def get_default_perms(cls, default_user_id):
799 q = Session.query(UserRepoToPerm, Repository, cls)\
811 q = Session.query(UserRepoToPerm, Repository, cls)\
800 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
812 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
801 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
813 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
802 .filter(UserRepoToPerm.user_id == default_user_id)
814 .filter(UserRepoToPerm.user_id == default_user_id)
803
815
804 return q.all()
816 return q.all()
805
817
806
818
807 class UserRepoToPerm(Base, BaseModel):
819 class UserRepoToPerm(Base, BaseModel):
808 __tablename__ = 'repo_to_perm'
820 __tablename__ = 'repo_to_perm'
809 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
821 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
810 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
822 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
811 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
823 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
812 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
824 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
813 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
825 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
814
826
815 user = relationship('User')
827 user = relationship('User')
816 permission = relationship('Permission')
828 permission = relationship('Permission')
817 repository = relationship('Repository')
829 repository = relationship('Repository')
818
830
819 @classmethod
831 @classmethod
820 def create(cls, user, repository, permission):
832 def create(cls, user, repository, permission):
821 n = cls()
833 n = cls()
822 n.user = user
834 n.user = user
823 n.repository = repository
835 n.repository = repository
824 n.permission = permission
836 n.permission = permission
825 Session.add(n)
837 Session.add(n)
826 return n
838 return n
827
839
828 def __repr__(self):
840 def __repr__(self):
829 return '<user:%s => %s >' % (self.user, self.repository)
841 return '<user:%s => %s >' % (self.user, self.repository)
830
842
831 class UserToPerm(Base, BaseModel):
843 class UserToPerm(Base, BaseModel):
832 __tablename__ = 'user_to_perm'
844 __tablename__ = 'user_to_perm'
833 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
845 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
834 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
846 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
835 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
847 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
836 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
848 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
837
849
838 user = relationship('User')
850 user = relationship('User')
839 permission = relationship('Permission', lazy='joined')
851 permission = relationship('Permission', lazy='joined')
840
852
841
853
842 class UsersGroupRepoToPerm(Base, BaseModel):
854 class UsersGroupRepoToPerm(Base, BaseModel):
843 __tablename__ = 'users_group_repo_to_perm'
855 __tablename__ = 'users_group_repo_to_perm'
844 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
856 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
845 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
857 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
846 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
858 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
847 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
859 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
848 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
860 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
849
861
850 users_group = relationship('UsersGroup')
862 users_group = relationship('UsersGroup')
851 permission = relationship('Permission')
863 permission = relationship('Permission')
852 repository = relationship('Repository')
864 repository = relationship('Repository')
853
865
854 @classmethod
866 @classmethod
855 def create(cls, users_group, repository, permission):
867 def create(cls, users_group, repository, permission):
856 n = cls()
868 n = cls()
857 n.users_group = users_group
869 n.users_group = users_group
858 n.repository = repository
870 n.repository = repository
859 n.permission = permission
871 n.permission = permission
860 Session.add(n)
872 Session.add(n)
861 return n
873 return n
862
874
863 def __repr__(self):
875 def __repr__(self):
864 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
876 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
865
877
866 class UsersGroupToPerm(Base, BaseModel):
878 class UsersGroupToPerm(Base, BaseModel):
867 __tablename__ = 'users_group_to_perm'
879 __tablename__ = 'users_group_to_perm'
868 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
880 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
869 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
881 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
870 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
882 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
871
883
872 users_group = relationship('UsersGroup')
884 users_group = relationship('UsersGroup')
873 permission = relationship('Permission')
885 permission = relationship('Permission')
874
886
875
887
876 class UserRepoGroupToPerm(Base, BaseModel):
888 class UserRepoGroupToPerm(Base, BaseModel):
877 __tablename__ = 'group_to_perm'
889 __tablename__ = 'group_to_perm'
878 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
890 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
879
891
880 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
892 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
881 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
893 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
882 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
894 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
883 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
895 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
884
896
885 user = relationship('User')
897 user = relationship('User')
886 permission = relationship('Permission')
898 permission = relationship('Permission')
887 group = relationship('RepoGroup')
899 group = relationship('RepoGroup')
888
900
889 class UsersGroupRepoGroupToPerm(Base, BaseModel):
901 class UsersGroupRepoGroupToPerm(Base, BaseModel):
890 __tablename__ = 'users_group_repo_group_to_perm'
902 __tablename__ = 'users_group_repo_group_to_perm'
891 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
903 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
892
904
893 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)
905 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)
894 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
906 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
895 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
907 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
896 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
908 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
897
909
898 users_group = relationship('UsersGroup')
910 users_group = relationship('UsersGroup')
899 permission = relationship('Permission')
911 permission = relationship('Permission')
900 group = relationship('RepoGroup')
912 group = relationship('RepoGroup')
901
913
902 class Statistics(Base, BaseModel):
914 class Statistics(Base, BaseModel):
903 __tablename__ = 'statistics'
915 __tablename__ = 'statistics'
904 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
916 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
905 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
917 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
906 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
918 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
907 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
919 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
908 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
920 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
909 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
921 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
910 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
922 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
911
923
912 repository = relationship('Repository', single_parent=True)
924 repository = relationship('Repository', single_parent=True)
913
925
914 class UserFollowing(Base, BaseModel):
926 class UserFollowing(Base, BaseModel):
915 __tablename__ = 'user_followings'
927 __tablename__ = 'user_followings'
916 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
928 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
917 UniqueConstraint('user_id', 'follows_user_id')
929 UniqueConstraint('user_id', 'follows_user_id')
918 , {'extend_existing':True})
930 , {'extend_existing':True})
919
931
920 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
932 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
921 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
933 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
922 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
934 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
923 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
935 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
924 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
936 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
925
937
926 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
938 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
927
939
928 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
940 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
929 follows_repository = relationship('Repository', order_by='Repository.repo_name')
941 follows_repository = relationship('Repository', order_by='Repository.repo_name')
930
942
931
943
932 @classmethod
944 @classmethod
933 def get_repo_followers(cls, repo_id):
945 def get_repo_followers(cls, repo_id):
934 return cls.query().filter(cls.follows_repo_id == repo_id)
946 return cls.query().filter(cls.follows_repo_id == repo_id)
935
947
936 class CacheInvalidation(Base, BaseModel):
948 class CacheInvalidation(Base, BaseModel):
937 __tablename__ = 'cache_invalidation'
949 __tablename__ = 'cache_invalidation'
938 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
950 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
939 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
951 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
940 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
952 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
941 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
953 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
942 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
954 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
943
955
944
956
945 def __init__(self, cache_key, cache_args=''):
957 def __init__(self, cache_key, cache_args=''):
946 self.cache_key = cache_key
958 self.cache_key = cache_key
947 self.cache_args = cache_args
959 self.cache_args = cache_args
948 self.cache_active = False
960 self.cache_active = False
949
961
950 def __repr__(self):
962 def __repr__(self):
951 return "<%s('%s:%s')>" % (self.__class__.__name__,
963 return "<%s('%s:%s')>" % (self.__class__.__name__,
952 self.cache_id, self.cache_key)
964 self.cache_id, self.cache_key)
953
965
954 @classmethod
966 @classmethod
955 def invalidate(cls, key):
967 def invalidate(cls, key):
956 """
968 """
957 Returns Invalidation object if this given key should be invalidated
969 Returns Invalidation object if this given key should be invalidated
958 None otherwise. `cache_active = False` means that this cache
970 None otherwise. `cache_active = False` means that this cache
959 state is not valid and needs to be invalidated
971 state is not valid and needs to be invalidated
960
972
961 :param key:
973 :param key:
962 """
974 """
963 return cls.query()\
975 return cls.query()\
964 .filter(CacheInvalidation.cache_key == key)\
976 .filter(CacheInvalidation.cache_key == key)\
965 .filter(CacheInvalidation.cache_active == False)\
977 .filter(CacheInvalidation.cache_active == False)\
966 .scalar()
978 .scalar()
967
979
968 @classmethod
980 @classmethod
969 def set_invalidate(cls, key):
981 def set_invalidate(cls, key):
970 """
982 """
971 Mark this Cache key for invalidation
983 Mark this Cache key for invalidation
972
984
973 :param key:
985 :param key:
974 """
986 """
975
987
976 log.debug('marking %s for invalidation' % key)
988 log.debug('marking %s for invalidation' % key)
977 inv_obj = Session.query(cls)\
989 inv_obj = Session.query(cls)\
978 .filter(cls.cache_key == key).scalar()
990 .filter(cls.cache_key == key).scalar()
979 if inv_obj:
991 if inv_obj:
980 inv_obj.cache_active = False
992 inv_obj.cache_active = False
981 else:
993 else:
982 log.debug('cache key not found in invalidation db -> creating one')
994 log.debug('cache key not found in invalidation db -> creating one')
983 inv_obj = CacheInvalidation(key)
995 inv_obj = CacheInvalidation(key)
984
996
985 try:
997 try:
986 Session.add(inv_obj)
998 Session.add(inv_obj)
987 Session.commit()
999 Session.commit()
988 except Exception:
1000 except Exception:
989 log.error(traceback.format_exc())
1001 log.error(traceback.format_exc())
990 Session.rollback()
1002 Session.rollback()
991
1003
992 @classmethod
1004 @classmethod
993 def set_valid(cls, key):
1005 def set_valid(cls, key):
994 """
1006 """
995 Mark this cache key as active and currently cached
1007 Mark this cache key as active and currently cached
996
1008
997 :param key:
1009 :param key:
998 """
1010 """
999 inv_obj = CacheInvalidation.query()\
1011 inv_obj = CacheInvalidation.query()\
1000 .filter(CacheInvalidation.cache_key == key).scalar()
1012 .filter(CacheInvalidation.cache_key == key).scalar()
1001 inv_obj.cache_active = True
1013 inv_obj.cache_active = True
1002 Session.add(inv_obj)
1014 Session.add(inv_obj)
1003 Session.commit()
1015 Session.commit()
1004
1016
1005
1017
1006 class ChangesetComment(Base, BaseModel):
1018 class ChangesetComment(Base, BaseModel):
1007 __tablename__ = 'changeset_comments'
1019 __tablename__ = 'changeset_comments'
1008 __table_args__ = ({'extend_existing':True},)
1020 __table_args__ = ({'extend_existing':True},)
1009 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1021 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1010 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1022 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1011 revision = Column('revision', String(40), nullable=False)
1023 revision = Column('revision', String(40), nullable=False)
1012 line_no = Column('line_no', Unicode(10), nullable=True)
1024 line_no = Column('line_no', Unicode(10), nullable=True)
1013 f_path = Column('f_path', Unicode(1000), nullable=True)
1025 f_path = Column('f_path', Unicode(1000), nullable=True)
1014 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1026 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1015 text = Column('text', Unicode(25000), nullable=False)
1027 text = Column('text', Unicode(25000), nullable=False)
1016 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1028 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1017
1029
1018 author = relationship('User', lazy='joined')
1030 author = relationship('User', lazy='joined')
1019 repo = relationship('Repository')
1031 repo = relationship('Repository')
1020
1032
1021
1033
1022 @classmethod
1034 @classmethod
1023 def get_users(cls, revision):
1035 def get_users(cls, revision):
1024 """
1036 """
1025 Returns user associated with this changesetComment. ie those
1037 Returns user associated with this changesetComment. ie those
1026 who actually commented
1038 who actually commented
1027
1039
1028 :param cls:
1040 :param cls:
1029 :param revision:
1041 :param revision:
1030 """
1042 """
1031 return Session.query(User)\
1043 return Session.query(User)\
1032 .filter(cls.revision == revision)\
1044 .filter(cls.revision == revision)\
1033 .join(ChangesetComment.author).all()
1045 .join(ChangesetComment.author).all()
1034
1046
1035
1047
1036 class Notification(Base, BaseModel):
1048 class Notification(Base, BaseModel):
1037 __tablename__ = 'notifications'
1049 __tablename__ = 'notifications'
1038 __table_args__ = ({'extend_existing':True})
1050 __table_args__ = ({'extend_existing':True})
1039
1051
1040 TYPE_CHANGESET_COMMENT = u'cs_comment'
1052 TYPE_CHANGESET_COMMENT = u'cs_comment'
1041 TYPE_MESSAGE = u'message'
1053 TYPE_MESSAGE = u'message'
1042 TYPE_MENTION = u'mention'
1054 TYPE_MENTION = u'mention'
1043 TYPE_REGISTRATION = u'registration'
1055 TYPE_REGISTRATION = u'registration'
1044
1056
1045 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1057 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1046 subject = Column('subject', Unicode(512), nullable=True)
1058 subject = Column('subject', Unicode(512), nullable=True)
1047 body = Column('body', Unicode(50000), nullable=True)
1059 body = Column('body', Unicode(50000), nullable=True)
1048 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1060 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1049 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1061 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1050 type_ = Column('type', Unicode(256))
1062 type_ = Column('type', Unicode(256))
1051
1063
1052 created_by_user = relationship('User')
1064 created_by_user = relationship('User')
1053 notifications_to_users = relationship('UserNotification', lazy='joined',
1065 notifications_to_users = relationship('UserNotification', lazy='joined',
1054 cascade="all, delete, delete-orphan")
1066 cascade="all, delete, delete-orphan")
1055
1067
1056 @property
1068 @property
1057 def recipients(self):
1069 def recipients(self):
1058 return [x.user for x in UserNotification.query()\
1070 return [x.user for x in UserNotification.query()\
1059 .filter(UserNotification.notification == self).all()]
1071 .filter(UserNotification.notification == self).all()]
1060
1072
1061 @classmethod
1073 @classmethod
1062 def create(cls, created_by, subject, body, recipients, type_=None):
1074 def create(cls, created_by, subject, body, recipients, type_=None):
1063 if type_ is None:
1075 if type_ is None:
1064 type_ = Notification.TYPE_MESSAGE
1076 type_ = Notification.TYPE_MESSAGE
1065
1077
1066 notification = cls()
1078 notification = cls()
1067 notification.created_by_user = created_by
1079 notification.created_by_user = created_by
1068 notification.subject = subject
1080 notification.subject = subject
1069 notification.body = body
1081 notification.body = body
1070 notification.type_ = type_
1082 notification.type_ = type_
1071 notification.created_on = datetime.datetime.now()
1083 notification.created_on = datetime.datetime.now()
1072
1084
1073 for u in recipients:
1085 for u in recipients:
1074 assoc = UserNotification()
1086 assoc = UserNotification()
1075 assoc.notification = notification
1087 assoc.notification = notification
1076 u.notifications.append(assoc)
1088 u.notifications.append(assoc)
1077 Session.add(notification)
1089 Session.add(notification)
1078 return notification
1090 return notification
1079
1091
1080 @property
1092 @property
1081 def description(self):
1093 def description(self):
1082 from rhodecode.model.notification import NotificationModel
1094 from rhodecode.model.notification import NotificationModel
1083 return NotificationModel().make_description(self)
1095 return NotificationModel().make_description(self)
1084
1096
1085 class UserNotification(Base, BaseModel):
1097 class UserNotification(Base, BaseModel):
1086 __tablename__ = 'user_to_notification'
1098 __tablename__ = 'user_to_notification'
1087 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1099 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1088 {'extend_existing':True})
1100 {'extend_existing':True})
1089 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1101 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1090 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1102 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1091 read = Column('read', Boolean, default=False)
1103 read = Column('read', Boolean, default=False)
1092 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1104 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1093
1105
1094 user = relationship('User', lazy="joined")
1106 user = relationship('User', lazy="joined")
1095 notification = relationship('Notification', lazy="joined",
1107 notification = relationship('Notification', lazy="joined",
1096 order_by=lambda:Notification.created_on.desc(),)
1108 order_by=lambda:Notification.created_on.desc(),)
1097
1109
1098 def mark_as_read(self):
1110 def mark_as_read(self):
1099 self.read = True
1111 self.read = True
1100 Session.add(self)
1112 Session.add(self)
1101
1113
1102 class DbMigrateVersion(Base, BaseModel):
1114 class DbMigrateVersion(Base, BaseModel):
1103 __tablename__ = 'db_migrate_version'
1115 __tablename__ = 'db_migrate_version'
1104 __table_args__ = {'extend_existing':True}
1116 __table_args__ = {'extend_existing':True}
1105 repository_id = Column('repository_id', String(250), primary_key=True)
1117 repository_id = Column('repository_id', String(250), primary_key=True)
1106 repository_path = Column('repository_path', Text)
1118 repository_path = Column('repository_path', Text)
1107 version = Column('version', Integer)
1119 version = Column('version', Integer)
1108
1120
@@ -1,504 +1,507 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 import url
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.lib import safe_unicode
32 from rhodecode.lib import safe_unicode
33 from rhodecode.lib.caching_query import FromCache
33 from rhodecode.lib.caching_query import FromCache
34
34
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 Notification
38 Notification
39 from rhodecode.lib.exceptions import DefaultUserException, \
39 from rhodecode.lib.exceptions import DefaultUserException, \
40 UserOwnsReposException
40 UserOwnsReposException
41
41
42 from sqlalchemy.exc import DatabaseError
42 from sqlalchemy.exc import DatabaseError
43 from rhodecode.lib import generate_api_key
43 from rhodecode.lib import generate_api_key
44 from sqlalchemy.orm import joinedload
44 from sqlalchemy.orm import joinedload
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 PERM_WEIGHTS = {'repository.none': 0,
49 PERM_WEIGHTS = {'repository.none': 0,
50 'repository.read': 1,
50 'repository.read': 1,
51 'repository.write': 3,
51 'repository.write': 3,
52 'repository.admin': 3}
52 'repository.admin': 3}
53
53
54
54
55 class UserModel(BaseModel):
55 class UserModel(BaseModel):
56
56
57 def __get_user(self, user):
57 def __get_user(self, user):
58 return self._get_instance(User, user)
58 return self._get_instance(User, user)
59
59
60 def get(self, user_id, cache=False):
60 def get(self, user_id, cache=False):
61 user = self.sa.query(User)
61 user = self.sa.query(User)
62 if cache:
62 if cache:
63 user = user.options(FromCache("sql_cache_short",
63 user = user.options(FromCache("sql_cache_short",
64 "get_user_%s" % user_id))
64 "get_user_%s" % user_id))
65 return user.get(user_id)
65 return user.get(user_id)
66
66
67 def get_by_username(self, username, cache=False, case_insensitive=False):
67 def get_by_username(self, username, cache=False, case_insensitive=False):
68
68
69 if case_insensitive:
69 if case_insensitive:
70 user = self.sa.query(User).filter(User.username.ilike(username))
70 user = self.sa.query(User).filter(User.username.ilike(username))
71 else:
71 else:
72 user = self.sa.query(User)\
72 user = self.sa.query(User)\
73 .filter(User.username == username)
73 .filter(User.username == username)
74 if cache:
74 if cache:
75 user = user.options(FromCache("sql_cache_short",
75 user = user.options(FromCache("sql_cache_short",
76 "get_user_%s" % username))
76 "get_user_%s" % username))
77 return user.scalar()
77 return user.scalar()
78
78
79 def get_by_api_key(self, api_key, cache=False):
79 def get_by_api_key(self, api_key, cache=False):
80 return User.get_by_api_key(api_key, cache)
80 return User.get_by_api_key(api_key, cache)
81
81
82 def create(self, form_data):
82 def create(self, form_data):
83 try:
83 try:
84 new_user = User()
84 new_user = User()
85 for k, v in form_data.items():
85 for k, v in form_data.items():
86 setattr(new_user, k, v)
86 setattr(new_user, k, v)
87
87
88 new_user.api_key = generate_api_key(form_data['username'])
88 new_user.api_key = generate_api_key(form_data['username'])
89 self.sa.add(new_user)
89 self.sa.add(new_user)
90 return new_user
90 return new_user
91 except:
91 except:
92 log.error(traceback.format_exc())
92 log.error(traceback.format_exc())
93 raise
93 raise
94
94
95
95
96 def create_or_update(self, username, password, email, name, lastname,
96 def create_or_update(self, username, password, email, name, lastname,
97 active=True, admin=False, ldap_dn=None):
97 active=True, admin=False, ldap_dn=None):
98 """
98 """
99 Creates a new instance if not found, or updates current one
99 Creates a new instance if not found, or updates current one
100
100
101 :param username:
101 :param username:
102 :param password:
102 :param password:
103 :param email:
103 :param email:
104 :param active:
104 :param active:
105 :param name:
105 :param name:
106 :param lastname:
106 :param lastname:
107 :param active:
107 :param active:
108 :param admin:
108 :param admin:
109 :param ldap_dn:
109 :param ldap_dn:
110 """
110 """
111
111
112 from rhodecode.lib.auth import get_crypt_password
112 from rhodecode.lib.auth import get_crypt_password
113
113
114 log.debug('Checking for %s account in RhodeCode database', username)
114 log.debug('Checking for %s account in RhodeCode database', username)
115 user = User.get_by_username(username, case_insensitive=True)
115 user = User.get_by_username(username, case_insensitive=True)
116 if user is None:
116 if user is None:
117 log.debug('creating new user %s', username)
117 log.debug('creating new user %s', username)
118 new_user = User()
118 new_user = User()
119 else:
119 else:
120 log.debug('updating user %s', username)
120 log.debug('updating user %s', username)
121 new_user = user
121 new_user = user
122
122
123 try:
123 try:
124 new_user.username = username
124 new_user.username = username
125 new_user.admin = admin
125 new_user.admin = admin
126 new_user.password = get_crypt_password(password)
126 new_user.password = get_crypt_password(password)
127 new_user.api_key = generate_api_key(username)
127 new_user.api_key = generate_api_key(username)
128 new_user.email = email
128 new_user.email = email
129 new_user.active = active
129 new_user.active = active
130 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
130 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
131 new_user.name = name
131 new_user.name = name
132 new_user.lastname = lastname
132 new_user.lastname = lastname
133 self.sa.add(new_user)
133 self.sa.add(new_user)
134 return new_user
134 return new_user
135 except (DatabaseError,):
135 except (DatabaseError,):
136 log.error(traceback.format_exc())
136 log.error(traceback.format_exc())
137 raise
137 raise
138
138
139
139
140 def create_for_container_auth(self, username, attrs):
140 def create_for_container_auth(self, username, attrs):
141 """
141 """
142 Creates the given user if it's not already in the database
142 Creates the given user if it's not already in the database
143
143
144 :param username:
144 :param username:
145 :param attrs:
145 :param attrs:
146 """
146 """
147 if self.get_by_username(username, case_insensitive=True) is None:
147 if self.get_by_username(username, case_insensitive=True) is None:
148
148
149 # autogenerate email for container account without one
149 # autogenerate email for container account without one
150 generate_email = lambda usr: '%s@container_auth.account' % usr
150 generate_email = lambda usr: '%s@container_auth.account' % usr
151
151
152 try:
152 try:
153 new_user = User()
153 new_user = User()
154 new_user.username = username
154 new_user.username = username
155 new_user.password = None
155 new_user.password = None
156 new_user.api_key = generate_api_key(username)
156 new_user.api_key = generate_api_key(username)
157 new_user.email = attrs['email']
157 new_user.email = attrs['email']
158 new_user.active = attrs.get('active', True)
158 new_user.active = attrs.get('active', True)
159 new_user.name = attrs['name'] or generate_email(username)
159 new_user.name = attrs['name'] or generate_email(username)
160 new_user.lastname = attrs['lastname']
160 new_user.lastname = attrs['lastname']
161
161
162 self.sa.add(new_user)
162 self.sa.add(new_user)
163 return new_user
163 return new_user
164 except (DatabaseError,):
164 except (DatabaseError,):
165 log.error(traceback.format_exc())
165 log.error(traceback.format_exc())
166 self.sa.rollback()
166 self.sa.rollback()
167 raise
167 raise
168 log.debug('User %s already exists. Skipping creation of account'
168 log.debug('User %s already exists. Skipping creation of account'
169 ' for container auth.', username)
169 ' for container auth.', username)
170 return None
170 return None
171
171
172 def create_ldap(self, username, password, user_dn, attrs):
172 def create_ldap(self, username, password, user_dn, attrs):
173 """
173 """
174 Checks if user is in database, if not creates this user marked
174 Checks if user is in database, if not creates this user marked
175 as ldap user
175 as ldap user
176
176
177 :param username:
177 :param username:
178 :param password:
178 :param password:
179 :param user_dn:
179 :param user_dn:
180 :param attrs:
180 :param attrs:
181 """
181 """
182 from rhodecode.lib.auth import get_crypt_password
182 from rhodecode.lib.auth import get_crypt_password
183 log.debug('Checking for such ldap account in RhodeCode database')
183 log.debug('Checking for such ldap account in RhodeCode database')
184 if self.get_by_username(username, case_insensitive=True) is None:
184 if self.get_by_username(username, case_insensitive=True) is None:
185
185
186 # autogenerate email for ldap account without one
186 # autogenerate email for ldap account without one
187 generate_email = lambda usr: '%s@ldap.account' % usr
187 generate_email = lambda usr: '%s@ldap.account' % usr
188
188
189 try:
189 try:
190 new_user = User()
190 new_user = User()
191 username = username.lower()
191 username = username.lower()
192 # add ldap account always lowercase
192 # add ldap account always lowercase
193 new_user.username = username
193 new_user.username = username
194 new_user.password = get_crypt_password(password)
194 new_user.password = get_crypt_password(password)
195 new_user.api_key = generate_api_key(username)
195 new_user.api_key = generate_api_key(username)
196 new_user.email = attrs['email'] or generate_email(username)
196 new_user.email = attrs['email'] or generate_email(username)
197 new_user.active = attrs.get('active', True)
197 new_user.active = attrs.get('active', True)
198 new_user.ldap_dn = safe_unicode(user_dn)
198 new_user.ldap_dn = safe_unicode(user_dn)
199 new_user.name = attrs['name']
199 new_user.name = attrs['name']
200 new_user.lastname = attrs['lastname']
200 new_user.lastname = attrs['lastname']
201
201
202 self.sa.add(new_user)
202 self.sa.add(new_user)
203 return new_user
203 return new_user
204 except (DatabaseError,):
204 except (DatabaseError,):
205 log.error(traceback.format_exc())
205 log.error(traceback.format_exc())
206 self.sa.rollback()
206 self.sa.rollback()
207 raise
207 raise
208 log.debug('this %s user exists skipping creation of ldap account',
208 log.debug('this %s user exists skipping creation of ldap account',
209 username)
209 username)
210 return None
210 return None
211
211
212 def create_registration(self, form_data):
212 def create_registration(self, form_data):
213 from rhodecode.model.notification import NotificationModel
213 from rhodecode.model.notification import NotificationModel
214
214
215 try:
215 try:
216 new_user = User()
216 new_user = User()
217 for k, v in form_data.items():
217 for k, v in form_data.items():
218 if k != 'admin':
218 if k != 'admin':
219 setattr(new_user, k, v)
219 setattr(new_user, k, v)
220
220
221 self.sa.add(new_user)
221 self.sa.add(new_user)
222 self.sa.flush()
222 self.sa.flush()
223
223
224 # notification to admins
224 # notification to admins
225 subject = _('new user registration')
225 subject = _('new user registration')
226 body = ('New user registration\n'
226 body = ('New user registration\n'
227 '---------------------\n'
227 '---------------------\n'
228 '- Username: %s\n'
228 '- Username: %s\n'
229 '- Full Name: %s\n'
229 '- Full Name: %s\n'
230 '- Email: %s\n')
230 '- Email: %s\n')
231 body = body % (new_user.username, new_user.full_name,
231 body = body % (new_user.username, new_user.full_name,
232 new_user.email)
232 new_user.email)
233 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
233 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
234 kw = {'registered_user_url':edit_url}
234 kw = {'registered_user_url':edit_url}
235 NotificationModel().create(created_by=new_user, subject=subject,
235 NotificationModel().create(created_by=new_user, subject=subject,
236 body=body, recipients=None,
236 body=body, recipients=None,
237 type_=Notification.TYPE_REGISTRATION,
237 type_=Notification.TYPE_REGISTRATION,
238 email_kwargs=kw)
238 email_kwargs=kw)
239
239
240 except:
240 except:
241 log.error(traceback.format_exc())
241 log.error(traceback.format_exc())
242 raise
242 raise
243
243
244 def update(self, user_id, form_data):
244 def update(self, user_id, form_data):
245 try:
245 try:
246 user = self.get(user_id, cache=False)
246 user = self.get(user_id, cache=False)
247 if user.username == 'default':
247 if user.username == 'default':
248 raise DefaultUserException(
248 raise DefaultUserException(
249 _("You can't Edit this user since it's"
249 _("You can't Edit this user since it's"
250 " crucial for entire application"))
250 " crucial for entire application"))
251
251
252 for k, v in form_data.items():
252 for k, v in form_data.items():
253 if k == 'new_password' and v != '':
253 if k == 'new_password' and v != '':
254 user.password = v
254 user.password = v
255 user.api_key = generate_api_key(user.username)
255 user.api_key = generate_api_key(user.username)
256 else:
256 else:
257 setattr(user, k, v)
257 setattr(user, k, v)
258
258
259 self.sa.add(user)
259 self.sa.add(user)
260 except:
260 except:
261 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
262 raise
262 raise
263
263
264 def update_my_account(self, user_id, form_data):
264 def update_my_account(self, user_id, form_data):
265 try:
265 try:
266 user = self.get(user_id, cache=False)
266 user = self.get(user_id, cache=False)
267 if user.username == 'default':
267 if user.username == 'default':
268 raise DefaultUserException(
268 raise DefaultUserException(
269 _("You can't Edit this user since it's"
269 _("You can't Edit this user since it's"
270 " crucial for entire application"))
270 " crucial for entire application"))
271 for k, v in form_data.items():
271 for k, v in form_data.items():
272 if k == 'new_password' and v != '':
272 if k == 'new_password' and v != '':
273 user.password = v
273 user.password = v
274 user.api_key = generate_api_key(user.username)
274 user.api_key = generate_api_key(user.username)
275 else:
275 else:
276 if k not in ['admin', 'active']:
276 if k not in ['admin', 'active']:
277 setattr(user, k, v)
277 setattr(user, k, v)
278
278
279 self.sa.add(user)
279 self.sa.add(user)
280 except:
280 except:
281 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
282 raise
282 raise
283
283
284 def delete(self, user_id):
284 def delete(self, user):
285 user = self.__get_user(user)
286
285 try:
287 try:
286 user = self.get(user_id, cache=False)
287 if user.username == 'default':
288 if user.username == 'default':
288 raise DefaultUserException(
289 raise DefaultUserException(
289 _("You can't remove this user since it's"
290 _("You can't remove this user since it's"
290 " crucial for entire application"))
291 " crucial for entire application"))
291 if user.repositories:
292 if user.repositories:
292 raise UserOwnsReposException(_('This user still owns %s '
293 raise UserOwnsReposException(_('This user still owns %s '
293 'repositories and cannot be '
294 'repositories and cannot be '
294 'removed. Switch owners or '
295 'removed. Switch owners or '
295 'remove those repositories') \
296 'remove those repositories') \
296 % user.repositories)
297 % user.repositories)
297 self.sa.delete(user)
298 self.sa.delete(user)
298 except:
299 except:
299 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
300 raise
301 raise
301
302
302 def reset_password_link(self, data):
303 def reset_password_link(self, data):
303 from rhodecode.lib.celerylib import tasks, run_task
304 from rhodecode.lib.celerylib import tasks, run_task
304 run_task(tasks.send_password_link, data['email'])
305 run_task(tasks.send_password_link, data['email'])
305
306
306 def reset_password(self, data):
307 def reset_password(self, data):
307 from rhodecode.lib.celerylib import tasks, run_task
308 from rhodecode.lib.celerylib import tasks, run_task
308 run_task(tasks.reset_user_password, data['email'])
309 run_task(tasks.reset_user_password, data['email'])
309
310
310 def fill_data(self, auth_user, user_id=None, api_key=None):
311 def fill_data(self, auth_user, user_id=None, api_key=None):
311 """
312 """
312 Fetches auth_user by user_id,or api_key if present.
313 Fetches auth_user by user_id,or api_key if present.
313 Fills auth_user attributes with those taken from database.
314 Fills auth_user attributes with those taken from database.
314 Additionally set's is_authenitated if lookup fails
315 Additionally set's is_authenitated if lookup fails
315 present in database
316 present in database
316
317
317 :param auth_user: instance of user to set attributes
318 :param auth_user: instance of user to set attributes
318 :param user_id: user id to fetch by
319 :param user_id: user id to fetch by
319 :param api_key: api key to fetch by
320 :param api_key: api key to fetch by
320 """
321 """
321 if user_id is None and api_key is None:
322 if user_id is None and api_key is None:
322 raise Exception('You need to pass user_id or api_key')
323 raise Exception('You need to pass user_id or api_key')
323
324
324 try:
325 try:
325 if api_key:
326 if api_key:
326 dbuser = self.get_by_api_key(api_key)
327 dbuser = self.get_by_api_key(api_key)
327 else:
328 else:
328 dbuser = self.get(user_id)
329 dbuser = self.get(user_id)
329
330
330 if dbuser is not None and dbuser.active:
331 if dbuser is not None and dbuser.active:
331 log.debug('filling %s data', dbuser)
332 log.debug('filling %s data', dbuser)
332 for k, v in dbuser.get_dict().items():
333 for k, v in dbuser.get_dict().items():
333 setattr(auth_user, k, v)
334 setattr(auth_user, k, v)
334 else:
335 else:
335 return False
336 return False
336
337
337 except:
338 except:
338 log.error(traceback.format_exc())
339 log.error(traceback.format_exc())
339 auth_user.is_authenticated = False
340 auth_user.is_authenticated = False
340 return False
341 return False
341
342
342 return True
343 return True
343
344
344 def fill_perms(self, user):
345 def fill_perms(self, user):
345 """
346 """
346 Fills user permission attribute with permissions taken from database
347 Fills user permission attribute with permissions taken from database
347 works for permissions given for repositories, and for permissions that
348 works for permissions given for repositories, and for permissions that
348 are granted to groups
349 are granted to groups
349
350
350 :param user: user instance to fill his perms
351 :param user: user instance to fill his perms
351 """
352 """
352
353
353 user.permissions['repositories'] = {}
354 user.permissions['repositories'] = {}
354 user.permissions['global'] = set()
355 user.permissions['global'] = set()
355
356
356 #======================================================================
357 #======================================================================
357 # fetch default permissions
358 # fetch default permissions
358 #======================================================================
359 #======================================================================
359 default_user = User.get_by_username('default', cache=True)
360 default_user = User.get_by_username('default', cache=True)
360 default_user_id = default_user.user_id
361 default_user_id = default_user.user_id
361
362
362 default_perms = Permission.get_default_perms(default_user_id)
363 default_perms = Permission.get_default_perms(default_user_id)
363
364
364 if user.is_admin:
365 if user.is_admin:
365 #==================================================================
366 #==================================================================
366 # #admin have all default rights set to admin
367 # #admin have all default rights set to admin
367 #==================================================================
368 #==================================================================
368 user.permissions['global'].add('hg.admin')
369 user.permissions['global'].add('hg.admin')
369
370
370 for perm in default_perms:
371 for perm in default_perms:
371 p = 'repository.admin'
372 p = 'repository.admin'
372 user.permissions['repositories'][perm.UserRepoToPerm.
373 user.permissions['repositories'][perm.UserRepoToPerm.
373 repository.repo_name] = p
374 repository.repo_name] = p
374
375
375 else:
376 else:
376 #==================================================================
377 #==================================================================
377 # set default permissions
378 # set default permissions
378 #==================================================================
379 #==================================================================
379 uid = user.user_id
380 uid = user.user_id
380
381
381 # default global
382 # default global
382 default_global_perms = self.sa.query(UserToPerm)\
383 default_global_perms = self.sa.query(UserToPerm)\
383 .filter(UserToPerm.user_id == default_user_id)
384 .filter(UserToPerm.user_id == default_user_id)
384
385
385 for perm in default_global_perms:
386 for perm in default_global_perms:
386 user.permissions['global'].add(perm.permission.permission_name)
387 user.permissions['global'].add(perm.permission.permission_name)
387
388
388 # default for repositories
389 # default for repositories
389 for perm in default_perms:
390 for perm in default_perms:
390 if perm.Repository.private and not (perm.Repository.user_id ==
391 if perm.Repository.private and not (perm.Repository.user_id ==
391 uid):
392 uid):
392 # disable defaults for private repos,
393 # disable defaults for private repos,
393 p = 'repository.none'
394 p = 'repository.none'
394 elif perm.Repository.user_id == uid:
395 elif perm.Repository.user_id == uid:
395 # set admin if owner
396 # set admin if owner
396 p = 'repository.admin'
397 p = 'repository.admin'
397 else:
398 else:
398 p = perm.Permission.permission_name
399 p = perm.Permission.permission_name
399
400
400 user.permissions['repositories'][perm.UserRepoToPerm.
401 user.permissions['repositories'][perm.UserRepoToPerm.
401 repository.repo_name] = p
402 repository.repo_name] = p
402
403
403 #==================================================================
404 #==================================================================
404 # overwrite default with user permissions if any
405 # overwrite default with user permissions if any
405 #==================================================================
406 #==================================================================
406
407
407 # user global
408 # user global
408 user_perms = self.sa.query(UserToPerm)\
409 user_perms = self.sa.query(UserToPerm)\
409 .options(joinedload(UserToPerm.permission))\
410 .options(joinedload(UserToPerm.permission))\
410 .filter(UserToPerm.user_id == uid).all()
411 .filter(UserToPerm.user_id == uid).all()
411
412
412 for perm in user_perms:
413 for perm in user_perms:
413 user.permissions['global'].add(perm.permission.permission_name)
414 user.permissions['global'].add(perm.permission.permission_name)
414
415
415 # user repositories
416 # user repositories
416 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
417 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
417 Repository)\
418 Repository)\
418 .join((Repository, UserRepoToPerm.repository_id ==
419 .join((Repository, UserRepoToPerm.repository_id ==
419 Repository.repo_id))\
420 Repository.repo_id))\
420 .join((Permission, UserRepoToPerm.permission_id ==
421 .join((Permission, UserRepoToPerm.permission_id ==
421 Permission.permission_id))\
422 Permission.permission_id))\
422 .filter(UserRepoToPerm.user_id == uid).all()
423 .filter(UserRepoToPerm.user_id == uid).all()
423
424
424 for perm in user_repo_perms:
425 for perm in user_repo_perms:
425 # set admin if owner
426 # set admin if owner
426 if perm.Repository.user_id == uid:
427 if perm.Repository.user_id == uid:
427 p = 'repository.admin'
428 p = 'repository.admin'
428 else:
429 else:
429 p = perm.Permission.permission_name
430 p = perm.Permission.permission_name
430 user.permissions['repositories'][perm.UserRepoToPerm.
431 user.permissions['repositories'][perm.UserRepoToPerm.
431 repository.repo_name] = p
432 repository.repo_name] = p
432
433
433 #==================================================================
434 #==================================================================
434 # check if user is part of groups for this repository and fill in
435 # check if user is part of groups for this repository and fill in
435 # (or replace with higher) permissions
436 # (or replace with higher) permissions
436 #==================================================================
437 #==================================================================
437
438
438 # users group global
439 # users group global
439 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
440 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
440 .options(joinedload(UsersGroupToPerm.permission))\
441 .options(joinedload(UsersGroupToPerm.permission))\
441 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
442 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
442 UsersGroupMember.users_group_id))\
443 UsersGroupMember.users_group_id))\
443 .filter(UsersGroupMember.user_id == uid).all()
444 .filter(UsersGroupMember.user_id == uid).all()
444
445
445 for perm in user_perms_from_users_groups:
446 for perm in user_perms_from_users_groups:
446 user.permissions['global'].add(perm.permission.permission_name)
447 user.permissions['global'].add(perm.permission.permission_name)
447
448
448 # users group repositories
449 # users group repositories
449 user_repo_perms_from_users_groups = self.sa.query(
450 user_repo_perms_from_users_groups = self.sa.query(
450 UsersGroupRepoToPerm,
451 UsersGroupRepoToPerm,
451 Permission, Repository,)\
452 Permission, Repository,)\
452 .join((Repository, UsersGroupRepoToPerm.repository_id ==
453 .join((Repository, UsersGroupRepoToPerm.repository_id ==
453 Repository.repo_id))\
454 Repository.repo_id))\
454 .join((Permission, UsersGroupRepoToPerm.permission_id ==
455 .join((Permission, UsersGroupRepoToPerm.permission_id ==
455 Permission.permission_id))\
456 Permission.permission_id))\
456 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
457 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
457 UsersGroupMember.users_group_id))\
458 UsersGroupMember.users_group_id))\
458 .filter(UsersGroupMember.user_id == uid).all()
459 .filter(UsersGroupMember.user_id == uid).all()
459
460
460 for perm in user_repo_perms_from_users_groups:
461 for perm in user_repo_perms_from_users_groups:
461 p = perm.Permission.permission_name
462 p = perm.Permission.permission_name
462 cur_perm = user.permissions['repositories'][perm.
463 cur_perm = user.permissions['repositories'][perm.
463 UsersGroupRepoToPerm.
464 UsersGroupRepoToPerm.
464 repository.repo_name]
465 repository.repo_name]
465 # overwrite permission only if it's greater than permission
466 # overwrite permission only if it's greater than permission
466 # given from other sources
467 # given from other sources
467 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
468 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
468 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
469 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
469 repository.repo_name] = p
470 repository.repo_name] = p
470
471
471 return user
472 return user
472
473
473
474
475 def has_perm(self, user, perm):
474 def has_perm(self, user, perm):
476 if not isinstance(perm, Permission):
475 if not isinstance(perm, Permission):
477 raise Exception('perm needs to be an instance of Permission class')
476 raise Exception('perm needs to be an instance of Permission class '
477 'got %s instead' % type(perm))
478
478
479 user = self.__get_user(user)
479 user = self.__get_user(user)
480
480
481 return UserToPerm.query().filter(UserToPerm.user == user.user)\
481 return UserToPerm.query().filter(UserToPerm.user == user)\
482 .filter(UserToPerm.permission == perm).scalar() is not None
482 .filter(UserToPerm.permission == perm).scalar() is not None
483
483
484 def grant_perm(self, user, perm):
484 def grant_perm(self, user, perm):
485 if not isinstance(perm, Permission):
485 if not isinstance(perm, Permission):
486 raise Exception('perm needs to be an instance of Permission class')
486 raise Exception('perm needs to be an instance of Permission class '
487 'got %s instead' % type(perm))
487
488
488 user = self.__get_user(user)
489 user = self.__get_user(user)
489
490
490 new = UserToPerm()
491 new = UserToPerm()
491 new.user = user.user
492 new.user = user
492 new.permission = perm
493 new.permission = perm
493 self.sa.add(new)
494 self.sa.add(new)
494
495
495
496
496 def revoke_perm(self, user, perm):
497 def revoke_perm(self, user, perm):
497 if not isinstance(perm, Permission):
498 if not isinstance(perm, Permission):
498 raise Exception('perm needs to be an instance of Permission class')
499 raise Exception('perm needs to be an instance of Permission class '
500 'got %s instead' % type(perm))
499
501
500 user = self.__get_user(user)
502 user = self.__get_user(user)
501
503
502 obj = UserToPerm.query().filter(UserToPerm.user == user.user)\
504 obj = UserToPerm.query().filter(UserToPerm.user == user)\
503 .filter(UserToPerm.permission == perm).one()
505 .filter(UserToPerm.permission == perm).scalar()
504 self.sa.delete(obj)
506 if obj:
507 self.sa.delete(obj)
@@ -1,96 +1,110 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 import time
11 import time
12 import logging
12 import logging
13 from os.path import join as jn
13 from os.path import join as jn
14
14
15 from unittest import TestCase
15 from unittest import TestCase
16 from tempfile import _RandomNameSequence
16 from tempfile import _RandomNameSequence
17
17
18 from paste.deploy import loadapp
18 from paste.deploy import loadapp
19 from paste.script.appinstall import SetupCommand
19 from paste.script.appinstall import SetupCommand
20 from pylons import config, url
20 from pylons import config, url
21 from routes.util import URLGenerator
21 from routes.util import URLGenerator
22 from webtest import TestApp
22 from webtest import TestApp
23
23
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.db import User
25 from rhodecode.model.db import User
26
26
27 import pylons.test
27 import pylons.test
28
28
29 os.environ['TZ'] = 'UTC'
29 os.environ['TZ'] = 'UTC'
30 time.tzset()
30 time.tzset()
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
34 __all__ = [
35 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
35 'environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
36 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ]
36 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
37 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
38 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
39 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL'
40 ]
37
41
38 # Invoke websetup with the current config file
42 # Invoke websetup with the current config file
39 # SetupCommand('setup-app').run([config_file])
43 # SetupCommand('setup-app').run([config_file])
40
44
41 ##RUNNING DESIRED TESTS
45 ##RUNNING DESIRED TESTS
42 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
46 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
43 # nosetests --pdb --pdb-failures
47 # nosetests --pdb --pdb-failures
44 environ = {}
48 environ = {}
45
49
46 #SOME GLOBALS FOR TESTS
50 #SOME GLOBALS FOR TESTS
47
51
48 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
52 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
49 TEST_USER_ADMIN_LOGIN = 'test_admin'
53 TEST_USER_ADMIN_LOGIN = 'test_admin'
50 TEST_USER_ADMIN_PASS = 'test12'
54 TEST_USER_ADMIN_PASS = 'test12'
55 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
56
57 TEST_USER_REGULAR_LOGIN = 'test_regular'
58 TEST_USER_REGULAR_PASS = 'test12'
59 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
60
61 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
62 TEST_USER_REGULAR2_PASS = 'test12'
63 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
64
51 HG_REPO = 'vcs_test_hg'
65 HG_REPO = 'vcs_test_hg'
52 GIT_REPO = 'vcs_test_git'
66 GIT_REPO = 'vcs_test_git'
53
67
54 NEW_HG_REPO = 'vcs_test_hg_new'
68 NEW_HG_REPO = 'vcs_test_hg_new'
55 NEW_GIT_REPO = 'vcs_test_git_new'
69 NEW_GIT_REPO = 'vcs_test_git_new'
56
70
57 HG_FORK = 'vcs_test_hg_fork'
71 HG_FORK = 'vcs_test_hg_fork'
58 GIT_FORK = 'vcs_test_git_fork'
72 GIT_FORK = 'vcs_test_git_fork'
59
73
60 class TestController(TestCase):
74 class TestController(TestCase):
61
75
62 def __init__(self, *args, **kwargs):
76 def __init__(self, *args, **kwargs):
63 wsgiapp = pylons.test.pylonsapp
77 wsgiapp = pylons.test.pylonsapp
64 config = wsgiapp.config
78 config = wsgiapp.config
65
79
66 self.app = TestApp(wsgiapp)
80 self.app = TestApp(wsgiapp)
67 url._push_object(URLGenerator(config['routes.map'], environ))
81 url._push_object(URLGenerator(config['routes.map'], environ))
68 self.Session = Session
82 self.Session = Session
69 self.index_location = config['app_conf']['index_dir']
83 self.index_location = config['app_conf']['index_dir']
70 TestCase.__init__(self, *args, **kwargs)
84 TestCase.__init__(self, *args, **kwargs)
71
85
72 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
86 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
73 password=TEST_USER_ADMIN_PASS):
87 password=TEST_USER_ADMIN_PASS):
74 self._logged_username = username
88 self._logged_username = username
75 response = self.app.post(url(controller='login', action='index'),
89 response = self.app.post(url(controller='login', action='index'),
76 {'username':username,
90 {'username':username,
77 'password':password})
91 'password':password})
78
92
79 if 'invalid user name' in response.body:
93 if 'invalid user name' in response.body:
80 self.fail('could not login using %s %s' % (username, password))
94 self.fail('could not login using %s %s' % (username, password))
81
95
82 self.assertEqual(response.status, '302 Found')
96 self.assertEqual(response.status, '302 Found')
83 ses = response.session['rhodecode_user']
97 ses = response.session['rhodecode_user']
84 self.assertEqual(ses.get('username'), username)
98 self.assertEqual(ses.get('username'), username)
85 response = response.follow()
99 response = response.follow()
86 self.assertEqual(ses.get('is_authenticated'), True)
100 self.assertEqual(ses.get('is_authenticated'), True)
87
101
88 return response.session['rhodecode_user']
102 return response.session['rhodecode_user']
89
103
90 def _get_logged_user(self):
104 def _get_logged_user(self):
91 return User.get_by_username(self._logged_username)
105 return User.get_by_username(self._logged_username)
92
106
93 def checkSessionFlash(self, response, msg):
107 def checkSessionFlash(self, response, msg):
94 self.assertTrue('flash' in response.session)
108 self.assertTrue('flash' in response.session)
95 self.assertTrue(msg in response.session['flash'][0][1])
109 self.assertTrue(msg in response.session['flash'][0][1])
96
110
@@ -1,122 +1,179 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import User
2 from rhodecode.model.db import User, Permission
3 from rhodecode.lib.auth import check_password
3 from rhodecode.lib.auth import check_password
4 from sqlalchemy.orm.exc import NoResultFound
4 from sqlalchemy.orm.exc import NoResultFound
5 from rhodecode.model.user import UserModel
5
6
6 class TestAdminUsersController(TestController):
7 class TestAdminUsersController(TestController):
7
8
8 def test_index(self):
9 def test_index(self):
10 self.log_user()
9 response = self.app.get(url('users'))
11 response = self.app.get(url('users'))
10 # Test response...
12 # Test response...
11
13
12 def test_index_as_xml(self):
14 def test_index_as_xml(self):
13 response = self.app.get(url('formatted_users', format='xml'))
15 response = self.app.get(url('formatted_users', format='xml'))
14
16
15 def test_create(self):
17 def test_create(self):
16 self.log_user()
18 self.log_user()
17 username = 'newtestuser'
19 username = 'newtestuser'
18 password = 'test12'
20 password = 'test12'
19 password_confirmation = password
21 password_confirmation = password
20 name = 'name'
22 name = 'name'
21 lastname = 'lastname'
23 lastname = 'lastname'
22 email = 'mail@mail.com'
24 email = 'mail@mail.com'
23
25
24 response = self.app.post(url('users'), {'username':username,
26 response = self.app.post(url('users'),
25 'password':password,
27 {'username':username,
26 'password_confirmation':password_confirmation,
28 'password':password,
27 'name':name,
29 'password_confirmation':password_confirmation,
28 'active':True,
30 'name':name,
29 'lastname':lastname,
31 'active':True,
30 'email':email})
32 'lastname':lastname,
33 'email':email})
31
34
32
35
33 assert '''created user %s''' % (username) in response.session['flash'][0], 'No flash message about new user'
36 self.assertTrue('''created user %s''' % (username) in
37 response.session['flash'][0])
34
38
35 new_user = self.Session.query(User).filter(User.username == username).one()
39 new_user = self.Session.query(User).\
36
40 filter(User.username == username).one()
37
41
38 assert new_user.username == username, 'wrong info about username'
42 self.assertEqual(new_user.username,username)
39 assert check_password(password, new_user.password) == True , 'wrong info about password'
43 self.assertEqual(check_password(password, new_user.password),True)
40 assert new_user.name == name, 'wrong info about name'
44 self.assertEqual(new_user.name,name)
41 assert new_user.lastname == lastname, 'wrong info about lastname'
45 self.assertEqual(new_user.lastname,lastname)
42 assert new_user.email == email, 'wrong info about email'
46 self.assertEqual(new_user.email,email)
43
44
47
45 response.follow()
48 response.follow()
46 response = response.follow()
49 response = response.follow()
47 assert """edit">newtestuser</a>""" in response.body
50 self.assertTrue("""edit">newtestuser</a>""" in response.body)
48
51
49 def test_create_err(self):
52 def test_create_err(self):
50 self.log_user()
53 self.log_user()
51 username = 'new_user'
54 username = 'new_user'
52 password = ''
55 password = ''
53 name = 'name'
56 name = 'name'
54 lastname = 'lastname'
57 lastname = 'lastname'
55 email = 'errmail.com'
58 email = 'errmail.com'
56
59
57 response = self.app.post(url('users'), {'username':username,
60 response = self.app.post(url('users'), {'username':username,
58 'password':password,
61 'password':password,
59 'name':name,
62 'name':name,
60 'active':False,
63 'active':False,
61 'lastname':lastname,
64 'lastname':lastname,
62 'email':email})
65 'email':email})
63
66
64 assert """<span class="error-message">Invalid username</span>""" in response.body
67 self.assertTrue("""<span class="error-message">Invalid username</span>""" in response.body)
65 assert """<span class="error-message">Please enter a value</span>""" in response.body
68 self.assertTrue("""<span class="error-message">Please enter a value</span>""" in response.body)
66 assert """<span class="error-message">An email address must contain a single @</span>""" in response.body
69 self.assertTrue("""<span class="error-message">An email address must contain a single @</span>""" in response.body)
67
70
68 def get_user():
71 def get_user():
69 self.Session.query(User).filter(User.username == username).one()
72 self.Session.query(User).filter(User.username == username).one()
70
73
71 self.assertRaises(NoResultFound, get_user), 'found user in database'
74 self.assertRaises(NoResultFound, get_user), 'found user in database'
72
75
73 def test_new(self):
76 def test_new(self):
77 self.log_user()
74 response = self.app.get(url('new_user'))
78 response = self.app.get(url('new_user'))
75
79
76 def test_new_as_xml(self):
80 def test_new_as_xml(self):
77 response = self.app.get(url('formatted_new_user', format='xml'))
81 response = self.app.get(url('formatted_new_user', format='xml'))
78
82
79 def test_update(self):
83 def test_update(self):
80 response = self.app.put(url('user', id=1))
84 response = self.app.put(url('user', id=1))
81
85
82 def test_update_browser_fakeout(self):
86 def test_update_browser_fakeout(self):
83 response = self.app.post(url('user', id=1), params=dict(_method='put'))
87 response = self.app.post(url('user', id=1), params=dict(_method='put'))
84
88
85 def test_delete(self):
89 def test_delete(self):
86 self.log_user()
90 self.log_user()
87 username = 'newtestuserdeleteme'
91 username = 'newtestuserdeleteme'
88 password = 'test12'
92 password = 'test12'
89 name = 'name'
93 name = 'name'
90 lastname = 'lastname'
94 lastname = 'lastname'
91 email = 'todeletemail@mail.com'
95 email = 'todeletemail@mail.com'
92
96
93 response = self.app.post(url('users'), {'username':username,
97 response = self.app.post(url('users'), {'username':username,
94 'password':password,
98 'password':password,
95 'password_confirmation':password,
99 'password_confirmation':password,
96 'name':name,
100 'name':name,
97 'active':True,
101 'active':True,
98 'lastname':lastname,
102 'lastname':lastname,
99 'email':email})
103 'email':email})
100
104
101 response = response.follow()
105 response = response.follow()
102
106
103 new_user = self.Session.query(User).filter(User.username == username).one()
107 new_user = self.Session.query(User)\
108 .filter(User.username == username).one()
104 response = self.app.delete(url('user', id=new_user.user_id))
109 response = self.app.delete(url('user', id=new_user.user_id))
105
110
106 assert """successfully deleted user""" in response.session['flash'][0], 'No info about user deletion'
111 self.assertTrue("""successfully deleted user""" in
112 response.session['flash'][0])
107
113
108
114
109 def test_delete_browser_fakeout(self):
115 def test_delete_browser_fakeout(self):
110 response = self.app.post(url('user', id=1), params=dict(_method='delete'))
116 response = self.app.post(url('user', id=1),
117 params=dict(_method='delete'))
111
118
112 def test_show(self):
119 def test_show(self):
113 response = self.app.get(url('user', id=1))
120 response = self.app.get(url('user', id=1))
114
121
115 def test_show_as_xml(self):
122 def test_show_as_xml(self):
116 response = self.app.get(url('formatted_user', id=1, format='xml'))
123 response = self.app.get(url('formatted_user', id=1, format='xml'))
117
124
118 def test_edit(self):
125 def test_edit(self):
119 response = self.app.get(url('edit_user', id=1))
126 self.log_user()
127 user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
128 response = self.app.get(url('edit_user', id=user.user_id))
129
130
131 def test_add_perm_create_repo(self):
132 self.log_user()
133 perm_none = Permission.get_by_key('hg.create.none')
134 perm_create = Permission.get_by_key('hg.create.repository')
135
136 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
137
138
139 #User should have None permission on creation repository
140 self.assertEqual(UserModel().has_perm(user, perm_none), False)
141 self.assertEqual(UserModel().has_perm(user, perm_create), False)
142
143 response = self.app.post(url('user_perm', id=user.user_id),
144 params=dict(_method='put',
145 create_repo_perm=True))
146
147 perm_none = Permission.get_by_key('hg.create.none')
148 perm_create = Permission.get_by_key('hg.create.repository')
149
150 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
151 #User should have None permission on creation repository
152 self.assertEqual(UserModel().has_perm(user, perm_none), False)
153 self.assertEqual(UserModel().has_perm(user, perm_create), True)
154
155 def test_revoke_perm_create_repo(self):
156 self.log_user()
157 perm_none = Permission.get_by_key('hg.create.none')
158 perm_create = Permission.get_by_key('hg.create.repository')
159
160 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
161
162
163 #User should have None permission on creation repository
164 self.assertEqual(UserModel().has_perm(user, perm_none), False)
165 self.assertEqual(UserModel().has_perm(user, perm_create), False)
166
167 response = self.app.post(url('user_perm', id=user.user_id),
168 params=dict(_method='put'))
169
170 perm_none = Permission.get_by_key('hg.create.none')
171 perm_create = Permission.get_by_key('hg.create.repository')
172
173 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
174 #User should have None permission on creation repository
175 self.assertEqual(UserModel().has_perm(user, perm_none), True)
176 self.assertEqual(UserModel().has_perm(user, perm_create), False)
120
177
121 def test_edit_as_xml(self):
178 def test_edit_as_xml(self):
122 response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
179 response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
@@ -1,362 +1,410 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 UsersGroup, UsersGroupMember
8 UsersGroup, UsersGroupMember, Permission
9 from sqlalchemy.exc import IntegrityError
9 from sqlalchemy.exc import IntegrityError
10 from rhodecode.model.user import UserModel
10 from rhodecode.model.user import UserModel
11
11
12 from rhodecode.model.meta import Session
12 from rhodecode.model.meta import Session
13 from rhodecode.model.notification import NotificationModel
13 from rhodecode.model.notification import NotificationModel
14 from rhodecode.model.users_group import UsersGroupModel
14 from rhodecode.model.users_group import UsersGroupModel
15
15
16 class TestReposGroups(unittest.TestCase):
16 class TestReposGroups(unittest.TestCase):
17
17
18 def setUp(self):
18 def setUp(self):
19 self.g1 = self.__make_group('test1', skip_if_exists=True)
19 self.g1 = self.__make_group('test1', skip_if_exists=True)
20 self.g2 = self.__make_group('test2', skip_if_exists=True)
20 self.g2 = self.__make_group('test2', skip_if_exists=True)
21 self.g3 = self.__make_group('test3', skip_if_exists=True)
21 self.g3 = self.__make_group('test3', skip_if_exists=True)
22
22
23 def tearDown(self):
23 def tearDown(self):
24 print 'out'
24 print 'out'
25
25
26 def __check_path(self, *path):
26 def __check_path(self, *path):
27 path = [TESTS_TMP_PATH] + list(path)
27 path = [TESTS_TMP_PATH] + list(path)
28 path = os.path.join(*path)
28 path = os.path.join(*path)
29 return os.path.isdir(path)
29 return os.path.isdir(path)
30
30
31 def _check_folders(self):
31 def _check_folders(self):
32 print os.listdir(TESTS_TMP_PATH)
32 print os.listdir(TESTS_TMP_PATH)
33
33
34 def __make_group(self, path, desc='desc', parent_id=None,
34 def __make_group(self, path, desc='desc', parent_id=None,
35 skip_if_exists=False):
35 skip_if_exists=False):
36
36
37 gr = RepoGroup.get_by_group_name(path)
37 gr = RepoGroup.get_by_group_name(path)
38 if gr and skip_if_exists:
38 if gr and skip_if_exists:
39 return gr
39 return gr
40
40
41 form_data = dict(group_name=path,
41 form_data = dict(group_name=path,
42 group_description=desc,
42 group_description=desc,
43 group_parent_id=parent_id)
43 group_parent_id=parent_id)
44 gr = ReposGroupModel().create(form_data)
44 gr = ReposGroupModel().create(form_data)
45 Session.commit()
45 Session.commit()
46 return gr
46 return gr
47
47
48 def __delete_group(self, id_):
48 def __delete_group(self, id_):
49 ReposGroupModel().delete(id_)
49 ReposGroupModel().delete(id_)
50
50
51
51
52 def __update_group(self, id_, path, desc='desc', parent_id=None):
52 def __update_group(self, id_, path, desc='desc', parent_id=None):
53 form_data = dict(group_name=path,
53 form_data = dict(group_name=path,
54 group_description=desc,
54 group_description=desc,
55 group_parent_id=parent_id)
55 group_parent_id=parent_id)
56
56
57 gr = ReposGroupModel().update(id_, form_data)
57 gr = ReposGroupModel().update(id_, form_data)
58 return gr
58 return gr
59
59
60 def test_create_group(self):
60 def test_create_group(self):
61 g = self.__make_group('newGroup')
61 g = self.__make_group('newGroup')
62 self.assertEqual(g.full_path, 'newGroup')
62 self.assertEqual(g.full_path, 'newGroup')
63
63
64 self.assertTrue(self.__check_path('newGroup'))
64 self.assertTrue(self.__check_path('newGroup'))
65
65
66
66
67 def test_create_same_name_group(self):
67 def test_create_same_name_group(self):
68 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
68 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
69 Session.rollback()
69 Session.rollback()
70
70
71 def test_same_subgroup(self):
71 def test_same_subgroup(self):
72 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
72 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
73 self.assertEqual(sg1.parent_group, self.g1)
73 self.assertEqual(sg1.parent_group, self.g1)
74 self.assertEqual(sg1.full_path, 'test1/sub1')
74 self.assertEqual(sg1.full_path, 'test1/sub1')
75 self.assertTrue(self.__check_path('test1', 'sub1'))
75 self.assertTrue(self.__check_path('test1', 'sub1'))
76
76
77 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
77 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
78 self.assertEqual(ssg1.parent_group, sg1)
78 self.assertEqual(ssg1.parent_group, sg1)
79 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
79 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
80 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
80 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
81
81
82
82
83 def test_remove_group(self):
83 def test_remove_group(self):
84 sg1 = self.__make_group('deleteme')
84 sg1 = self.__make_group('deleteme')
85 self.__delete_group(sg1.group_id)
85 self.__delete_group(sg1.group_id)
86
86
87 self.assertEqual(RepoGroup.get(sg1.group_id), None)
87 self.assertEqual(RepoGroup.get(sg1.group_id), None)
88 self.assertFalse(self.__check_path('deteteme'))
88 self.assertFalse(self.__check_path('deteteme'))
89
89
90 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
90 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
91 self.__delete_group(sg1.group_id)
91 self.__delete_group(sg1.group_id)
92
92
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
94 self.assertFalse(self.__check_path('test1', 'deteteme'))
94 self.assertFalse(self.__check_path('test1', 'deteteme'))
95
95
96
96
97 def test_rename_single_group(self):
97 def test_rename_single_group(self):
98 sg1 = self.__make_group('initial')
98 sg1 = self.__make_group('initial')
99
99
100 new_sg1 = self.__update_group(sg1.group_id, 'after')
100 new_sg1 = self.__update_group(sg1.group_id, 'after')
101 self.assertTrue(self.__check_path('after'))
101 self.assertTrue(self.__check_path('after'))
102 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
102 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
103
103
104
104
105 def test_update_group_parent(self):
105 def test_update_group_parent(self):
106
106
107 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
107 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
108
108
109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
110 self.assertTrue(self.__check_path('test1', 'after'))
110 self.assertTrue(self.__check_path('test1', 'after'))
111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
112
112
113
113
114 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
114 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
115 self.assertTrue(self.__check_path('test3', 'after'))
115 self.assertTrue(self.__check_path('test3', 'after'))
116 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
116 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
117
117
118
118
119 new_sg1 = self.__update_group(sg1.group_id, 'hello')
119 new_sg1 = self.__update_group(sg1.group_id, 'hello')
120 self.assertTrue(self.__check_path('hello'))
120 self.assertTrue(self.__check_path('hello'))
121
121
122 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
122 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
123
123
124
124
125
125
126 def test_subgrouping_with_repo(self):
126 def test_subgrouping_with_repo(self):
127
127
128 g1 = self.__make_group('g1')
128 g1 = self.__make_group('g1')
129 g2 = self.__make_group('g2')
129 g2 = self.__make_group('g2')
130
130
131 # create new repo
131 # create new repo
132 form_data = dict(repo_name='john',
132 form_data = dict(repo_name='john',
133 repo_name_full='john',
133 repo_name_full='john',
134 fork_name=None,
134 fork_name=None,
135 description=None,
135 description=None,
136 repo_group=None,
136 repo_group=None,
137 private=False,
137 private=False,
138 repo_type='hg',
138 repo_type='hg',
139 clone_uri=None)
139 clone_uri=None)
140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 r = RepoModel().create(form_data, cur_user)
141 r = RepoModel().create(form_data, cur_user)
142
142
143 self.assertEqual(r.repo_name, 'john')
143 self.assertEqual(r.repo_name, 'john')
144
144
145 # put repo into group
145 # put repo into group
146 form_data = form_data
146 form_data = form_data
147 form_data['repo_group'] = g1.group_id
147 form_data['repo_group'] = g1.group_id
148 form_data['perms_new'] = []
148 form_data['perms_new'] = []
149 form_data['perms_updates'] = []
149 form_data['perms_updates'] = []
150 RepoModel().update(r.repo_name, form_data)
150 RepoModel().update(r.repo_name, form_data)
151 self.assertEqual(r.repo_name, 'g1/john')
151 self.assertEqual(r.repo_name, 'g1/john')
152
152
153
153
154 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
155 self.assertTrue(self.__check_path('g2', 'g1'))
155 self.assertTrue(self.__check_path('g2', 'g1'))
156
156
157 # test repo
157 # test repo
158 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
158 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
159
159
160 class TestUser(unittest.TestCase):
160 class TestUser(unittest.TestCase):
161
161 def __init__(self, methodName='runTest'):
162 Session.remove()
163 super(TestUser, self).__init__(methodName=methodName)
164
162 def test_create_and_remove(self):
165 def test_create_and_remove(self):
163 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
166 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
164 email=u'u232@rhodecode.org',
167 email=u'u232@rhodecode.org',
165 name=u'u1', lastname=u'u1')
168 name=u'u1', lastname=u'u1')
166 Session.commit()
169 Session.commit()
167 self.assertEqual(User.get_by_username(u'test_user'), usr)
170 self.assertEqual(User.get_by_username(u'test_user'), usr)
168
171
169 # make users group
172 # make users group
170 users_group = UsersGroupModel().create('some_example_group')
173 users_group = UsersGroupModel().create('some_example_group')
171 Session.commit()
174 Session.commit()
172
175
173 UsersGroupModel().add_user_to_group(users_group, usr)
176 UsersGroupModel().add_user_to_group(users_group, usr)
174 Session.commit()
177 Session.commit()
175
178
176 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
179 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
177 self.assertEqual(UsersGroupMember.query().count(), 1)
180 self.assertEqual(UsersGroupMember.query().count(), 1)
178 UserModel().delete(usr.user_id)
181 UserModel().delete(usr.user_id)
179 Session.commit()
182 Session.commit()
180
183
181 self.assertEqual(UsersGroupMember.query().all(), [])
184 self.assertEqual(UsersGroupMember.query().all(), [])
182
185
183
186
184 class TestNotifications(unittest.TestCase):
187 class TestNotifications(unittest.TestCase):
185
188
186 def __init__(self, methodName='runTest'):
189 def __init__(self, methodName='runTest'):
190 Session.remove()
187 self.u1 = UserModel().create_or_update(username=u'u1',
191 self.u1 = UserModel().create_or_update(username=u'u1',
188 password=u'qweqwe',
192 password=u'qweqwe',
189 email=u'u1@rhodecode.org',
193 email=u'u1@rhodecode.org',
190 name=u'u1', lastname=u'u1')
194 name=u'u1', lastname=u'u1')
191 Session.commit()
195 Session.commit()
192 self.u1 = self.u1.user_id
196 self.u1 = self.u1.user_id
193
197
194 self.u2 = UserModel().create_or_update(username=u'u2',
198 self.u2 = UserModel().create_or_update(username=u'u2',
195 password=u'qweqwe',
199 password=u'qweqwe',
196 email=u'u2@rhodecode.org',
200 email=u'u2@rhodecode.org',
197 name=u'u2', lastname=u'u3')
201 name=u'u2', lastname=u'u3')
198 Session.commit()
202 Session.commit()
199 self.u2 = self.u2.user_id
203 self.u2 = self.u2.user_id
200
204
201 self.u3 = UserModel().create_or_update(username=u'u3',
205 self.u3 = UserModel().create_or_update(username=u'u3',
202 password=u'qweqwe',
206 password=u'qweqwe',
203 email=u'u3@rhodecode.org',
207 email=u'u3@rhodecode.org',
204 name=u'u3', lastname=u'u3')
208 name=u'u3', lastname=u'u3')
205 Session.commit()
209 Session.commit()
206 self.u3 = self.u3.user_id
210 self.u3 = self.u3.user_id
207
211
208 super(TestNotifications, self).__init__(methodName=methodName)
212 super(TestNotifications, self).__init__(methodName=methodName)
209
213
210 def _clean_notifications(self):
214 def _clean_notifications(self):
211 for n in Notification.query().all():
215 for n in Notification.query().all():
212 Session.delete(n)
216 Session.delete(n)
213
217
214 Session.commit()
218 Session.commit()
215 self.assertEqual(Notification.query().all(), [])
219 self.assertEqual(Notification.query().all(), [])
216
220
221 def tearDown(self):
222 self._clean_notifications()
217
223
218 def test_create_notification(self):
224 def test_create_notification(self):
219 self.assertEqual([], Notification.query().all())
225 self.assertEqual([], Notification.query().all())
220 self.assertEqual([], UserNotification.query().all())
226 self.assertEqual([], UserNotification.query().all())
221
227
222 usrs = [self.u1, self.u2]
228 usrs = [self.u1, self.u2]
223 notification = NotificationModel().create(created_by=self.u1,
229 notification = NotificationModel().create(created_by=self.u1,
224 subject=u'subj', body=u'hi there',
230 subject=u'subj', body=u'hi there',
225 recipients=usrs)
231 recipients=usrs)
226 Session.commit()
232 Session.commit()
227 u1 = User.get(self.u1)
233 u1 = User.get(self.u1)
228 u2 = User.get(self.u2)
234 u2 = User.get(self.u2)
229 u3 = User.get(self.u3)
235 u3 = User.get(self.u3)
230 notifications = Notification.query().all()
236 notifications = Notification.query().all()
231 self.assertEqual(len(notifications), 1)
237 self.assertEqual(len(notifications), 1)
232
238
233 unotification = UserNotification.query()\
239 unotification = UserNotification.query()\
234 .filter(UserNotification.notification == notification).all()
240 .filter(UserNotification.notification == notification).all()
235
241
236 self.assertEqual(notifications[0].recipients, [u1, u2])
242 self.assertEqual(notifications[0].recipients, [u1, u2])
237 self.assertEqual(notification.notification_id,
243 self.assertEqual(notification.notification_id,
238 notifications[0].notification_id)
244 notifications[0].notification_id)
239 self.assertEqual(len(unotification), len(usrs))
245 self.assertEqual(len(unotification), len(usrs))
240 self.assertEqual([x.user.user_id for x in unotification], usrs)
246 self.assertEqual([x.user.user_id for x in unotification], usrs)
241
247
242 self._clean_notifications()
243
248
244 def test_user_notifications(self):
249 def test_user_notifications(self):
245 self.assertEqual([], Notification.query().all())
250 self.assertEqual([], Notification.query().all())
246 self.assertEqual([], UserNotification.query().all())
251 self.assertEqual([], UserNotification.query().all())
247
252
248 notification1 = NotificationModel().create(created_by=self.u1,
253 notification1 = NotificationModel().create(created_by=self.u1,
249 subject=u'subj', body=u'hi there1',
254 subject=u'subj', body=u'hi there1',
250 recipients=[self.u3])
255 recipients=[self.u3])
251 Session.commit()
256 Session.commit()
252 notification2 = NotificationModel().create(created_by=self.u1,
257 notification2 = NotificationModel().create(created_by=self.u1,
253 subject=u'subj', body=u'hi there2',
258 subject=u'subj', body=u'hi there2',
254 recipients=[self.u3])
259 recipients=[self.u3])
255 Session.commit()
260 Session.commit()
256 u3 = Session.query(User).get(self.u3)
261 u3 = Session.query(User).get(self.u3)
257
262
258 self.assertEqual(sorted([x.notification for x in u3.notifications]),
263 self.assertEqual(sorted([x.notification for x in u3.notifications]),
259 sorted([notification2, notification1]))
264 sorted([notification2, notification1]))
260 self._clean_notifications()
261
265
262 def test_delete_notifications(self):
266 def test_delete_notifications(self):
263 self.assertEqual([], Notification.query().all())
267 self.assertEqual([], Notification.query().all())
264 self.assertEqual([], UserNotification.query().all())
268 self.assertEqual([], UserNotification.query().all())
265
269
266 notification = NotificationModel().create(created_by=self.u1,
270 notification = NotificationModel().create(created_by=self.u1,
267 subject=u'title', body=u'hi there3',
271 subject=u'title', body=u'hi there3',
268 recipients=[self.u3, self.u1, self.u2])
272 recipients=[self.u3, self.u1, self.u2])
269 Session.commit()
273 Session.commit()
270 notifications = Notification.query().all()
274 notifications = Notification.query().all()
271 self.assertTrue(notification in notifications)
275 self.assertTrue(notification in notifications)
272
276
273 Notification.delete(notification.notification_id)
277 Notification.delete(notification.notification_id)
274 Session.commit()
278 Session.commit()
275
279
276 notifications = Notification.query().all()
280 notifications = Notification.query().all()
277 self.assertFalse(notification in notifications)
281 self.assertFalse(notification in notifications)
278
282
279 un = UserNotification.query().filter(UserNotification.notification
283 un = UserNotification.query().filter(UserNotification.notification
280 == notification).all()
284 == notification).all()
281 self.assertEqual(un, [])
285 self.assertEqual(un, [])
282
286
283 self._clean_notifications()
284
287
285 def test_delete_association(self):
288 def test_delete_association(self):
286
289
287 self.assertEqual([], Notification.query().all())
290 self.assertEqual([], Notification.query().all())
288 self.assertEqual([], UserNotification.query().all())
291 self.assertEqual([], UserNotification.query().all())
289
292
290 notification = NotificationModel().create(created_by=self.u1,
293 notification = NotificationModel().create(created_by=self.u1,
291 subject=u'title', body=u'hi there3',
294 subject=u'title', body=u'hi there3',
292 recipients=[self.u3, self.u1, self.u2])
295 recipients=[self.u3, self.u1, self.u2])
293 Session.commit()
296 Session.commit()
294
297
295 unotification = UserNotification.query()\
298 unotification = UserNotification.query()\
296 .filter(UserNotification.notification ==
299 .filter(UserNotification.notification ==
297 notification)\
300 notification)\
298 .filter(UserNotification.user_id == self.u3)\
301 .filter(UserNotification.user_id == self.u3)\
299 .scalar()
302 .scalar()
300
303
301 self.assertEqual(unotification.user_id, self.u3)
304 self.assertEqual(unotification.user_id, self.u3)
302
305
303 NotificationModel().delete(self.u3,
306 NotificationModel().delete(self.u3,
304 notification.notification_id)
307 notification.notification_id)
305 Session.commit()
308 Session.commit()
306
309
307 u3notification = UserNotification.query()\
310 u3notification = UserNotification.query()\
308 .filter(UserNotification.notification ==
311 .filter(UserNotification.notification ==
309 notification)\
312 notification)\
310 .filter(UserNotification.user_id == self.u3)\
313 .filter(UserNotification.user_id == self.u3)\
311 .scalar()
314 .scalar()
312
315
313 self.assertEqual(u3notification, None)
316 self.assertEqual(u3notification, None)
314
317
315 # notification object is still there
318 # notification object is still there
316 self.assertEqual(Notification.query().all(), [notification])
319 self.assertEqual(Notification.query().all(), [notification])
317
320
318 #u1 and u2 still have assignments
321 #u1 and u2 still have assignments
319 u1notification = UserNotification.query()\
322 u1notification = UserNotification.query()\
320 .filter(UserNotification.notification ==
323 .filter(UserNotification.notification ==
321 notification)\
324 notification)\
322 .filter(UserNotification.user_id == self.u1)\
325 .filter(UserNotification.user_id == self.u1)\
323 .scalar()
326 .scalar()
324 self.assertNotEqual(u1notification, None)
327 self.assertNotEqual(u1notification, None)
325 u2notification = UserNotification.query()\
328 u2notification = UserNotification.query()\
326 .filter(UserNotification.notification ==
329 .filter(UserNotification.notification ==
327 notification)\
330 notification)\
328 .filter(UserNotification.user_id == self.u2)\
331 .filter(UserNotification.user_id == self.u2)\
329 .scalar()
332 .scalar()
330 self.assertNotEqual(u2notification, None)
333 self.assertNotEqual(u2notification, None)
331
334
332 self._clean_notifications()
333
334 def test_notification_counter(self):
335 def test_notification_counter(self):
335 self._clean_notifications()
336 self._clean_notifications()
336 self.assertEqual([], Notification.query().all())
337 self.assertEqual([], Notification.query().all())
337 self.assertEqual([], UserNotification.query().all())
338 self.assertEqual([], UserNotification.query().all())
338
339
339 NotificationModel().create(created_by=self.u1,
340 NotificationModel().create(created_by=self.u1,
340 subject=u'title', body=u'hi there_delete',
341 subject=u'title', body=u'hi there_delete',
341 recipients=[self.u3, self.u1])
342 recipients=[self.u3, self.u1])
342 Session.commit()
343 Session.commit()
343
344
344 self.assertEqual(NotificationModel()
345 self.assertEqual(NotificationModel()
345 .get_unread_cnt_for_user(self.u1), 1)
346 .get_unread_cnt_for_user(self.u1), 1)
346 self.assertEqual(NotificationModel()
347 self.assertEqual(NotificationModel()
347 .get_unread_cnt_for_user(self.u2), 0)
348 .get_unread_cnt_for_user(self.u2), 0)
348 self.assertEqual(NotificationModel()
349 self.assertEqual(NotificationModel()
349 .get_unread_cnt_for_user(self.u3), 1)
350 .get_unread_cnt_for_user(self.u3), 1)
350
351
351 notification = NotificationModel().create(created_by=self.u1,
352 notification = NotificationModel().create(created_by=self.u1,
352 subject=u'title', body=u'hi there3',
353 subject=u'title', body=u'hi there3',
353 recipients=[self.u3, self.u1, self.u2])
354 recipients=[self.u3, self.u1, self.u2])
354 Session.commit()
355 Session.commit()
355
356
356 self.assertEqual(NotificationModel()
357 self.assertEqual(NotificationModel()
357 .get_unread_cnt_for_user(self.u1), 2)
358 .get_unread_cnt_for_user(self.u1), 2)
358 self.assertEqual(NotificationModel()
359 self.assertEqual(NotificationModel()
359 .get_unread_cnt_for_user(self.u2), 1)
360 .get_unread_cnt_for_user(self.u2), 1)
360 self.assertEqual(NotificationModel()
361 self.assertEqual(NotificationModel()
361 .get_unread_cnt_for_user(self.u3), 2)
362 .get_unread_cnt_for_user(self.u3), 2)
362 self._clean_notifications()
363
364 class TestUsers(unittest.TestCase):
365
366 def __init__(self, methodName='runTest'):
367 super(TestUsers, self).__init__(methodName=methodName)
368
369 def setUp(self):
370 self.u1 = UserModel().create_or_update(username=u'u1',
371 password=u'qweqwe',
372 email=u'u1@rhodecode.org',
373 name=u'u1', lastname=u'u1')
374
375 def tearDown(self):
376 perm = Permission.query().all()
377 for p in perm:
378 UserModel().revoke_perm(self.u1, p)
379
380 UserModel().delete(self.u1)
381 Session.commit()
382
383 def test_add_perm(self):
384 perm = Permission.query().all()[0]
385 UserModel().grant_perm(self.u1, perm)
386 Session.commit()
387 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
388
389 def test_has_perm(self):
390 perm = Permission.query().all()
391 for p in perm:
392 has_p = UserModel().has_perm(self.u1, p)
393 self.assertEqual(False, has_p)
394
395 def test_revoke_perm(self):
396 perm = Permission.query().all()[0]
397 UserModel().grant_perm(self.u1, perm)
398 Session.commit()
399 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
400
401 #revoke
402 UserModel().revoke_perm(self.u1, perm)
403 Session.commit()
404 self.assertEqual(UserModel().has_perm(self.u1, perm),False)
405
406
407
408
409
410
General Comments 0
You need to be logged in to leave comments. Login now