##// END OF EJS Templates
fixed issues with not unique emails when using ldap or container auth.
marcink -
r1690:6944b124 beta
parent child Browse files
Show More
@@ -1,154 +1,153 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changelog
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode authentication library for LDAP
6 RhodeCode authentication library for LDAP
7
7
8 :created_on: Created on Nov 17, 2010
8 :created_on: Created on Nov 17, 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
27
28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
28 from rhodecode.lib.exceptions import LdapConnectionError, LdapUsernameError, \
29 LdapPasswordError
29 LdapPasswordError
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 try:
34 try:
35 import ldap
35 import ldap
36 except ImportError:
36 except ImportError:
37 # means that python-ldap is not installed
37 # means that python-ldap is not installed
38 pass
38 pass
39
39
40
40
41 class AuthLdap(object):
41 class AuthLdap(object):
42
42
43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
43 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
44 tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3,
45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
45 ldap_filter='(&(objectClass=user)(!(objectClass=computer)))',
46 search_scope='SUBTREE',
46 search_scope = 'SUBTREE', attr_login = 'uid'):
47 attr_login='uid'):
48 self.ldap_version = ldap_version
47 self.ldap_version = ldap_version
49 ldap_server_type = 'ldap'
48 ldap_server_type = 'ldap'
50
49
51 self.TLS_KIND = tls_kind
50 self.TLS_KIND = tls_kind
52
51
53 if self.TLS_KIND == 'LDAPS':
52 if self.TLS_KIND == 'LDAPS':
54 port = port or 689
53 port = port or 689
55 ldap_server_type = ldap_server_type + 's'
54 ldap_server_type = ldap_server_type + 's'
56
55
57 OPT_X_TLS_DEMAND = 2
56 OPT_X_TLS_DEMAND = 2
58 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
57 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert,
59 OPT_X_TLS_DEMAND)
58 OPT_X_TLS_DEMAND)
60 self.LDAP_SERVER_ADDRESS = server
59 self.LDAP_SERVER_ADDRESS = server
61 self.LDAP_SERVER_PORT = port
60 self.LDAP_SERVER_PORT = port
62
61
63 #USE FOR READ ONLY BIND TO LDAP SERVER
62 # USE FOR READ ONLY BIND TO LDAP SERVER
64 self.LDAP_BIND_DN = bind_dn
63 self.LDAP_BIND_DN = bind_dn
65 self.LDAP_BIND_PASS = bind_pass
64 self.LDAP_BIND_PASS = bind_pass
66
65
67 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
66 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
68 self.LDAP_SERVER_ADDRESS,
67 self.LDAP_SERVER_ADDRESS,
69 self.LDAP_SERVER_PORT)
68 self.LDAP_SERVER_PORT)
70
69
71 self.BASE_DN = base_dn
70 self.BASE_DN = base_dn
72 self.LDAP_FILTER = ldap_filter
71 self.LDAP_FILTER = ldap_filter
73 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
72 self.SEARCH_SCOPE = getattr(ldap, 'SCOPE_%s' % search_scope)
74 self.attr_login = attr_login
73 self.attr_login = attr_login
75
74
76 def authenticate_ldap(self, username, password):
75 def authenticate_ldap(self, username, password):
77 """Authenticate a user via LDAP and return his/her LDAP properties.
76 """Authenticate a user via LDAP and return his/her LDAP properties.
78
77
79 Raises AuthenticationError if the credentials are rejected, or
78 Raises AuthenticationError if the credentials are rejected, or
80 EnvironmentError if the LDAP server can't be reached.
79 EnvironmentError if the LDAP server can't be reached.
81
80
82 :param username: username
81 :param username: username
83 :param password: password
82 :param password: password
84 """
83 """
85
84
86 from rhodecode.lib.helpers import chop_at
85 from rhodecode.lib.helpers import chop_at
87
86
88 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
87 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
89
88
90 if not password:
89 if not password:
91 log.debug("Attempt to authenticate LDAP user with blank password rejected.")
90 log.debug("Attempt to authenticate LDAP user with blank password rejected.")
92 raise LdapPasswordError()
91 raise LdapPasswordError()
93 if "," in username:
92 if "," in username:
94 raise LdapUsernameError("invalid character in username: ,")
93 raise LdapUsernameError("invalid character in username: ,")
95 try:
94 try:
96 if hasattr(ldap,'OPT_X_TLS_CACERTDIR'):
95 if hasattr(ldap,'OPT_X_TLS_CACERTDIR'):
97 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
96 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR,
98 '/etc/openldap/cacerts')
97 '/etc/openldap/cacerts')
99 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
98 ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF)
100 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
99 ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON)
101 ldap.set_option(ldap.OPT_TIMEOUT, 20)
100 ldap.set_option(ldap.OPT_TIMEOUT, 20)
102 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
101 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
103 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
102 ldap.set_option(ldap.OPT_TIMELIMIT, 15)
104 if self.TLS_KIND != 'PLAIN':
103 if self.TLS_KIND != 'PLAIN':
105 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
104 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, self.TLS_REQCERT)
106 server = ldap.initialize(self.LDAP_SERVER)
105 server = ldap.initialize(self.LDAP_SERVER)
107 if self.ldap_version == 2:
106 if self.ldap_version == 2:
108 server.protocol = ldap.VERSION2
107 server.protocol = ldap.VERSION2
109 else:
108 else:
110 server.protocol = ldap.VERSION3
109 server.protocol = ldap.VERSION3
111
110
112 if self.TLS_KIND == 'START_TLS':
111 if self.TLS_KIND == 'START_TLS':
113 server.start_tls_s()
112 server.start_tls_s()
114
113
115 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
114 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
116 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
115 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
117
116
118 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
117 filt = '(&%s(%s=%s))' % (self.LDAP_FILTER, self.attr_login,
119 username)
118 username)
120 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
119 log.debug("Authenticating %r filt %s at %s", self.BASE_DN,
121 filt, self.LDAP_SERVER)
120 filt, self.LDAP_SERVER)
122 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
121 lobjects = server.search_ext_s(self.BASE_DN, self.SEARCH_SCOPE,
123 filt)
122 filt)
124
123
125 if not lobjects:
124 if not lobjects:
126 raise ldap.NO_SUCH_OBJECT()
125 raise ldap.NO_SUCH_OBJECT()
127
126
128 for (dn, _attrs) in lobjects:
127 for (dn, _attrs) in lobjects:
129 if dn is None:
128 if dn is None:
130 continue
129 continue
131
130
132 try:
131 try:
133 server.simple_bind_s(dn, password)
132 server.simple_bind_s(dn, password)
134 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
133 attrs = server.search_ext_s(dn, ldap.SCOPE_BASE,
135 '(objectClass=*)')[0][1]
134 '(objectClass=*)')[0][1]
136 break
135 break
137
136
138 except ldap.INVALID_CREDENTIALS, e:
137 except ldap.INVALID_CREDENTIALS, e:
139 log.debug("LDAP rejected password for user '%s' (%s): %s",
138 log.debug("LDAP rejected password for user '%s' (%s): %s",
140 uid, username, dn)
139 uid, username, dn)
141
140
142 else:
141 else:
143 log.debug("No matching LDAP objects for authentication "
142 log.debug("No matching LDAP objects for authentication "
144 "of '%s' (%s)", uid, username)
143 "of '%s' (%s)", uid, username)
145 raise LdapPasswordError()
144 raise LdapPasswordError()
146
145
147 except ldap.NO_SUCH_OBJECT, e:
146 except ldap.NO_SUCH_OBJECT, e:
148 log.debug("LDAP says no such user '%s' (%s)", uid, username)
147 log.debug("LDAP says no such user '%s' (%s)", uid, username)
149 raise LdapUsernameError()
148 raise LdapUsernameError()
150 except ldap.SERVER_DOWN, e:
149 except ldap.SERVER_DOWN, e:
151 raise LdapConnectionError("LDAP can't access "
150 raise LdapConnectionError("LDAP can't access "
152 "authentication server")
151 "authentication server")
153
152
154 return (dn, attrs)
153 return (dn, attrs)
@@ -1,476 +1,480 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.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from rhodecode.lib import safe_unicode
31 from rhodecode.lib import safe_unicode
32 from rhodecode.lib.caching_query import FromCache
32 from rhodecode.lib.caching_query import FromCache
33
33
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
35 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
36 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
36 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
37 from rhodecode.lib.exceptions import DefaultUserException, \
37 from rhodecode.lib.exceptions import DefaultUserException, \
38 UserOwnsReposException
38 UserOwnsReposException
39
39
40 from sqlalchemy.exc import DatabaseError
40 from sqlalchemy.exc import DatabaseError
41 from rhodecode.lib import generate_api_key
41 from rhodecode.lib import generate_api_key
42 from sqlalchemy.orm import joinedload
42 from sqlalchemy.orm import joinedload
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 PERM_WEIGHTS = {'repository.none': 0,
46 PERM_WEIGHTS = {'repository.none': 0,
47 'repository.read': 1,
47 'repository.read': 1,
48 'repository.write': 3,
48 'repository.write': 3,
49 'repository.admin': 3}
49 'repository.admin': 3}
50
50
51
51
52 class UserModel(BaseModel):
52 class UserModel(BaseModel):
53 def get(self, user_id, cache=False):
53 def get(self, user_id, cache=False):
54 user = self.sa.query(User)
54 user = self.sa.query(User)
55 if cache:
55 if cache:
56 user = user.options(FromCache("sql_cache_short",
56 user = user.options(FromCache("sql_cache_short",
57 "get_user_%s" % user_id))
57 "get_user_%s" % user_id))
58 return user.get(user_id)
58 return user.get(user_id)
59
59
60 def get_by_username(self, username, cache=False, case_insensitive=False):
60 def get_by_username(self, username, cache=False, case_insensitive=False):
61
61
62 if case_insensitive:
62 if case_insensitive:
63 user = self.sa.query(User).filter(User.username.ilike(username))
63 user = self.sa.query(User).filter(User.username.ilike(username))
64 else:
64 else:
65 user = self.sa.query(User)\
65 user = self.sa.query(User)\
66 .filter(User.username == username)
66 .filter(User.username == username)
67 if cache:
67 if cache:
68 user = user.options(FromCache("sql_cache_short",
68 user = user.options(FromCache("sql_cache_short",
69 "get_user_%s" % username))
69 "get_user_%s" % username))
70 return user.scalar()
70 return user.scalar()
71
71
72 def get_by_api_key(self, api_key, cache=False):
72 def get_by_api_key(self, api_key, cache=False):
73
73
74 user = self.sa.query(User)\
74 user = self.sa.query(User)\
75 .filter(User.api_key == api_key)
75 .filter(User.api_key == api_key)
76 if cache:
76 if cache:
77 user = user.options(FromCache("sql_cache_short",
77 user = user.options(FromCache("sql_cache_short",
78 "get_user_%s" % api_key))
78 "get_user_%s" % api_key))
79 return user.scalar()
79 return user.scalar()
80
80
81 def create(self, form_data):
81 def create(self, form_data):
82 try:
82 try:
83 new_user = User()
83 new_user = User()
84 for k, v in form_data.items():
84 for k, v in form_data.items():
85 setattr(new_user, k, v)
85 setattr(new_user, k, v)
86
86
87 new_user.api_key = generate_api_key(form_data['username'])
87 new_user.api_key = generate_api_key(form_data['username'])
88 self.sa.add(new_user)
88 self.sa.add(new_user)
89 self.sa.commit()
89 self.sa.commit()
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 self.sa.rollback()
93 self.sa.rollback()
94 raise
94 raise
95
95
96
96
97 def create_or_update(self, username, password, email, name, lastname,
97 def create_or_update(self, username, password, email, name, lastname,
98 active=True, admin=False, ldap_dn=None):
98 active=True, admin=False, ldap_dn=None):
99 """
99 """
100 Creates a new instance if not found, or updates current one
100 Creates a new instance if not found, or updates current one
101
101
102 :param username:
102 :param username:
103 :param password:
103 :param password:
104 :param email:
104 :param email:
105 :param active:
105 :param active:
106 :param name:
106 :param name:
107 :param lastname:
107 :param lastname:
108 :param active:
108 :param active:
109 :param admin:
109 :param admin:
110 :param ldap_dn:
110 :param ldap_dn:
111 """
111 """
112
112
113 from rhodecode.lib.auth import get_crypt_password
113 from rhodecode.lib.auth import get_crypt_password
114
114
115 log.debug('Checking for %s account in RhodeCode database', username)
115 log.debug('Checking for %s account in RhodeCode database', username)
116 user = User.get_by_username(username, case_insensitive=True)
116 user = User.get_by_username(username, case_insensitive=True)
117 if user is None:
117 if user is None:
118 log.debug('creating new user %s', username)
118 log.debug('creating new user %s', username)
119 new_user = User()
119 new_user = User()
120 else:
120 else:
121 log.debug('updating user %s', username)
121 log.debug('updating user %s', username)
122 new_user = user
122 new_user = user
123
123
124 try:
124 try:
125 new_user.username = username
125 new_user.username = username
126 new_user.admin = admin
126 new_user.admin = admin
127 new_user.password = get_crypt_password(password)
127 new_user.password = get_crypt_password(password)
128 new_user.api_key = generate_api_key(username)
128 new_user.api_key = generate_api_key(username)
129 new_user.email = email
129 new_user.email = email
130 new_user.active = active
130 new_user.active = active
131 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
131 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
132 new_user.name = name
132 new_user.name = name
133 new_user.lastname = lastname
133 new_user.lastname = lastname
134
134
135 self.sa.add(new_user)
135 self.sa.add(new_user)
136 self.sa.commit()
136 self.sa.commit()
137 return new_user
137 return new_user
138 except (DatabaseError,):
138 except (DatabaseError,):
139 log.error(traceback.format_exc())
139 log.error(traceback.format_exc())
140 self.sa.rollback()
140 self.sa.rollback()
141 raise
141 raise
142
142
143
143
144 def create_for_container_auth(self, username, attrs):
144 def create_for_container_auth(self, username, attrs):
145 """
145 """
146 Creates the given user if it's not already in the database
146 Creates the given user if it's not already in the database
147
147
148 :param username:
148 :param username:
149 :param attrs:
149 :param attrs:
150 """
150 """
151 if self.get_by_username(username, case_insensitive=True) is None:
151 if self.get_by_username(username, case_insensitive=True) is None:
152
153 # autogenerate email for container account without one
154 generate_email = lambda usr: '%s@container_auth.account' % usr
155
152 try:
156 try:
153 new_user = User()
157 new_user = User()
154 new_user.username = username
158 new_user.username = username
155 new_user.password = None
159 new_user.password = None
156 new_user.api_key = generate_api_key(username)
160 new_user.api_key = generate_api_key(username)
157 new_user.email = attrs['email']
161 new_user.email = attrs['email']
158 new_user.active = attrs.get('active', True)
162 new_user.active = attrs.get('active', True)
159 new_user.name = attrs['name']
163 new_user.name = attrs['name'] or generate_email(username)
160 new_user.lastname = attrs['lastname']
164 new_user.lastname = attrs['lastname']
161
165
162 self.sa.add(new_user)
166 self.sa.add(new_user)
163 self.sa.commit()
167 self.sa.commit()
164 return new_user
168 return new_user
165 except (DatabaseError,):
169 except (DatabaseError,):
166 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
167 self.sa.rollback()
171 self.sa.rollback()
168 raise
172 raise
169 log.debug('User %s already exists. Skipping creation of account'
173 log.debug('User %s already exists. Skipping creation of account'
170 ' for container auth.', username)
174 ' for container auth.', username)
171 return None
175 return None
172
176
173 def create_ldap(self, username, password, user_dn, attrs):
177 def create_ldap(self, username, password, user_dn, attrs):
174 """
178 """
175 Checks if user is in database, if not creates this user marked
179 Checks if user is in database, if not creates this user marked
176 as ldap user
180 as ldap user
177
181
178 :param username:
182 :param username:
179 :param password:
183 :param password:
180 :param user_dn:
184 :param user_dn:
181 :param attrs:
185 :param attrs:
182 """
186 """
183 from rhodecode.lib.auth import get_crypt_password
187 from rhodecode.lib.auth import get_crypt_password
184 log.debug('Checking for such ldap account in RhodeCode database')
188 log.debug('Checking for such ldap account in RhodeCode database')
185 if self.get_by_username(username, case_insensitive=True) is None:
189 if self.get_by_username(username, case_insensitive=True) is None:
186
190
187 # autogenerate email for ldap account without one
191 # autogenerate email for ldap account without one
188 generate_email = lambda usr: '%s@ldap.account' % usr
192 generate_email = lambda usr: '%s@ldap.account' % usr
189
193
190 try:
194 try:
191 new_user = User()
195 new_user = User()
192 username = username.lower()
196 username = username.lower()
193 # add ldap account always lowercase
197 # add ldap account always lowercase
194 new_user.username = username
198 new_user.username = username
195 new_user.password = get_crypt_password(password)
199 new_user.password = get_crypt_password(password)
196 new_user.api_key = generate_api_key(username)
200 new_user.api_key = generate_api_key(username)
197 new_user.email = attrs['email'] or generate_email(username)
201 new_user.email = attrs['email'] or generate_email(username)
198 new_user.active = attrs.get('active', True)
202 new_user.active = attrs.get('active', True)
199 new_user.ldap_dn = safe_unicode(user_dn)
203 new_user.ldap_dn = safe_unicode(user_dn)
200 new_user.name = attrs['name']
204 new_user.name = attrs['name']
201 new_user.lastname = attrs['lastname']
205 new_user.lastname = attrs['lastname']
202
206
203 self.sa.add(new_user)
207 self.sa.add(new_user)
204 self.sa.commit()
208 self.sa.commit()
205 return new_user
209 return new_user
206 except (DatabaseError,):
210 except (DatabaseError,):
207 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
208 self.sa.rollback()
212 self.sa.rollback()
209 raise
213 raise
210 log.debug('this %s user exists skipping creation of ldap account',
214 log.debug('this %s user exists skipping creation of ldap account',
211 username)
215 username)
212 return None
216 return None
213
217
214 def create_registration(self, form_data):
218 def create_registration(self, form_data):
215 from rhodecode.lib.celerylib import tasks, run_task
219 from rhodecode.lib.celerylib import tasks, run_task
216 try:
220 try:
217 new_user = User()
221 new_user = User()
218 for k, v in form_data.items():
222 for k, v in form_data.items():
219 if k != 'admin':
223 if k != 'admin':
220 setattr(new_user, k, v)
224 setattr(new_user, k, v)
221
225
222 self.sa.add(new_user)
226 self.sa.add(new_user)
223 self.sa.commit()
227 self.sa.commit()
224 body = ('New user registration\n'
228 body = ('New user registration\n'
225 'username: %s\n'
229 'username: %s\n'
226 'email: %s\n')
230 'email: %s\n')
227 body = body % (form_data['username'], form_data['email'])
231 body = body % (form_data['username'], form_data['email'])
228
232
229 run_task(tasks.send_email, None,
233 run_task(tasks.send_email, None,
230 _('[RhodeCode] New User registration'),
234 _('[RhodeCode] New User registration'),
231 body)
235 body)
232 except:
236 except:
233 log.error(traceback.format_exc())
237 log.error(traceback.format_exc())
234 self.sa.rollback()
238 self.sa.rollback()
235 raise
239 raise
236
240
237 def update(self, user_id, form_data):
241 def update(self, user_id, form_data):
238 try:
242 try:
239 user = self.get(user_id, cache=False)
243 user = self.get(user_id, cache=False)
240 if user.username == 'default':
244 if user.username == 'default':
241 raise DefaultUserException(
245 raise DefaultUserException(
242 _("You can't Edit this user since it's"
246 _("You can't Edit this user since it's"
243 " crucial for entire application"))
247 " crucial for entire application"))
244
248
245 for k, v in form_data.items():
249 for k, v in form_data.items():
246 if k == 'new_password' and v != '':
250 if k == 'new_password' and v != '':
247 user.password = v
251 user.password = v
248 user.api_key = generate_api_key(user.username)
252 user.api_key = generate_api_key(user.username)
249 else:
253 else:
250 setattr(user, k, v)
254 setattr(user, k, v)
251
255
252 self.sa.add(user)
256 self.sa.add(user)
253 self.sa.commit()
257 self.sa.commit()
254 except:
258 except:
255 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
256 self.sa.rollback()
260 self.sa.rollback()
257 raise
261 raise
258
262
259 def update_my_account(self, user_id, form_data):
263 def update_my_account(self, user_id, form_data):
260 try:
264 try:
261 user = self.get(user_id, cache=False)
265 user = self.get(user_id, cache=False)
262 if user.username == 'default':
266 if user.username == 'default':
263 raise DefaultUserException(
267 raise DefaultUserException(
264 _("You can't Edit this user since it's"
268 _("You can't Edit this user since it's"
265 " crucial for entire application"))
269 " crucial for entire application"))
266 for k, v in form_data.items():
270 for k, v in form_data.items():
267 if k == 'new_password' and v != '':
271 if k == 'new_password' and v != '':
268 user.password = v
272 user.password = v
269 user.api_key = generate_api_key(user.username)
273 user.api_key = generate_api_key(user.username)
270 else:
274 else:
271 if k not in ['admin', 'active']:
275 if k not in ['admin', 'active']:
272 setattr(user, k, v)
276 setattr(user, k, v)
273
277
274 self.sa.add(user)
278 self.sa.add(user)
275 self.sa.commit()
279 self.sa.commit()
276 except:
280 except:
277 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
278 self.sa.rollback()
282 self.sa.rollback()
279 raise
283 raise
280
284
281 def delete(self, user_id):
285 def delete(self, user_id):
282 try:
286 try:
283 user = self.get(user_id, cache=False)
287 user = self.get(user_id, cache=False)
284 if user.username == 'default':
288 if user.username == 'default':
285 raise DefaultUserException(
289 raise DefaultUserException(
286 _("You can't remove this user since it's"
290 _("You can't remove this user since it's"
287 " crucial for entire application"))
291 " crucial for entire application"))
288 if user.repositories:
292 if user.repositories:
289 raise UserOwnsReposException(_('This user still owns %s '
293 raise UserOwnsReposException(_('This user still owns %s '
290 'repositories and cannot be '
294 'repositories and cannot be '
291 'removed. Switch owners or '
295 'removed. Switch owners or '
292 'remove those repositories') \
296 'remove those repositories') \
293 % user.repositories)
297 % user.repositories)
294 self.sa.delete(user)
298 self.sa.delete(user)
295 self.sa.commit()
299 self.sa.commit()
296 except:
300 except:
297 log.error(traceback.format_exc())
301 log.error(traceback.format_exc())
298 self.sa.rollback()
302 self.sa.rollback()
299 raise
303 raise
300
304
301 def reset_password_link(self, data):
305 def reset_password_link(self, data):
302 from rhodecode.lib.celerylib import tasks, run_task
306 from rhodecode.lib.celerylib import tasks, run_task
303 run_task(tasks.send_password_link, data['email'])
307 run_task(tasks.send_password_link, data['email'])
304
308
305 def reset_password(self, data):
309 def reset_password(self, data):
306 from rhodecode.lib.celerylib import tasks, run_task
310 from rhodecode.lib.celerylib import tasks, run_task
307 run_task(tasks.reset_user_password, data['email'])
311 run_task(tasks.reset_user_password, data['email'])
308
312
309 def fill_data(self, auth_user, user_id=None, api_key=None):
313 def fill_data(self, auth_user, user_id=None, api_key=None):
310 """
314 """
311 Fetches auth_user by user_id,or api_key if present.
315 Fetches auth_user by user_id,or api_key if present.
312 Fills auth_user attributes with those taken from database.
316 Fills auth_user attributes with those taken from database.
313 Additionally set's is_authenitated if lookup fails
317 Additionally set's is_authenitated if lookup fails
314 present in database
318 present in database
315
319
316 :param auth_user: instance of user to set attributes
320 :param auth_user: instance of user to set attributes
317 :param user_id: user id to fetch by
321 :param user_id: user id to fetch by
318 :param api_key: api key to fetch by
322 :param api_key: api key to fetch by
319 """
323 """
320 if user_id is None and api_key is None:
324 if user_id is None and api_key is None:
321 raise Exception('You need to pass user_id or api_key')
325 raise Exception('You need to pass user_id or api_key')
322
326
323 try:
327 try:
324 if api_key:
328 if api_key:
325 dbuser = self.get_by_api_key(api_key)
329 dbuser = self.get_by_api_key(api_key)
326 else:
330 else:
327 dbuser = self.get(user_id)
331 dbuser = self.get(user_id)
328
332
329 if dbuser is not None and dbuser.active:
333 if dbuser is not None and dbuser.active:
330 log.debug('filling %s data', dbuser)
334 log.debug('filling %s data', dbuser)
331 for k, v in dbuser.get_dict().items():
335 for k, v in dbuser.get_dict().items():
332 setattr(auth_user, k, v)
336 setattr(auth_user, k, v)
333 else:
337 else:
334 return False
338 return False
335
339
336 except:
340 except:
337 log.error(traceback.format_exc())
341 log.error(traceback.format_exc())
338 auth_user.is_authenticated = False
342 auth_user.is_authenticated = False
339 return False
343 return False
340
344
341 return True
345 return True
342
346
343 def fill_perms(self, user):
347 def fill_perms(self, user):
344 """
348 """
345 Fills user permission attribute with permissions taken from database
349 Fills user permission attribute with permissions taken from database
346 works for permissions given for repositories, and for permissions that
350 works for permissions given for repositories, and for permissions that
347 are granted to groups
351 are granted to groups
348
352
349 :param user: user instance to fill his perms
353 :param user: user instance to fill his perms
350 """
354 """
351
355
352 user.permissions['repositories'] = {}
356 user.permissions['repositories'] = {}
353 user.permissions['global'] = set()
357 user.permissions['global'] = set()
354
358
355 #======================================================================
359 #======================================================================
356 # fetch default permissions
360 # fetch default permissions
357 #======================================================================
361 #======================================================================
358 default_user = self.get_by_username('default', cache=True)
362 default_user = self.get_by_username('default', cache=True)
359
363
360 default_perms = self.sa.query(UserRepoToPerm, Repository, Permission)\
364 default_perms = self.sa.query(UserRepoToPerm, Repository, Permission)\
361 .join((Repository, UserRepoToPerm.repository_id ==
365 .join((Repository, UserRepoToPerm.repository_id ==
362 Repository.repo_id))\
366 Repository.repo_id))\
363 .join((Permission, UserRepoToPerm.permission_id ==
367 .join((Permission, UserRepoToPerm.permission_id ==
364 Permission.permission_id))\
368 Permission.permission_id))\
365 .filter(UserRepoToPerm.user == default_user).all()
369 .filter(UserRepoToPerm.user == default_user).all()
366
370
367 if user.is_admin:
371 if user.is_admin:
368 #==================================================================
372 #==================================================================
369 # #admin have all default rights set to admin
373 # #admin have all default rights set to admin
370 #==================================================================
374 #==================================================================
371 user.permissions['global'].add('hg.admin')
375 user.permissions['global'].add('hg.admin')
372
376
373 for perm in default_perms:
377 for perm in default_perms:
374 p = 'repository.admin'
378 p = 'repository.admin'
375 user.permissions['repositories'][perm.UserRepoToPerm.
379 user.permissions['repositories'][perm.UserRepoToPerm.
376 repository.repo_name] = p
380 repository.repo_name] = p
377
381
378 else:
382 else:
379 #==================================================================
383 #==================================================================
380 # set default permissions
384 # set default permissions
381 #==================================================================
385 #==================================================================
382 uid = user.user_id
386 uid = user.user_id
383
387
384 #default global
388 #default global
385 default_global_perms = self.sa.query(UserToPerm)\
389 default_global_perms = self.sa.query(UserToPerm)\
386 .filter(UserToPerm.user == default_user)
390 .filter(UserToPerm.user == default_user)
387
391
388 for perm in default_global_perms:
392 for perm in default_global_perms:
389 user.permissions['global'].add(perm.permission.permission_name)
393 user.permissions['global'].add(perm.permission.permission_name)
390
394
391 #default for repositories
395 #default for repositories
392 for perm in default_perms:
396 for perm in default_perms:
393 if perm.Repository.private and not (perm.Repository.user_id ==
397 if perm.Repository.private and not (perm.Repository.user_id ==
394 uid):
398 uid):
395 #diself.sable defaults for private repos,
399 #diself.sable defaults for private repos,
396 p = 'repository.none'
400 p = 'repository.none'
397 elif perm.Repository.user_id == uid:
401 elif perm.Repository.user_id == uid:
398 #set admin if owner
402 #set admin if owner
399 p = 'repository.admin'
403 p = 'repository.admin'
400 else:
404 else:
401 p = perm.Permission.permission_name
405 p = perm.Permission.permission_name
402
406
403 user.permissions['repositories'][perm.UserRepoToPerm.
407 user.permissions['repositories'][perm.UserRepoToPerm.
404 repository.repo_name] = p
408 repository.repo_name] = p
405
409
406 #==================================================================
410 #==================================================================
407 # overwrite default with user permissions if any
411 # overwrite default with user permissions if any
408 #==================================================================
412 #==================================================================
409
413
410 #user global
414 #user global
411 user_perms = self.sa.query(UserToPerm)\
415 user_perms = self.sa.query(UserToPerm)\
412 .options(joinedload(UserToPerm.permission))\
416 .options(joinedload(UserToPerm.permission))\
413 .filter(UserToPerm.user_id == uid).all()
417 .filter(UserToPerm.user_id == uid).all()
414
418
415 for perm in user_perms:
419 for perm in user_perms:
416 user.permissions['global'].add(perm.permission.
420 user.permissions['global'].add(perm.permission.
417 permission_name)
421 permission_name)
418
422
419 #user repositories
423 #user repositories
420 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
424 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
421 Repository)\
425 Repository)\
422 .join((Repository, UserRepoToPerm.repository_id ==
426 .join((Repository, UserRepoToPerm.repository_id ==
423 Repository.repo_id))\
427 Repository.repo_id))\
424 .join((Permission, UserRepoToPerm.permission_id ==
428 .join((Permission, UserRepoToPerm.permission_id ==
425 Permission.permission_id))\
429 Permission.permission_id))\
426 .filter(UserRepoToPerm.user_id == uid).all()
430 .filter(UserRepoToPerm.user_id == uid).all()
427
431
428 for perm in user_repo_perms:
432 for perm in user_repo_perms:
429 # set admin if owner
433 # set admin if owner
430 if perm.Repository.user_id == uid:
434 if perm.Repository.user_id == uid:
431 p = 'repository.admin'
435 p = 'repository.admin'
432 else:
436 else:
433 p = perm.Permission.permission_name
437 p = perm.Permission.permission_name
434 user.permissions['repositories'][perm.UserRepoToPerm.
438 user.permissions['repositories'][perm.UserRepoToPerm.
435 repository.repo_name] = p
439 repository.repo_name] = p
436
440
437 #==================================================================
441 #==================================================================
438 # check if user is part of groups for this repository and fill in
442 # check if user is part of groups for this repository and fill in
439 # (or replace with higher) permissions
443 # (or replace with higher) permissions
440 #==================================================================
444 #==================================================================
441
445
442 #users group global
446 #users group global
443 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
447 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
444 .options(joinedload(UsersGroupToPerm.permission))\
448 .options(joinedload(UsersGroupToPerm.permission))\
445 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
449 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
446 UsersGroupMember.users_group_id))\
450 UsersGroupMember.users_group_id))\
447 .filter(UsersGroupMember.user_id == uid).all()
451 .filter(UsersGroupMember.user_id == uid).all()
448
452
449 for perm in user_perms_from_users_groups:
453 for perm in user_perms_from_users_groups:
450 user.permissions['global'].add(perm.permission.permission_name)
454 user.permissions['global'].add(perm.permission.permission_name)
451
455
452 #users group repositories
456 #users group repositories
453 user_repo_perms_from_users_groups = self.sa.query(
457 user_repo_perms_from_users_groups = self.sa.query(
454 UsersGroupRepoToPerm,
458 UsersGroupRepoToPerm,
455 Permission, Repository,)\
459 Permission, Repository,)\
456 .join((Repository, UsersGroupRepoToPerm.repository_id ==
460 .join((Repository, UsersGroupRepoToPerm.repository_id ==
457 Repository.repo_id))\
461 Repository.repo_id))\
458 .join((Permission, UsersGroupRepoToPerm.permission_id ==
462 .join((Permission, UsersGroupRepoToPerm.permission_id ==
459 Permission.permission_id))\
463 Permission.permission_id))\
460 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
464 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
461 UsersGroupMember.users_group_id))\
465 UsersGroupMember.users_group_id))\
462 .filter(UsersGroupMember.user_id == uid).all()
466 .filter(UsersGroupMember.user_id == uid).all()
463
467
464 for perm in user_repo_perms_from_users_groups:
468 for perm in user_repo_perms_from_users_groups:
465 p = perm.Permission.permission_name
469 p = perm.Permission.permission_name
466 cur_perm = user.permissions['repositories'][perm.
470 cur_perm = user.permissions['repositories'][perm.
467 UsersGroupRepoToPerm.
471 UsersGroupRepoToPerm.
468 repository.repo_name]
472 repository.repo_name]
469 #overwrite permission only if it's greater than permission
473 #overwrite permission only if it's greater than permission
470 # given from other sources
474 # given from other sources
471 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
475 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
472 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
476 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
473 repository.repo_name] = p
477 repository.repo_name] = p
474
478
475 return user
479 return user
476
480
General Comments 0
You need to be logged in to leave comments. Login now