##// END OF EJS Templates
fixed #72 show warning on removal when user still is owner of existing repositories...
marcink -
r713:1bb0fcde beta
parent child Browse files
Show More
@@ -1,166 +1,167 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # users controller for pylons
3 # users controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22 users controller for pylons
22 users controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib.exceptions import *
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.db import User, UserLog
34 from rhodecode.model.db import User
34 from rhodecode.model.forms import UserForm
35 from rhodecode.model.forms import UserForm
35 from rhodecode.model.user import UserModel, DefaultUserException
36 from rhodecode.model.user import UserModel
36 import formencode
37 import formencode
37 import logging
38 import logging
38 import traceback
39 import traceback
39
40
40 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
41
42
42 class UsersController(BaseController):
43 class UsersController(BaseController):
43 """REST Controller styled on the Atom Publishing Protocol"""
44 """REST Controller styled on the Atom Publishing Protocol"""
44 # To properly map this controller, ensure your config/routing.py
45 # To properly map this controller, ensure your config/routing.py
45 # file has a resource setup:
46 # file has a resource setup:
46 # map.resource('user', 'users')
47 # map.resource('user', 'users')
47
48
48 @LoginRequired()
49 @LoginRequired()
49 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
50 def __before__(self):
51 def __before__(self):
51 c.admin_user = session.get('admin_user')
52 c.admin_user = session.get('admin_user')
52 c.admin_username = session.get('admin_username')
53 c.admin_username = session.get('admin_username')
53 super(UsersController, self).__before__()
54 super(UsersController, self).__before__()
54
55
55
56
56 def index(self, format='html'):
57 def index(self, format='html'):
57 """GET /users: All items in the collection"""
58 """GET /users: All items in the collection"""
58 # url('users')
59 # url('users')
59
60
60 c.users_list = self.sa.query(User).all()
61 c.users_list = self.sa.query(User).all()
61 return render('admin/users/users.html')
62 return render('admin/users/users.html')
62
63
63 def create(self):
64 def create(self):
64 """POST /users: Create a new item"""
65 """POST /users: Create a new item"""
65 # url('users')
66 # url('users')
66
67
67 user_model = UserModel()
68 user_model = UserModel()
68 login_form = UserForm()()
69 login_form = UserForm()()
69 try:
70 try:
70 form_result = login_form.to_python(dict(request.POST))
71 form_result = login_form.to_python(dict(request.POST))
71 user_model.create(form_result)
72 user_model.create(form_result)
72 h.flash(_('created user %s') % form_result['username'],
73 h.flash(_('created user %s') % form_result['username'],
73 category='success')
74 category='success')
74 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
75 except formencode.Invalid, errors:
76 except formencode.Invalid, errors:
76 return htmlfill.render(
77 return htmlfill.render(
77 render('admin/users/user_add.html'),
78 render('admin/users/user_add.html'),
78 defaults=errors.value,
79 defaults=errors.value,
79 errors=errors.error_dict or {},
80 errors=errors.error_dict or {},
80 prefix_error=False,
81 prefix_error=False,
81 encoding="UTF-8")
82 encoding="UTF-8")
82 except Exception:
83 except Exception:
83 log.error(traceback.format_exc())
84 log.error(traceback.format_exc())
84 h.flash(_('error occured during creation of user %s') \
85 h.flash(_('error occured during creation of user %s') \
85 % request.POST.get('username'), category='error')
86 % request.POST.get('username'), category='error')
86 return redirect(url('users'))
87 return redirect(url('users'))
87
88
88 def new(self, format='html'):
89 def new(self, format='html'):
89 """GET /users/new: Form to create a new item"""
90 """GET /users/new: Form to create a new item"""
90 # url('new_user')
91 # url('new_user')
91 return render('admin/users/user_add.html')
92 return render('admin/users/user_add.html')
92
93
93 def update(self, id):
94 def update(self, id):
94 """PUT /users/id: Update an existing item"""
95 """PUT /users/id: Update an existing item"""
95 # Forms posted to this method should contain a hidden field:
96 # Forms posted to this method should contain a hidden field:
96 # <input type="hidden" name="_method" value="PUT" />
97 # <input type="hidden" name="_method" value="PUT" />
97 # Or using helpers:
98 # Or using helpers:
98 # h.form(url('user', id=ID),
99 # h.form(url('user', id=ID),
99 # method='put')
100 # method='put')
100 # url('user', id=ID)
101 # url('user', id=ID)
101 user_model = UserModel()
102 user_model = UserModel()
102 c.user = user_model.get(id)
103 c.user = user_model.get(id)
103
104
104 _form = UserForm(edit=True, old_data={'user_id':id,
105 _form = UserForm(edit=True, old_data={'user_id':id,
105 'email':c.user.email})()
106 'email':c.user.email})()
106 form_result = {}
107 form_result = {}
107 try:
108 try:
108 form_result = _form.to_python(dict(request.POST))
109 form_result = _form.to_python(dict(request.POST))
109 user_model.update(id, form_result)
110 user_model.update(id, form_result)
110 h.flash(_('User updated succesfully'), category='success')
111 h.flash(_('User updated succesfully'), category='success')
111
112
112 except formencode.Invalid, errors:
113 except formencode.Invalid, errors:
113 return htmlfill.render(
114 return htmlfill.render(
114 render('admin/users/user_edit.html'),
115 render('admin/users/user_edit.html'),
115 defaults=errors.value,
116 defaults=errors.value,
116 errors=errors.error_dict or {},
117 errors=errors.error_dict or {},
117 prefix_error=False,
118 prefix_error=False,
118 encoding="UTF-8")
119 encoding="UTF-8")
119 except Exception:
120 except Exception:
120 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
121 h.flash(_('error occured during update of user %s') \
122 h.flash(_('error occured during update of user %s') \
122 % form_result.get('username'), category='error')
123 % form_result.get('username'), category='error')
123
124
124 return redirect(url('users'))
125 return redirect(url('users'))
125
126
126 def delete(self, id):
127 def delete(self, id):
127 """DELETE /users/id: Delete an existing item"""
128 """DELETE /users/id: Delete an existing item"""
128 # Forms posted to this method should contain a hidden field:
129 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="DELETE" />
130 # <input type="hidden" name="_method" value="DELETE" />
130 # Or using helpers:
131 # Or using helpers:
131 # h.form(url('user', id=ID),
132 # h.form(url('user', id=ID),
132 # method='delete')
133 # method='delete')
133 # url('user', id=ID)
134 # url('user', id=ID)
134 user_model = UserModel()
135 user_model = UserModel()
135 try:
136 try:
136 user_model.delete(id)
137 user_model.delete(id)
137 h.flash(_('sucessfully deleted user'), category='success')
138 h.flash(_('sucessfully deleted user'), category='success')
138 except DefaultUserException, e:
139 except (UserOwnsReposException, DefaultUserException), e:
139 h.flash(str(e), category='warning')
140 h.flash(str(e), category='warning')
140 except Exception:
141 except Exception:
141 h.flash(_('An error occured during deletion of user'),
142 h.flash(_('An error occured during deletion of user'),
142 category='error')
143 category='error')
143 return redirect(url('users'))
144 return redirect(url('users'))
144
145
145 def show(self, id, format='html'):
146 def show(self, id, format='html'):
146 """GET /users/id: Show a specific item"""
147 """GET /users/id: Show a specific item"""
147 # url('user', id=ID)
148 # url('user', id=ID)
148
149
149
150
150 def edit(self, id, format='html'):
151 def edit(self, id, format='html'):
151 """GET /users/id/edit: Form to edit an existing item"""
152 """GET /users/id/edit: Form to edit an existing item"""
152 # url('edit_user', id=ID)
153 # url('edit_user', id=ID)
153 c.user = self.sa.query(User).get(id)
154 c.user = self.sa.query(User).get(id)
154 if not c.user:
155 if not c.user:
155 return redirect(url('users'))
156 return redirect(url('users'))
156 if c.user.username == 'default':
157 if c.user.username == 'default':
157 h.flash(_("You can't edit this user"), category='warning')
158 h.flash(_("You can't edit this user"), category='warning')
158 return redirect(url('users'))
159 return redirect(url('users'))
159
160
160 defaults = c.user.__dict__
161 defaults = c.user.__dict__
161 return htmlfill.render(
162 return htmlfill.render(
162 render('admin/users/user_edit.html'),
163 render('admin/users/user_edit.html'),
163 defaults=defaults,
164 defaults=defaults,
164 encoding="UTF-8",
165 encoding="UTF-8",
165 force_defaults=False
166 force_defaults=False
166 )
167 )
@@ -1,525 +1,526 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import config, session, url, request
25 from pylons import config, session, url, request
26 from pylons.controllers.util import abort, redirect
26 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.exceptions import *
27 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.auth_ldap import AuthLdap, UsernameError, PasswordError
29 from rhodecode.lib.auth_ldap import AuthLdap
29 from rhodecode.model import meta
30 from rhodecode.model import meta
30 from rhodecode.model.user import UserModel
31 from rhodecode.model.user import UserModel
31 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
33 UserToPerm
34 UserToPerm
34 import bcrypt
35 import bcrypt
35 from decorator import decorator
36 from decorator import decorator
36 import logging
37 import logging
37 import random
38 import random
38 import traceback
39 import traceback
39
40
40 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
41
42
42 class PasswordGenerator(object):
43 class PasswordGenerator(object):
43 """This is a simple class for generating password from
44 """This is a simple class for generating password from
44 different sets of characters
45 different sets of characters
45 usage:
46 usage:
46 passwd_gen = PasswordGenerator()
47 passwd_gen = PasswordGenerator()
47 #print 8-letter password containing only big and small letters of alphabet
48 #print 8-letter password containing only big and small letters of alphabet
48 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
49 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
49 """
50 """
50 ALPHABETS_NUM = r'''1234567890'''#[0]
51 ALPHABETS_NUM = r'''1234567890'''#[0]
51 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
52 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
52 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
53 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
53 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
54 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
54 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
55 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
55 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
56 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
56 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
57 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
57 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
58 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
58 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
59 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
59
60
60 def __init__(self, passwd=''):
61 def __init__(self, passwd=''):
61 self.passwd = passwd
62 self.passwd = passwd
62
63
63 def gen_password(self, len, type):
64 def gen_password(self, len, type):
64 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
65 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
65 return self.passwd
66 return self.passwd
66
67
67
68
68 def get_crypt_password(password):
69 def get_crypt_password(password):
69 """Cryptographic function used for password hashing based on sha1
70 """Cryptographic function used for password hashing based on sha1
70 :param password: password to hash
71 :param password: password to hash
71 """
72 """
72 return bcrypt.hashpw(password, bcrypt.gensalt(10))
73 return bcrypt.hashpw(password, bcrypt.gensalt(10))
73
74
74 def check_password(password, hashed):
75 def check_password(password, hashed):
75 return bcrypt.hashpw(password, hashed) == hashed
76 return bcrypt.hashpw(password, hashed) == hashed
76
77
77 def authfunc(environ, username, password):
78 def authfunc(environ, username, password):
78 """
79 """
79 Authentication function used in Mercurial/Git/ and access control,
80 Authentication function used in Mercurial/Git/ and access control,
80 firstly checks for db authentication then if ldap is enabled for ldap
81 firstly checks for db authentication then if ldap is enabled for ldap
81 authentication, also creates ldap user if not in database
82 authentication, also creates ldap user if not in database
82
83
83 :param environ: needed only for using in Basic auth, can be None
84 :param environ: needed only for using in Basic auth, can be None
84 :param username: username
85 :param username: username
85 :param password: password
86 :param password: password
86 """
87 """
87 user_model = UserModel()
88 user_model = UserModel()
88 user = user_model.get_by_username(username, cache=False)
89 user = user_model.get_by_username(username, cache=False)
89
90
90 if user is not None and user.is_ldap is False:
91 if user is not None and user.is_ldap is False:
91 if user.active:
92 if user.active:
92
93
93 if user.username == 'default' and user.active:
94 if user.username == 'default' and user.active:
94 log.info('user %s authenticated correctly', username)
95 log.info('user %s authenticated correctly', username)
95 return True
96 return True
96
97
97 elif user.username == username and check_password(password, user.password):
98 elif user.username == username and check_password(password, user.password):
98 log.info('user %s authenticated correctly', username)
99 log.info('user %s authenticated correctly', username)
99 return True
100 return True
100 else:
101 else:
101 log.error('user %s is disabled', username)
102 log.error('user %s is disabled', username)
102
103
103
104
104 else:
105 else:
105 from rhodecode.model.settings import SettingsModel
106 from rhodecode.model.settings import SettingsModel
106 ldap_settings = SettingsModel().get_ldap_settings()
107 ldap_settings = SettingsModel().get_ldap_settings()
107
108
108 #======================================================================
109 #======================================================================
109 # FALLBACK TO LDAP AUTH IN ENABLE
110 # FALLBACK TO LDAP AUTH IN ENABLE
110 #======================================================================
111 #======================================================================
111 if ldap_settings.get('ldap_active', False):
112 if ldap_settings.get('ldap_active', False):
112 kwargs = {
113 kwargs = {
113 'server':ldap_settings.get('ldap_host', ''),
114 'server':ldap_settings.get('ldap_host', ''),
114 'base_dn':ldap_settings.get('ldap_base_dn', ''),
115 'base_dn':ldap_settings.get('ldap_base_dn', ''),
115 'port':ldap_settings.get('ldap_port'),
116 'port':ldap_settings.get('ldap_port'),
116 'bind_dn':ldap_settings.get('ldap_dn_user'),
117 'bind_dn':ldap_settings.get('ldap_dn_user'),
117 'bind_pass':ldap_settings.get('ldap_dn_pass'),
118 'bind_pass':ldap_settings.get('ldap_dn_pass'),
118 'use_ldaps':ldap_settings.get('ldap_ldaps'),
119 'use_ldaps':ldap_settings.get('ldap_ldaps'),
119 'ldap_version':3,
120 'ldap_version':3,
120 }
121 }
121 log.debug('Checking for ldap authentication')
122 log.debug('Checking for ldap authentication')
122 try:
123 try:
123 aldap = AuthLdap(**kwargs)
124 aldap = AuthLdap(**kwargs)
124 res = aldap.authenticate_ldap(username, password)
125 res = aldap.authenticate_ldap(username, password)
125
126
126 authenticated = res[1]['uid'][0] == username
127 authenticated = res[1]['uid'][0] == username
127
128
128 if authenticated and user_model.create_ldap(username, password):
129 if authenticated and user_model.create_ldap(username, password):
129 log.info('created new ldap user')
130 log.info('created new ldap user')
130
131
131 return authenticated
132 return authenticated
132 except (UsernameError, PasswordError):
133 except (LdapUsernameError, LdapPasswordError):
133 return False
134 return False
134 except:
135 except:
135 log.error(traceback.format_exc())
136 log.error(traceback.format_exc())
136 return False
137 return False
137 return False
138 return False
138
139
139 class AuthUser(object):
140 class AuthUser(object):
140 """
141 """
141 A simple object that handles a mercurial username for authentication
142 A simple object that handles a mercurial username for authentication
142 """
143 """
143 def __init__(self):
144 def __init__(self):
144 self.username = 'None'
145 self.username = 'None'
145 self.name = ''
146 self.name = ''
146 self.lastname = ''
147 self.lastname = ''
147 self.email = ''
148 self.email = ''
148 self.user_id = None
149 self.user_id = None
149 self.is_authenticated = False
150 self.is_authenticated = False
150 self.is_admin = False
151 self.is_admin = False
151 self.permissions = {}
152 self.permissions = {}
152
153
153 def __repr__(self):
154 def __repr__(self):
154 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
155 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
155
156
156 def set_available_permissions(config):
157 def set_available_permissions(config):
157 """
158 """
158 This function will propagate pylons globals with all available defined
159 This function will propagate pylons globals with all available defined
159 permission given in db. We don't wannt to check each time from db for new
160 permission given in db. We don't wannt to check each time from db for new
160 permissions since adding a new permission also requires application restart
161 permissions since adding a new permission also requires application restart
161 ie. to decorate new views with the newly created permission
162 ie. to decorate new views with the newly created permission
162 :param config:
163 :param config:
163 """
164 """
164 log.info('getting information about all available permissions')
165 log.info('getting information about all available permissions')
165 try:
166 try:
166 sa = meta.Session()
167 sa = meta.Session()
167 all_perms = sa.query(Permission).all()
168 all_perms = sa.query(Permission).all()
168 except:
169 except:
169 pass
170 pass
170 finally:
171 finally:
171 meta.Session.remove()
172 meta.Session.remove()
172
173
173 config['available_permissions'] = [x.permission_name for x in all_perms]
174 config['available_permissions'] = [x.permission_name for x in all_perms]
174
175
175 def set_base_path(config):
176 def set_base_path(config):
176 config['base_path'] = config['pylons.app_globals'].base_path
177 config['base_path'] = config['pylons.app_globals'].base_path
177
178
178
179
179 def fill_perms(user):
180 def fill_perms(user):
180 """
181 """
181 Fills user permission attribute with permissions taken from database
182 Fills user permission attribute with permissions taken from database
182 :param user:
183 :param user:
183 """
184 """
184
185
185 sa = meta.Session()
186 sa = meta.Session()
186 user.permissions['repositories'] = {}
187 user.permissions['repositories'] = {}
187 user.permissions['global'] = set()
188 user.permissions['global'] = set()
188
189
189 #===========================================================================
190 #===========================================================================
190 # fetch default permissions
191 # fetch default permissions
191 #===========================================================================
192 #===========================================================================
192 default_user = UserModel().get_by_username('default', cache=True)
193 default_user = UserModel().get_by_username('default', cache=True)
193
194
194 default_perms = sa.query(RepoToPerm, Repository, Permission)\
195 default_perms = sa.query(RepoToPerm, Repository, Permission)\
195 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
196 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
196 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
197 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
197 .filter(RepoToPerm.user == default_user).all()
198 .filter(RepoToPerm.user == default_user).all()
198
199
199 if user.is_admin:
200 if user.is_admin:
200 #=======================================================================
201 #=======================================================================
201 # #admin have all default rights set to admin
202 # #admin have all default rights set to admin
202 #=======================================================================
203 #=======================================================================
203 user.permissions['global'].add('hg.admin')
204 user.permissions['global'].add('hg.admin')
204
205
205 for perm in default_perms:
206 for perm in default_perms:
206 p = 'repository.admin'
207 p = 'repository.admin'
207 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
208 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
208
209
209 else:
210 else:
210 #=======================================================================
211 #=======================================================================
211 # set default permissions
212 # set default permissions
212 #=======================================================================
213 #=======================================================================
213
214
214 #default global
215 #default global
215 default_global_perms = sa.query(UserToPerm)\
216 default_global_perms = sa.query(UserToPerm)\
216 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
217 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
217 'default').one())
218 'default').one())
218
219
219 for perm in default_global_perms:
220 for perm in default_global_perms:
220 user.permissions['global'].add(perm.permission.permission_name)
221 user.permissions['global'].add(perm.permission.permission_name)
221
222
222 #default repositories
223 #default repositories
223 for perm in default_perms:
224 for perm in default_perms:
224 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
225 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
225 #disable defaults for private repos,
226 #disable defaults for private repos,
226 p = 'repository.none'
227 p = 'repository.none'
227 elif perm.Repository.user_id == user.user_id:
228 elif perm.Repository.user_id == user.user_id:
228 #set admin if owner
229 #set admin if owner
229 p = 'repository.admin'
230 p = 'repository.admin'
230 else:
231 else:
231 p = perm.Permission.permission_name
232 p = perm.Permission.permission_name
232
233
233 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
234 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
234
235
235 #=======================================================================
236 #=======================================================================
236 # #overwrite default with user permissions if any
237 # #overwrite default with user permissions if any
237 #=======================================================================
238 #=======================================================================
238 user_perms = sa.query(RepoToPerm, Permission, Repository)\
239 user_perms = sa.query(RepoToPerm, Permission, Repository)\
239 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
240 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
240 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
241 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
241 .filter(RepoToPerm.user_id == user.user_id).all()
242 .filter(RepoToPerm.user_id == user.user_id).all()
242
243
243 for perm in user_perms:
244 for perm in user_perms:
244 if perm.Repository.user_id == user.user_id:#set admin if owner
245 if perm.Repository.user_id == user.user_id:#set admin if owner
245 p = 'repository.admin'
246 p = 'repository.admin'
246 else:
247 else:
247 p = perm.Permission.permission_name
248 p = perm.Permission.permission_name
248 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
249 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
249 meta.Session.remove()
250 meta.Session.remove()
250 return user
251 return user
251
252
252 def get_user(session):
253 def get_user(session):
253 """
254 """
254 Gets user from session, and wraps permissions into user
255 Gets user from session, and wraps permissions into user
255 :param session:
256 :param session:
256 """
257 """
257 user = session.get('rhodecode_user', AuthUser())
258 user = session.get('rhodecode_user', AuthUser())
258 #if the user is not logged in we check for anonymous access
259 #if the user is not logged in we check for anonymous access
259 #if user is logged and it's a default user check if we still have anonymous
260 #if user is logged and it's a default user check if we still have anonymous
260 #access enabled
261 #access enabled
261 if user.user_id is None or user.username == 'default':
262 if user.user_id is None or user.username == 'default':
262 anonymous_user = UserModel().get_by_username('default', cache=True)
263 anonymous_user = UserModel().get_by_username('default', cache=True)
263 if anonymous_user.active is True:
264 if anonymous_user.active is True:
264 #then we set this user is logged in
265 #then we set this user is logged in
265 user.is_authenticated = True
266 user.is_authenticated = True
266 user.user_id = anonymous_user.user_id
267 user.user_id = anonymous_user.user_id
267 else:
268 else:
268 user.is_authenticated = False
269 user.is_authenticated = False
269
270
270 if user.is_authenticated:
271 if user.is_authenticated:
271 user = UserModel().fill_data(user)
272 user = UserModel().fill_data(user)
272
273
273 user = fill_perms(user)
274 user = fill_perms(user)
274 session['rhodecode_user'] = user
275 session['rhodecode_user'] = user
275 session.save()
276 session.save()
276 return user
277 return user
277
278
278 #===============================================================================
279 #===============================================================================
279 # CHECK DECORATORS
280 # CHECK DECORATORS
280 #===============================================================================
281 #===============================================================================
281 class LoginRequired(object):
282 class LoginRequired(object):
282 """Must be logged in to execute this function else redirect to login page"""
283 """Must be logged in to execute this function else redirect to login page"""
283
284
284 def __call__(self, func):
285 def __call__(self, func):
285 return decorator(self.__wrapper, func)
286 return decorator(self.__wrapper, func)
286
287
287 def __wrapper(self, func, *fargs, **fkwargs):
288 def __wrapper(self, func, *fargs, **fkwargs):
288 user = session.get('rhodecode_user', AuthUser())
289 user = session.get('rhodecode_user', AuthUser())
289 log.debug('Checking login required for user:%s', user.username)
290 log.debug('Checking login required for user:%s', user.username)
290 if user.is_authenticated:
291 if user.is_authenticated:
291 log.debug('user %s is authenticated', user.username)
292 log.debug('user %s is authenticated', user.username)
292 return func(*fargs, **fkwargs)
293 return func(*fargs, **fkwargs)
293 else:
294 else:
294 log.warn('user %s not authenticated', user.username)
295 log.warn('user %s not authenticated', user.username)
295
296
296 p = ''
297 p = ''
297 if request.environ.get('SCRIPT_NAME') != '/':
298 if request.environ.get('SCRIPT_NAME') != '/':
298 p += request.environ.get('SCRIPT_NAME')
299 p += request.environ.get('SCRIPT_NAME')
299
300
300 p += request.environ.get('PATH_INFO')
301 p += request.environ.get('PATH_INFO')
301 if request.environ.get('QUERY_STRING'):
302 if request.environ.get('QUERY_STRING'):
302 p += '?' + request.environ.get('QUERY_STRING')
303 p += '?' + request.environ.get('QUERY_STRING')
303
304
304 log.debug('redirecting to login page with %s', p)
305 log.debug('redirecting to login page with %s', p)
305 return redirect(url('login_home', came_from=p))
306 return redirect(url('login_home', came_from=p))
306
307
307 class PermsDecorator(object):
308 class PermsDecorator(object):
308 """Base class for decorators"""
309 """Base class for decorators"""
309
310
310 def __init__(self, *required_perms):
311 def __init__(self, *required_perms):
311 available_perms = config['available_permissions']
312 available_perms = config['available_permissions']
312 for perm in required_perms:
313 for perm in required_perms:
313 if perm not in available_perms:
314 if perm not in available_perms:
314 raise Exception("'%s' permission is not defined" % perm)
315 raise Exception("'%s' permission is not defined" % perm)
315 self.required_perms = set(required_perms)
316 self.required_perms = set(required_perms)
316 self.user_perms = None
317 self.user_perms = None
317
318
318 def __call__(self, func):
319 def __call__(self, func):
319 return decorator(self.__wrapper, func)
320 return decorator(self.__wrapper, func)
320
321
321
322
322 def __wrapper(self, func, *fargs, **fkwargs):
323 def __wrapper(self, func, *fargs, **fkwargs):
323 # _wrapper.__name__ = func.__name__
324 # _wrapper.__name__ = func.__name__
324 # _wrapper.__dict__.update(func.__dict__)
325 # _wrapper.__dict__.update(func.__dict__)
325 # _wrapper.__doc__ = func.__doc__
326 # _wrapper.__doc__ = func.__doc__
326 self.user = session.get('rhodecode_user', AuthUser())
327 self.user = session.get('rhodecode_user', AuthUser())
327 self.user_perms = self.user.permissions
328 self.user_perms = self.user.permissions
328 log.debug('checking %s permissions %s for %s %s',
329 log.debug('checking %s permissions %s for %s %s',
329 self.__class__.__name__, self.required_perms, func.__name__,
330 self.__class__.__name__, self.required_perms, func.__name__,
330 self.user)
331 self.user)
331
332
332 if self.check_permissions():
333 if self.check_permissions():
333 log.debug('Permission granted for %s %s', func.__name__, self.user)
334 log.debug('Permission granted for %s %s', func.__name__, self.user)
334
335
335 return func(*fargs, **fkwargs)
336 return func(*fargs, **fkwargs)
336
337
337 else:
338 else:
338 log.warning('Permission denied for %s %s', func.__name__, self.user)
339 log.warning('Permission denied for %s %s', func.__name__, self.user)
339 #redirect with forbidden ret code
340 #redirect with forbidden ret code
340 return abort(403)
341 return abort(403)
341
342
342
343
343
344
344 def check_permissions(self):
345 def check_permissions(self):
345 """Dummy function for overriding"""
346 """Dummy function for overriding"""
346 raise Exception('You have to write this function in child class')
347 raise Exception('You have to write this function in child class')
347
348
348 class HasPermissionAllDecorator(PermsDecorator):
349 class HasPermissionAllDecorator(PermsDecorator):
349 """Checks for access permission for all given predicates. All of them
350 """Checks for access permission for all given predicates. All of them
350 have to be meet in order to fulfill the request
351 have to be meet in order to fulfill the request
351 """
352 """
352
353
353 def check_permissions(self):
354 def check_permissions(self):
354 if self.required_perms.issubset(self.user_perms.get('global')):
355 if self.required_perms.issubset(self.user_perms.get('global')):
355 return True
356 return True
356 return False
357 return False
357
358
358
359
359 class HasPermissionAnyDecorator(PermsDecorator):
360 class HasPermissionAnyDecorator(PermsDecorator):
360 """Checks for access permission for any of given predicates. In order to
361 """Checks for access permission for any of given predicates. In order to
361 fulfill the request any of predicates must be meet
362 fulfill the request any of predicates must be meet
362 """
363 """
363
364
364 def check_permissions(self):
365 def check_permissions(self):
365 if self.required_perms.intersection(self.user_perms.get('global')):
366 if self.required_perms.intersection(self.user_perms.get('global')):
366 return True
367 return True
367 return False
368 return False
368
369
369 class HasRepoPermissionAllDecorator(PermsDecorator):
370 class HasRepoPermissionAllDecorator(PermsDecorator):
370 """Checks for access permission for all given predicates for specific
371 """Checks for access permission for all given predicates for specific
371 repository. All of them have to be meet in order to fulfill the request
372 repository. All of them have to be meet in order to fulfill the request
372 """
373 """
373
374
374 def check_permissions(self):
375 def check_permissions(self):
375 repo_name = get_repo_slug(request)
376 repo_name = get_repo_slug(request)
376 try:
377 try:
377 user_perms = set([self.user_perms['repositories'][repo_name]])
378 user_perms = set([self.user_perms['repositories'][repo_name]])
378 except KeyError:
379 except KeyError:
379 return False
380 return False
380 if self.required_perms.issubset(user_perms):
381 if self.required_perms.issubset(user_perms):
381 return True
382 return True
382 return False
383 return False
383
384
384
385
385 class HasRepoPermissionAnyDecorator(PermsDecorator):
386 class HasRepoPermissionAnyDecorator(PermsDecorator):
386 """Checks for access permission for any of given predicates for specific
387 """Checks for access permission for any of given predicates for specific
387 repository. In order to fulfill the request any of predicates must be meet
388 repository. In order to fulfill the request any of predicates must be meet
388 """
389 """
389
390
390 def check_permissions(self):
391 def check_permissions(self):
391 repo_name = get_repo_slug(request)
392 repo_name = get_repo_slug(request)
392
393
393 try:
394 try:
394 user_perms = set([self.user_perms['repositories'][repo_name]])
395 user_perms = set([self.user_perms['repositories'][repo_name]])
395 except KeyError:
396 except KeyError:
396 return False
397 return False
397 if self.required_perms.intersection(user_perms):
398 if self.required_perms.intersection(user_perms):
398 return True
399 return True
399 return False
400 return False
400 #===============================================================================
401 #===============================================================================
401 # CHECK FUNCTIONS
402 # CHECK FUNCTIONS
402 #===============================================================================
403 #===============================================================================
403
404
404 class PermsFunction(object):
405 class PermsFunction(object):
405 """Base function for other check functions"""
406 """Base function for other check functions"""
406
407
407 def __init__(self, *perms):
408 def __init__(self, *perms):
408 available_perms = config['available_permissions']
409 available_perms = config['available_permissions']
409
410
410 for perm in perms:
411 for perm in perms:
411 if perm not in available_perms:
412 if perm not in available_perms:
412 raise Exception("'%s' permission in not defined" % perm)
413 raise Exception("'%s' permission in not defined" % perm)
413 self.required_perms = set(perms)
414 self.required_perms = set(perms)
414 self.user_perms = None
415 self.user_perms = None
415 self.granted_for = ''
416 self.granted_for = ''
416 self.repo_name = None
417 self.repo_name = None
417
418
418 def __call__(self, check_Location=''):
419 def __call__(self, check_Location=''):
419 user = session.get('rhodecode_user', False)
420 user = session.get('rhodecode_user', False)
420 if not user:
421 if not user:
421 return False
422 return False
422 self.user_perms = user.permissions
423 self.user_perms = user.permissions
423 self.granted_for = user.username
424 self.granted_for = user.username
424 log.debug('checking %s %s %s', self.__class__.__name__,
425 log.debug('checking %s %s %s', self.__class__.__name__,
425 self.required_perms, user)
426 self.required_perms, user)
426
427
427 if self.check_permissions():
428 if self.check_permissions():
428 log.debug('Permission granted for %s @ %s %s', self.granted_for,
429 log.debug('Permission granted for %s @ %s %s', self.granted_for,
429 check_Location, user)
430 check_Location, user)
430 return True
431 return True
431
432
432 else:
433 else:
433 log.warning('Permission denied for %s @ %s %s', self.granted_for,
434 log.warning('Permission denied for %s @ %s %s', self.granted_for,
434 check_Location, user)
435 check_Location, user)
435 return False
436 return False
436
437
437 def check_permissions(self):
438 def check_permissions(self):
438 """Dummy function for overriding"""
439 """Dummy function for overriding"""
439 raise Exception('You have to write this function in child class')
440 raise Exception('You have to write this function in child class')
440
441
441 class HasPermissionAll(PermsFunction):
442 class HasPermissionAll(PermsFunction):
442 def check_permissions(self):
443 def check_permissions(self):
443 if self.required_perms.issubset(self.user_perms.get('global')):
444 if self.required_perms.issubset(self.user_perms.get('global')):
444 return True
445 return True
445 return False
446 return False
446
447
447 class HasPermissionAny(PermsFunction):
448 class HasPermissionAny(PermsFunction):
448 def check_permissions(self):
449 def check_permissions(self):
449 if self.required_perms.intersection(self.user_perms.get('global')):
450 if self.required_perms.intersection(self.user_perms.get('global')):
450 return True
451 return True
451 return False
452 return False
452
453
453 class HasRepoPermissionAll(PermsFunction):
454 class HasRepoPermissionAll(PermsFunction):
454
455
455 def __call__(self, repo_name=None, check_Location=''):
456 def __call__(self, repo_name=None, check_Location=''):
456 self.repo_name = repo_name
457 self.repo_name = repo_name
457 return super(HasRepoPermissionAll, self).__call__(check_Location)
458 return super(HasRepoPermissionAll, self).__call__(check_Location)
458
459
459 def check_permissions(self):
460 def check_permissions(self):
460 if not self.repo_name:
461 if not self.repo_name:
461 self.repo_name = get_repo_slug(request)
462 self.repo_name = get_repo_slug(request)
462
463
463 try:
464 try:
464 self.user_perms = set([self.user_perms['repositories']\
465 self.user_perms = set([self.user_perms['repositories']\
465 [self.repo_name]])
466 [self.repo_name]])
466 except KeyError:
467 except KeyError:
467 return False
468 return False
468 self.granted_for = self.repo_name
469 self.granted_for = self.repo_name
469 if self.required_perms.issubset(self.user_perms):
470 if self.required_perms.issubset(self.user_perms):
470 return True
471 return True
471 return False
472 return False
472
473
473 class HasRepoPermissionAny(PermsFunction):
474 class HasRepoPermissionAny(PermsFunction):
474
475
475 def __call__(self, repo_name=None, check_Location=''):
476 def __call__(self, repo_name=None, check_Location=''):
476 self.repo_name = repo_name
477 self.repo_name = repo_name
477 return super(HasRepoPermissionAny, self).__call__(check_Location)
478 return super(HasRepoPermissionAny, self).__call__(check_Location)
478
479
479 def check_permissions(self):
480 def check_permissions(self):
480 if not self.repo_name:
481 if not self.repo_name:
481 self.repo_name = get_repo_slug(request)
482 self.repo_name = get_repo_slug(request)
482
483
483 try:
484 try:
484 self.user_perms = set([self.user_perms['repositories']\
485 self.user_perms = set([self.user_perms['repositories']\
485 [self.repo_name]])
486 [self.repo_name]])
486 except KeyError:
487 except KeyError:
487 return False
488 return False
488 self.granted_for = self.repo_name
489 self.granted_for = self.repo_name
489 if self.required_perms.intersection(self.user_perms):
490 if self.required_perms.intersection(self.user_perms):
490 return True
491 return True
491 return False
492 return False
492
493
493 #===============================================================================
494 #===============================================================================
494 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
495 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
495 #===============================================================================
496 #===============================================================================
496
497
497 class HasPermissionAnyMiddleware(object):
498 class HasPermissionAnyMiddleware(object):
498 def __init__(self, *perms):
499 def __init__(self, *perms):
499 self.required_perms = set(perms)
500 self.required_perms = set(perms)
500
501
501 def __call__(self, user, repo_name):
502 def __call__(self, user, repo_name):
502 usr = AuthUser()
503 usr = AuthUser()
503 usr.user_id = user.user_id
504 usr.user_id = user.user_id
504 usr.username = user.username
505 usr.username = user.username
505 usr.is_admin = user.admin
506 usr.is_admin = user.admin
506
507
507 try:
508 try:
508 self.user_perms = set([fill_perms(usr)\
509 self.user_perms = set([fill_perms(usr)\
509 .permissions['repositories'][repo_name]])
510 .permissions['repositories'][repo_name]])
510 except:
511 except:
511 self.user_perms = set()
512 self.user_perms = set()
512 self.granted_for = ''
513 self.granted_for = ''
513 self.username = user.username
514 self.username = user.username
514 self.repo_name = repo_name
515 self.repo_name = repo_name
515 return self.check_permissions()
516 return self.check_permissions()
516
517
517 def check_permissions(self):
518 def check_permissions(self):
518 log.debug('checking mercurial protocol '
519 log.debug('checking mercurial protocol '
519 'permissions for user:%s repository:%s',
520 'permissions for user:%s repository:%s',
520 self.username, self.repo_name)
521 self.username, self.repo_name)
521 if self.required_perms.intersection(self.user_perms):
522 if self.required_perms.intersection(self.user_perms):
522 log.debug('permission granted')
523 log.debug('permission granted')
523 return True
524 return True
524 log.debug('permission denied')
525 log.debug('permission denied')
525 return False
526 return False
@@ -1,93 +1,105 b''
1 #==============================================================================
1 #!/usr/bin/env python
2 # LDAP
2 # encoding: utf-8
3 #Name = Just a description for the auth modes page
3 # ldap authentication lib
4 #Host = DepartmentName.OrganizationName.local/ IP
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #Port = 389 default for ldap
5 #
6 #LDAPS = no set True if You need to use ldaps
6 # This program is free software; you can redistribute it and/or
7 #Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
7 # modify it under the terms of the GNU General Public License
8 #Password = <password>
8 # as published by the Free Software Foundation; version 2
9 #Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
9 # of the License or (at your opinion) any later version of the license.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
21 Created on Nov 17, 2010
10
22
11 #==============================================================================
23 @author: marcink
24 """
12
25
13 from rhodecode.lib.exceptions import LdapImportError, UsernameError, \
26 from rhodecode.lib.exceptions import *
14 PasswordError, ConnectionError
15 import logging
27 import logging
16
28
17 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
18
30
19 try:
31 try:
20 import ldap
32 import ldap
21 except ImportError:
33 except ImportError:
22 pass
34 pass
23
35
24 class AuthLdap(object):
36 class AuthLdap(object):
25
37
26 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
27 use_ldaps=False, ldap_version=3):
39 use_ldaps=False, ldap_version=3):
28 self.ldap_version = ldap_version
40 self.ldap_version = ldap_version
29 if use_ldaps:
41 if use_ldaps:
30 port = port or 689
42 port = port or 689
31 self.LDAP_USE_LDAPS = use_ldaps
43 self.LDAP_USE_LDAPS = use_ldaps
32 self.LDAP_SERVER_ADDRESS = server
44 self.LDAP_SERVER_ADDRESS = server
33 self.LDAP_SERVER_PORT = port
45 self.LDAP_SERVER_PORT = port
34
46
35 #USE FOR READ ONLY BIND TO LDAP SERVER
47 #USE FOR READ ONLY BIND TO LDAP SERVER
36 self.LDAP_BIND_DN = bind_dn
48 self.LDAP_BIND_DN = bind_dn
37 self.LDAP_BIND_PASS = bind_pass
49 self.LDAP_BIND_PASS = bind_pass
38
50
39 ldap_server_type = 'ldap'
51 ldap_server_type = 'ldap'
40 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
41 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
42 self.LDAP_SERVER_ADDRESS,
54 self.LDAP_SERVER_ADDRESS,
43 self.LDAP_SERVER_PORT)
55 self.LDAP_SERVER_PORT)
44
56
45 self.BASE_DN = base_dn
57 self.BASE_DN = base_dn
46 self.AUTH_DN = "uid=%s,%s"
58 self.AUTH_DN = "uid=%s,%s"
47
59
48 def authenticate_ldap(self, username, password):
60 def authenticate_ldap(self, username, password):
49 """Authenticate a user via LDAP and return his/her LDAP properties.
61 """Authenticate a user via LDAP and return his/her LDAP properties.
50
62
51 Raises AuthenticationError if the credentials are rejected, or
63 Raises AuthenticationError if the credentials are rejected, or
52 EnvironmentError if the LDAP server can't be reached.
64 EnvironmentError if the LDAP server can't be reached.
53
65
54 :param username: username
66 :param username: username
55 :param password: password
67 :param password: password
56 """
68 """
57
69
58 from rhodecode.lib.helpers import chop_at
70 from rhodecode.lib.helpers import chop_at
59
71
60 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
72 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
61 dn = self.AUTH_DN % (uid, self.BASE_DN)
73 dn = self.AUTH_DN % (uid, self.BASE_DN)
62 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
74 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
63 if "," in username:
75 if "," in username:
64 raise UsernameError("invalid character in username: ,")
76 raise LdapUsernameError("invalid character in username: ,")
65 try:
77 try:
66 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
78 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
67 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
79 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
68 server = ldap.initialize(self.LDAP_SERVER)
80 server = ldap.initialize(self.LDAP_SERVER)
69 if self.ldap_version == 2:
81 if self.ldap_version == 2:
70 server.protocol = ldap.VERSION2
82 server.protocol = ldap.VERSION2
71 else:
83 else:
72 server.protocol = ldap.VERSION3
84 server.protocol = ldap.VERSION3
73
85
74 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
86 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
75 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
87 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
76 self.BASE_DN),
88 self.BASE_DN),
77 self.LDAP_BIND_PASS)
89 self.LDAP_BIND_PASS)
78
90
79 server.simple_bind_s(dn, password)
91 server.simple_bind_s(dn, password)
80 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
81 if not properties:
93 if not properties:
82 raise ldap.NO_SUCH_OBJECT()
94 raise ldap.NO_SUCH_OBJECT()
83 except ldap.NO_SUCH_OBJECT, e:
95 except ldap.NO_SUCH_OBJECT, e:
84 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 log.debug("LDAP says no such user '%s' (%s)", uid, username)
85 raise UsernameError()
97 raise LdapUsernameError()
86 except ldap.INVALID_CREDENTIALS, e:
98 except ldap.INVALID_CREDENTIALS, e:
87 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
88 raise PasswordError()
100 raise LdapPasswordError()
89 except ldap.SERVER_DOWN, e:
101 except ldap.SERVER_DOWN, e:
90 raise ConnectionError("LDAP can't access authentication server")
102 raise LdapConnectionError("LDAP can't access authentication server")
91
103
92 return properties[0]
104 return properties[0]
93
105
@@ -1,29 +1,32 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Custom Exceptions modules
3 # Custom Exceptions modules
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Nov 17, 2010
21 Created on Nov 17, 2010
22 Custom Exceptions modules
22 Custom Exceptions modules
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 class UsernameError(Exception):pass
26 class LdapUsernameError(Exception):pass
27 class PasswordError(Exception):pass
27 class LdapPasswordError(Exception):pass
28 class ConnectionError(Exception):pass
28 class LdapConnectionError(Exception):pass
29 class LdapImportError(Exception):pass
29 class LdapImportError(Exception):pass
30
31 class DefaultUserException(Exception):pass
32 class UserOwnsReposException(Exception):pass
@@ -1,164 +1,166 b''
1 from rhodecode.model.meta import Base
1 from rhodecode.model.meta import Base
2 from sqlalchemy import *
2 from sqlalchemy import *
3 from sqlalchemy.orm import relation, backref
3 from sqlalchemy.orm import relation, backref
4 from sqlalchemy.orm.session import Session
4 from sqlalchemy.orm.session import Session
5 from vcs.utils.lazy import LazyProperty
5 from vcs.utils.lazy import LazyProperty
6 import logging
6 import logging
7 log = logging.getLogger(__name__)
7 log = logging.getLogger(__name__)
8
8
9 class RhodeCodeSettings(Base):
9 class RhodeCodeSettings(Base):
10 __tablename__ = 'rhodecode_settings'
10 __tablename__ = 'rhodecode_settings'
11 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
11 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
12 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
12 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
13 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
13 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
14 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
14 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15
15
16 def __init__(self, k, v):
16 def __init__(self, k, v):
17 self.app_settings_name = k
17 self.app_settings_name = k
18 self.app_settings_value = v
18 self.app_settings_value = v
19
19
20 def __repr__(self):
20 def __repr__(self):
21 return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
21 return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
22 self.app_settings_value)
22 self.app_settings_value)
23
23
24 class RhodeCodeUi(Base):
24 class RhodeCodeUi(Base):
25 __tablename__ = 'rhodecode_ui'
25 __tablename__ = 'rhodecode_ui'
26 __table_args__ = {'useexisting':True}
26 __table_args__ = {'useexisting':True}
27 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
27 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
28 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
28 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
29 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
29 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
30 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
30 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
31 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
32
32
33
33
34 class User(Base):
34 class User(Base):
35 __tablename__ = 'users'
35 __tablename__ = 'users'
36 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
36 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
37 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
37 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
38 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
38 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
39 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
39 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
40 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
40 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
41 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
41 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
42 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
42 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
43 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
43 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
44 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
44 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
45 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
45 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
46 is_ldap = Column("is_ldap", BOOLEAN(), nullable=False, unique=None, default=False)
46 is_ldap = Column("is_ldap", BOOLEAN(), nullable=False, unique=None, default=False)
47
47
48 user_log = relation('UserLog', cascade='all')
48 user_log = relation('UserLog', cascade='all')
49 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
49 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
50
50
51 repositories = relation('Repository')
52
51 @LazyProperty
53 @LazyProperty
52 def full_contact(self):
54 def full_contact(self):
53 return '%s %s <%s>' % (self.name, self.lastname, self.email)
55 return '%s %s <%s>' % (self.name, self.lastname, self.email)
54
56
55 def __repr__(self):
57 def __repr__(self):
56 return "<User('id:%s:%s')>" % (self.user_id, self.username)
58 return "<User('id:%s:%s')>" % (self.user_id, self.username)
57
59
58 def update_lastlogin(self):
60 def update_lastlogin(self):
59 """Update user lastlogin"""
61 """Update user lastlogin"""
60 import datetime
62 import datetime
61
63
62 try:
64 try:
63 session = Session.object_session(self)
65 session = Session.object_session(self)
64 self.last_login = datetime.datetime.now()
66 self.last_login = datetime.datetime.now()
65 session.add(self)
67 session.add(self)
66 session.commit()
68 session.commit()
67 log.debug('updated user %s lastlogin', self.username)
69 log.debug('updated user %s lastlogin', self.username)
68 except Exception:
70 except Exception:
69 session.rollback()
71 session.rollback()
70
72
71
73
72 class UserLog(Base):
74 class UserLog(Base):
73 __tablename__ = 'user_logs'
75 __tablename__ = 'user_logs'
74 __table_args__ = {'useexisting':True}
76 __table_args__ = {'useexisting':True}
75 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
77 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
76 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
78 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
77 repository_id = Column("repository_id", INTEGER(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
79 repository_id = Column("repository_id", INTEGER(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
78 repository_name = Column("repository_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
80 repository_name = Column("repository_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
79 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
81 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
80 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
82 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
81 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
83 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
82
84
83 user = relation('User')
85 user = relation('User')
84 repository = relation('Repository')
86 repository = relation('Repository')
85
87
86 class Repository(Base):
88 class Repository(Base):
87 __tablename__ = 'repositories'
89 __tablename__ = 'repositories'
88 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
90 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
89 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
91 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
90 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
92 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
91 repo_type = Column("repo_type", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
93 repo_type = Column("repo_type", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
92 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
94 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
93 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
95 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
94 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
96 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
95 fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
97 fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
96
98
97 user = relation('User')
99 user = relation('User')
98 fork = relation('Repository', remote_side=repo_id)
100 fork = relation('Repository', remote_side=repo_id)
99 repo_to_perm = relation('RepoToPerm', cascade='all')
101 repo_to_perm = relation('RepoToPerm', cascade='all')
100 stats = relation('Statistics', cascade='all', uselist=False)
102 stats = relation('Statistics', cascade='all', uselist=False)
101
103
102 def __repr__(self):
104 def __repr__(self):
103 return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
105 return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
104
106
105 class Permission(Base):
107 class Permission(Base):
106 __tablename__ = 'permissions'
108 __tablename__ = 'permissions'
107 __table_args__ = {'useexisting':True}
109 __table_args__ = {'useexisting':True}
108 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
110 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
109 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
111 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
112 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
111
113
112 def __repr__(self):
114 def __repr__(self):
113 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
115 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
114
116
115 class RepoToPerm(Base):
117 class RepoToPerm(Base):
116 __tablename__ = 'repo_to_perm'
118 __tablename__ = 'repo_to_perm'
117 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
119 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
118 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
120 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
119 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
121 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
120 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
122 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
121 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
123 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
122
124
123 user = relation('User')
125 user = relation('User')
124 permission = relation('Permission')
126 permission = relation('Permission')
125 repository = relation('Repository')
127 repository = relation('Repository')
126
128
127 class UserToPerm(Base):
129 class UserToPerm(Base):
128 __tablename__ = 'user_to_perm'
130 __tablename__ = 'user_to_perm'
129 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
131 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
130 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
132 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
131 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
133 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
132 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
134 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
133
135
134 user = relation('User')
136 user = relation('User')
135 permission = relation('Permission')
137 permission = relation('Permission')
136
138
137 class Statistics(Base):
139 class Statistics(Base):
138 __tablename__ = 'statistics'
140 __tablename__ = 'statistics'
139 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
141 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
140 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
142 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
141 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
143 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
142 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
144 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
143 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
145 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
144 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
146 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
145 languages = Column("languages", BLOB(), nullable=False)#JSON data
147 languages = Column("languages", BLOB(), nullable=False)#JSON data
146
148
147 repository = relation('Repository', single_parent=True)
149 repository = relation('Repository', single_parent=True)
148
150
149 class CacheInvalidation(Base):
151 class CacheInvalidation(Base):
150 __tablename__ = 'cache_invalidation'
152 __tablename__ = 'cache_invalidation'
151 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
153 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
152 cache_id = Column("cache_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
154 cache_id = Column("cache_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
153 cache_key = Column("cache_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 cache_key = Column("cache_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 cache_args = Column("cache_args", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 cache_args = Column("cache_args", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 cache_active = Column("cache_active", BOOLEAN(), nullable=True, unique=None, default=False)
157 cache_active = Column("cache_active", BOOLEAN(), nullable=True, unique=None, default=False)
156
158
157
159
158 def __init__(self, cache_key, cache_args=''):
160 def __init__(self, cache_key, cache_args=''):
159 self.cache_key = cache_key
161 self.cache_key = cache_key
160 self.cache_args = cache_args
162 self.cache_args = cache_args
161 self.cache_active = False
163 self.cache_active = False
162
164
163 def __repr__(self):
165 def __repr__(self):
164 return "<CacheInvaidation('%s:%s')>" % (self.cache_id, self.cache_key)
166 return "<CacheInvaidation('%s:%s')>" % (self.cache_id, self.cache_key)
@@ -1,207 +1,215 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for users
3 # Model for users
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for users
22 Model for users
23 :author: marcink
23 :author: marcink
24 """
24 """
25
25
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.model.caching_query import FromCache
27 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.db import User
28 from rhodecode.model.db import User
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 from rhodecode.lib.exceptions import *
30 import logging
31 import logging
31 import traceback
32 import traceback
32
33
33 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
34
35
35 class DefaultUserException(Exception):pass
36
36
37
37 class UserModel(object):
38 class UserModel(object):
38
39
39 def __init__(self):
40 def __init__(self):
40 self.sa = Session()
41 self.sa = Session()
41
42
42 def get(self, user_id, cache=False):
43 def get(self, user_id, cache=False):
43 user = self.sa.query(User)
44 user = self.sa.query(User)
44 if cache:
45 if cache:
45 user = user.options(FromCache("sql_cache_short",
46 user = user.options(FromCache("sql_cache_short",
46 "get_user_%s" % user_id))
47 "get_user_%s" % user_id))
47 return user.get(user_id)
48 return user.get(user_id)
48
49
49
50
50 def get_by_username(self, username, cache=False):
51 def get_by_username(self, username, cache=False):
51 user = self.sa.query(User)\
52 user = self.sa.query(User)\
52 .filter(User.username == username)
53 .filter(User.username == username)
53 if cache:
54 if cache:
54 user = user.options(FromCache("sql_cache_short",
55 user = user.options(FromCache("sql_cache_short",
55 "get_user_%s" % username))
56 "get_user_%s" % username))
56 return user.scalar()
57 return user.scalar()
57
58
58 def create(self, form_data):
59 def create(self, form_data):
59 try:
60 try:
60 new_user = User()
61 new_user = User()
61 for k, v in form_data.items():
62 for k, v in form_data.items():
62 setattr(new_user, k, v)
63 setattr(new_user, k, v)
63
64
64 self.sa.add(new_user)
65 self.sa.add(new_user)
65 self.sa.commit()
66 self.sa.commit()
66 except:
67 except:
67 log.error(traceback.format_exc())
68 log.error(traceback.format_exc())
68 self.sa.rollback()
69 self.sa.rollback()
69 raise
70 raise
70
71
71 def create_ldap(self, username, password):
72 def create_ldap(self, username, password):
72 """
73 """
73 Checks if user is in database, if not creates this user marked
74 Checks if user is in database, if not creates this user marked
74 as ldap user
75 as ldap user
75 :param username:
76 :param username:
76 :param password:
77 :param password:
77 """
78 """
78
79
79 if self.get_by_username(username) is None:
80 if self.get_by_username(username) is None:
80 try:
81 try:
81 new_user = User()
82 new_user = User()
82 new_user.username = username
83 new_user.username = username
83 new_user.password = password
84 new_user.password = password
84 new_user.email = '%s@ldap.server' % username
85 new_user.email = '%s@ldap.server' % username
85 new_user.active = True
86 new_user.active = True
86 new_user.is_ldap = True
87 new_user.is_ldap = True
87 new_user.name = '%s@ldap' % username
88 new_user.name = '%s@ldap' % username
88 new_user.lastname = ''
89 new_user.lastname = ''
89
90
90
91
91 self.sa.add(new_user)
92 self.sa.add(new_user)
92 self.sa.commit()
93 self.sa.commit()
93 return True
94 return True
94 except:
95 except:
95 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
96 self.sa.rollback()
97 self.sa.rollback()
97 raise
98 raise
98
99
99 return False
100 return False
100
101
101 def create_registration(self, form_data):
102 def create_registration(self, form_data):
102 from rhodecode.lib.celerylib import tasks, run_task
103 from rhodecode.lib.celerylib import tasks, run_task
103 try:
104 try:
104 new_user = User()
105 new_user = User()
105 for k, v in form_data.items():
106 for k, v in form_data.items():
106 if k != 'admin':
107 if k != 'admin':
107 setattr(new_user, k, v)
108 setattr(new_user, k, v)
108
109
109 self.sa.add(new_user)
110 self.sa.add(new_user)
110 self.sa.commit()
111 self.sa.commit()
111 body = ('New user registration\n'
112 body = ('New user registration\n'
112 'username: %s\n'
113 'username: %s\n'
113 'email: %s\n')
114 'email: %s\n')
114 body = body % (form_data['username'], form_data['email'])
115 body = body % (form_data['username'], form_data['email'])
115
116
116 run_task(tasks.send_email, None,
117 run_task(tasks.send_email, None,
117 _('[RhodeCode] New User registration'),
118 _('[RhodeCode] New User registration'),
118 body)
119 body)
119 except:
120 except:
120 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
121 self.sa.rollback()
122 self.sa.rollback()
122 raise
123 raise
123
124
124 def update(self, user_id, form_data):
125 def update(self, user_id, form_data):
125 try:
126 try:
126 new_user = self.get(user_id, cache=False)
127 new_user = self.get(user_id, cache=False)
127 if new_user.username == 'default':
128 if new_user.username == 'default':
128 raise DefaultUserException(
129 raise DefaultUserException(
129 _("You can't Edit this user since it's"
130 _("You can't Edit this user since it's"
130 " crucial for entire application"))
131 " crucial for entire application"))
132
131 for k, v in form_data.items():
133 for k, v in form_data.items():
132 if k == 'new_password' and v != '':
134 if k == 'new_password' and v != '':
133 new_user.password = v
135 new_user.password = v
134 else:
136 else:
135 setattr(new_user, k, v)
137 setattr(new_user, k, v)
136
138
137 self.sa.add(new_user)
139 self.sa.add(new_user)
138 self.sa.commit()
140 self.sa.commit()
139 except:
141 except:
140 log.error(traceback.format_exc())
142 log.error(traceback.format_exc())
141 self.sa.rollback()
143 self.sa.rollback()
142 raise
144 raise
143
145
144 def update_my_account(self, user_id, form_data):
146 def update_my_account(self, user_id, form_data):
145 try:
147 try:
146 new_user = self.get(user_id, cache=False)
148 new_user = self.get(user_id, cache=False)
147 if new_user.username == 'default':
149 if new_user.username == 'default':
148 raise DefaultUserException(
150 raise DefaultUserException(
149 _("You can't Edit this user since it's"
151 _("You can't Edit this user since it's"
150 " crucial for entire application"))
152 " crucial for entire application"))
151 for k, v in form_data.items():
153 for k, v in form_data.items():
152 if k == 'new_password' and v != '':
154 if k == 'new_password' and v != '':
153 new_user.password = v
155 new_user.password = v
154 else:
156 else:
155 if k not in ['admin', 'active']:
157 if k not in ['admin', 'active']:
156 setattr(new_user, k, v)
158 setattr(new_user, k, v)
157
159
158 self.sa.add(new_user)
160 self.sa.add(new_user)
159 self.sa.commit()
161 self.sa.commit()
160 except:
162 except:
161 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
162 self.sa.rollback()
164 self.sa.rollback()
163 raise
165 raise
164
166
165 def delete(self, user_id):
167 def delete(self, user_id):
166 try:
168 try:
167 user = self.get(user_id, cache=False)
169 user = self.get(user_id, cache=False)
168 if user.username == 'default':
170 if user.username == 'default':
169 raise DefaultUserException(
171 raise DefaultUserException(
170 _("You can't remove this user since it's"
172 _("You can't remove this user since it's"
171 " crucial for entire application"))
173 " crucial for entire application"))
174 if user.repositories:
175 raise UserOwnsReposException(_('This user still owns %s '
176 'repositories and cannot be '
177 'removed. Switch owners or '
178 'remove those repositories') \
179 % user.repositories)
172 self.sa.delete(user)
180 self.sa.delete(user)
173 self.sa.commit()
181 self.sa.commit()
174 except:
182 except:
175 log.error(traceback.format_exc())
183 log.error(traceback.format_exc())
176 self.sa.rollback()
184 self.sa.rollback()
177 raise
185 raise
178
186
179 def reset_password(self, data):
187 def reset_password(self, data):
180 from rhodecode.lib.celerylib import tasks, run_task
188 from rhodecode.lib.celerylib import tasks, run_task
181 run_task(tasks.reset_user_password, data['email'])
189 run_task(tasks.reset_user_password, data['email'])
182
190
183
191
184 def fill_data(self, user):
192 def fill_data(self, user):
185 """
193 """
186 Fills user data with those from database and log out user if not
194 Fills user data with those from database and log out user if not
187 present in database
195 present in database
188 :param user:
196 :param user:
189 """
197 """
190
198
191 if not hasattr(user, 'user_id') or user.user_id is None:
199 if not hasattr(user, 'user_id') or user.user_id is None:
192 raise Exception('passed in user has to have the user_id attribute')
200 raise Exception('passed in user has to have the user_id attribute')
193
201
194
202
195 log.debug('filling auth user data')
203 log.debug('filling auth user data')
196 try:
204 try:
197 dbuser = self.get(user.user_id)
205 dbuser = self.get(user.user_id)
198 user.username = dbuser.username
206 user.username = dbuser.username
199 user.is_admin = dbuser.admin
207 user.is_admin = dbuser.admin
200 user.name = dbuser.name
208 user.name = dbuser.name
201 user.lastname = dbuser.lastname
209 user.lastname = dbuser.lastname
202 user.email = dbuser.email
210 user.email = dbuser.email
203 except:
211 except:
204 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
205 user.is_authenticated = False
213 user.is_authenticated = False
206
214
207 return user
215 return user
General Comments 0
You need to be logged in to leave comments. Login now