##// END OF EJS Templates
fix for api key lookup, reuse same function in user model
marcink -
r1693:60249224 beta
parent child Browse files
Show More
@@ -1,248 +1,250 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 JSON RPC controller
6 JSON RPC controller
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import inspect
28 import inspect
29 import logging
29 import logging
30 import types
30 import types
31 import urllib
31 import urllib
32 import traceback
32 import traceback
33
33
34 from rhodecode.lib.compat import izip_longest, json
34 from rhodecode.lib.compat import izip_longest, json
35
35
36 from paste.response import replace_header
36 from paste.response import replace_header
37
37
38 from pylons.controllers import WSGIController
38 from pylons.controllers import WSGIController
39
39
40
40
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
42 HTTPBadRequest, HTTPError
42 HTTPBadRequest, HTTPError
43
43
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
45 from rhodecode.lib.auth import AuthUser
45 from rhodecode.lib.auth import AuthUser
46
46
47 log = logging.getLogger('JSONRPC')
47 log = logging.getLogger('JSONRPC')
48
48
49 class JSONRPCError(BaseException):
49 class JSONRPCError(BaseException):
50
50
51 def __init__(self, message):
51 def __init__(self, message):
52 self.message = message
52 self.message = message
53
53
54 def __str__(self):
54 def __str__(self):
55 return str(self.message)
55 return str(self.message)
56
56
57
57
58 def jsonrpc_error(message, code=None):
58 def jsonrpc_error(message, code=None):
59 """
59 """
60 Generate a Response object with a JSON-RPC error body
60 Generate a Response object with a JSON-RPC error body
61 """
61 """
62 from pylons.controllers.util import Response
62 from pylons.controllers.util import Response
63 resp = Response(body=json.dumps(dict(result=None, error=message)),
63 resp = Response(body=json.dumps(dict(result=None, error=message)),
64 status=code,
64 status=code,
65 content_type='application/json')
65 content_type='application/json')
66 return resp
66 return resp
67
67
68
68
69
69
70 class JSONRPCController(WSGIController):
70 class JSONRPCController(WSGIController):
71 """
71 """
72 A WSGI-speaking JSON-RPC controller class
72 A WSGI-speaking JSON-RPC controller class
73
73
74 See the specification:
74 See the specification:
75 <http://json-rpc.org/wiki/specification>`.
75 <http://json-rpc.org/wiki/specification>`.
76
76
77 Valid controller return values should be json-serializable objects.
77 Valid controller return values should be json-serializable objects.
78
78
79 Sub-classes should catch their exceptions and raise JSONRPCError
79 Sub-classes should catch their exceptions and raise JSONRPCError
80 if they want to pass meaningful errors to the client.
80 if they want to pass meaningful errors to the client.
81
81
82 """
82 """
83
83
84 def _get_method_args(self):
84 def _get_method_args(self):
85 """
85 """
86 Return `self._rpc_args` to dispatched controller method
86 Return `self._rpc_args` to dispatched controller method
87 chosen by __call__
87 chosen by __call__
88 """
88 """
89 return self._rpc_args
89 return self._rpc_args
90
90
91 def __call__(self, environ, start_response):
91 def __call__(self, environ, start_response):
92 """
92 """
93 Parse the request body as JSON, look up the method on the
93 Parse the request body as JSON, look up the method on the
94 controller and if it exists, dispatch to it.
94 controller and if it exists, dispatch to it.
95 """
95 """
96 if 'CONTENT_LENGTH' not in environ:
96 if 'CONTENT_LENGTH' not in environ:
97 log.debug("No Content-Length")
97 log.debug("No Content-Length")
98 return jsonrpc_error(message="No Content-Length in request")
98 return jsonrpc_error(message="No Content-Length in request")
99 else:
99 else:
100 length = environ['CONTENT_LENGTH'] or 0
100 length = environ['CONTENT_LENGTH'] or 0
101 length = int(environ['CONTENT_LENGTH'])
101 length = int(environ['CONTENT_LENGTH'])
102 log.debug('Content-Length: %s', length)
102 log.debug('Content-Length: %s', length)
103
103
104 if length == 0:
104 if length == 0:
105 log.debug("Content-Length is 0")
105 log.debug("Content-Length is 0")
106 return jsonrpc_error(message="Content-Length is 0")
106 return jsonrpc_error(message="Content-Length is 0")
107
107
108 raw_body = environ['wsgi.input'].read(length)
108 raw_body = environ['wsgi.input'].read(length)
109
109
110 try:
110 try:
111 json_body = json.loads(urllib.unquote_plus(raw_body))
111 json_body = json.loads(urllib.unquote_plus(raw_body))
112 except ValueError, e:
112 except ValueError, e:
113 #catch JSON errors Here
113 #catch JSON errors Here
114 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
114 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
115 % (e, urllib.unquote_plus(raw_body)))
115 % (e, urllib.unquote_plus(raw_body)))
116
116
117 #check AUTH based on API KEY
117 # check AUTH based on API KEY
118 try:
118 try:
119 self._req_api_key = json_body['api_key']
119 self._req_api_key = json_body['api_key']
120 self._req_method = json_body['method']
120 self._req_method = json_body['method']
121 self._req_params = json_body['args']
121 self._req_params = json_body['args']
122 log.debug('method: %s, params: %s',
122 log.debug('method: %s, params: %s',
123 self._req_method,
123 self._req_method,
124 self._req_params)
124 self._req_params)
125 except KeyError, e:
125 except KeyError, e:
126 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
126 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
127
127
128 #check if we can find this session using api_key
128 # check if we can find this session using api_key
129 try:
129 try:
130 u = User.get_by_api_key(self._req_api_key)
130 u = User.get_by_api_key(self._req_api_key)
131 if u is None:
132 return jsonrpc_error(message='Invalid API KEY')
131 auth_u = AuthUser(u.user_id, self._req_api_key)
133 auth_u = AuthUser(u.user_id, self._req_api_key)
132 except Exception, e:
134 except Exception, e:
133 return jsonrpc_error(message='Invalid API KEY')
135 return jsonrpc_error(message='Invalid API KEY')
134
136
135 self._error = None
137 self._error = None
136 try:
138 try:
137 self._func = self._find_method()
139 self._func = self._find_method()
138 except AttributeError, e:
140 except AttributeError, e:
139 return jsonrpc_error(message=str(e))
141 return jsonrpc_error(message=str(e))
140
142
141 # now that we have a method, add self._req_params to
143 # now that we have a method, add self._req_params to
142 # self.kargs and dispatch control to WGIController
144 # self.kargs and dispatch control to WGIController
143 argspec = inspect.getargspec(self._func)
145 argspec = inspect.getargspec(self._func)
144 arglist = argspec[0][1:]
146 arglist = argspec[0][1:]
145 defaults = argspec[3] or []
147 defaults = argspec[3] or []
146 default_empty = types.NotImplementedType
148 default_empty = types.NotImplementedType
147
149
148 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
150 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
149 fillvalue=default_empty))
151 fillvalue=default_empty))
150
152
151 # this is little trick to inject logged in user for
153 # this is little trick to inject logged in user for
152 # perms decorators to work they expect the controller class to have
154 # perms decorators to work they expect the controller class to have
153 # rhodecode_user attribute set
155 # rhodecode_user attribute set
154 self.rhodecode_user = auth_u
156 self.rhodecode_user = auth_u
155
157
156 # This attribute will need to be first param of a method that uses
158 # This attribute will need to be first param of a method that uses
157 # api_key, which is translated to instance of user at that name
159 # api_key, which is translated to instance of user at that name
158 USER_SESSION_ATTR = 'apiuser'
160 USER_SESSION_ATTR = 'apiuser'
159
161
160 if USER_SESSION_ATTR not in arglist:
162 if USER_SESSION_ATTR not in arglist:
161 return jsonrpc_error(message='This method [%s] does not support '
163 return jsonrpc_error(message='This method [%s] does not support '
162 'authentication (missing %s param)' %
164 'authentication (missing %s param)' %
163 (self._func.__name__, USER_SESSION_ATTR))
165 (self._func.__name__, USER_SESSION_ATTR))
164
166
165 # get our arglist and check if we provided them as args
167 # get our arglist and check if we provided them as args
166 for arg, default in kwarglist:
168 for arg, default in kwarglist:
167 if arg == USER_SESSION_ATTR:
169 if arg == USER_SESSION_ATTR:
168 # USER_SESSION_ATTR is something translated from api key and
170 # USER_SESSION_ATTR is something translated from api key and
169 # this is checked before so we don't need validate it
171 # this is checked before so we don't need validate it
170 continue
172 continue
171
173
172 # skip the required param check if it's default value is
174 # skip the required param check if it's default value is
173 # NotImplementedType (default_empty)
175 # NotImplementedType (default_empty)
174 if not self._req_params or (type(default) == default_empty
176 if not self._req_params or (type(default) == default_empty
175 and arg not in self._req_params):
177 and arg not in self._req_params):
176 return jsonrpc_error(message=('Missing non optional %s arg '
178 return jsonrpc_error(message=('Missing non optional %s arg '
177 'in JSON DATA') % arg)
179 'in JSON DATA') % arg)
178
180
179 self._rpc_args = {USER_SESSION_ATTR:u}
181 self._rpc_args = {USER_SESSION_ATTR:u}
180 self._rpc_args.update(self._req_params)
182 self._rpc_args.update(self._req_params)
181
183
182 self._rpc_args['action'] = self._req_method
184 self._rpc_args['action'] = self._req_method
183 self._rpc_args['environ'] = environ
185 self._rpc_args['environ'] = environ
184 self._rpc_args['start_response'] = start_response
186 self._rpc_args['start_response'] = start_response
185
187
186 status = []
188 status = []
187 headers = []
189 headers = []
188 exc_info = []
190 exc_info = []
189 def change_content(new_status, new_headers, new_exc_info=None):
191 def change_content(new_status, new_headers, new_exc_info=None):
190 status.append(new_status)
192 status.append(new_status)
191 headers.extend(new_headers)
193 headers.extend(new_headers)
192 exc_info.append(new_exc_info)
194 exc_info.append(new_exc_info)
193
195
194 output = WSGIController.__call__(self, environ, change_content)
196 output = WSGIController.__call__(self, environ, change_content)
195 output = list(output)
197 output = list(output)
196 headers.append(('Content-Length', str(len(output[0]))))
198 headers.append(('Content-Length', str(len(output[0]))))
197 replace_header(headers, 'Content-Type', 'application/json')
199 replace_header(headers, 'Content-Type', 'application/json')
198 start_response(status[0], headers, exc_info[0])
200 start_response(status[0], headers, exc_info[0])
199
201
200 return output
202 return output
201
203
202 def _dispatch_call(self):
204 def _dispatch_call(self):
203 """
205 """
204 Implement dispatch interface specified by WSGIController
206 Implement dispatch interface specified by WSGIController
205 """
207 """
206 try:
208 try:
207 raw_response = self._inspect_call(self._func)
209 raw_response = self._inspect_call(self._func)
208 if isinstance(raw_response, HTTPError):
210 if isinstance(raw_response, HTTPError):
209 self._error = str(raw_response)
211 self._error = str(raw_response)
210 except JSONRPCError, e:
212 except JSONRPCError, e:
211 self._error = str(e)
213 self._error = str(e)
212 except Exception, e:
214 except Exception, e:
213 log.error('Encountered unhandled exception: %s' \
215 log.error('Encountered unhandled exception: %s' \
214 % traceback.format_exc())
216 % traceback.format_exc())
215 json_exc = JSONRPCError('Internal server error')
217 json_exc = JSONRPCError('Internal server error')
216 self._error = str(json_exc)
218 self._error = str(json_exc)
217
219
218 if self._error is not None:
220 if self._error is not None:
219 raw_response = None
221 raw_response = None
220
222
221 response = dict(result=raw_response, error=self._error)
223 response = dict(result=raw_response, error=self._error)
222
224
223 try:
225 try:
224 return json.dumps(response)
226 return json.dumps(response)
225 except TypeError, e:
227 except TypeError, e:
226 log.debug('Error encoding response: %s', e)
228 log.debug('Error encoding response: %s', e)
227 return json.dumps(dict(result=None,
229 return json.dumps(dict(result=None,
228 error="Error encoding response"))
230 error="Error encoding response"))
229
231
230 def _find_method(self):
232 def _find_method(self):
231 """
233 """
232 Return method named by `self._req_method` in controller if able
234 Return method named by `self._req_method` in controller if able
233 """
235 """
234 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
236 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
235 if self._req_method.startswith('_'):
237 if self._req_method.startswith('_'):
236 raise AttributeError("Method not allowed")
238 raise AttributeError("Method not allowed")
237
239
238 try:
240 try:
239 func = getattr(self, self._req_method, None)
241 func = getattr(self, self._req_method, None)
240 except UnicodeEncodeError:
242 except UnicodeEncodeError:
241 raise AttributeError("Problem decoding unicode in requested "
243 raise AttributeError("Problem decoding unicode in requested "
242 "method name.")
244 "method name.")
243
245
244 if isinstance(func, types.MethodType):
246 if isinstance(func, types.MethodType):
245 return func
247 return func
246 else:
248 else:
247 raise AttributeError("No such method: %s" % self._req_method)
249 raise AttributeError("No such method: %s" % self._req_method)
248
250
@@ -1,1120 +1,1120 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from vcs import get_backend
38 from vcs import get_backend
39 from vcs.utils.helpers import get_scm
39 from vcs.utils.helpers import get_scm
40 from vcs.exceptions import VCSError
40 from vcs.exceptions import VCSError
41 from vcs.utils.lazy import LazyProperty
41 from vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
44 generate_api_key, safe_unicode
44 generate_api_key, safe_unicode
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib.compat import json
46 from rhodecode.lib.compat import json
47 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.lib.caching_query import FromCache
48
48
49 from rhodecode.model.meta import Base, Session
49 from rhodecode.model.meta import Base, Session
50
50
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55 #==============================================================================
55 #==============================================================================
56 # BASE CLASSES
56 # BASE CLASSES
57 #==============================================================================
57 #==============================================================================
58
58
59 class ModelSerializer(json.JSONEncoder):
59 class ModelSerializer(json.JSONEncoder):
60 """
60 """
61 Simple Serializer for JSON,
61 Simple Serializer for JSON,
62
62
63 usage::
63 usage::
64
64
65 to make object customized for serialization implement a __json__
65 to make object customized for serialization implement a __json__
66 method that will return a dict for serialization into json
66 method that will return a dict for serialization into json
67
67
68 example::
68 example::
69
69
70 class Task(object):
70 class Task(object):
71
71
72 def __init__(self, name, value):
72 def __init__(self, name, value):
73 self.name = name
73 self.name = name
74 self.value = value
74 self.value = value
75
75
76 def __json__(self):
76 def __json__(self):
77 return dict(name=self.name,
77 return dict(name=self.name,
78 value=self.value)
78 value=self.value)
79
79
80 """
80 """
81
81
82 def default(self, obj):
82 def default(self, obj):
83
83
84 if hasattr(obj, '__json__'):
84 if hasattr(obj, '__json__'):
85 return obj.__json__()
85 return obj.__json__()
86 else:
86 else:
87 return json.JSONEncoder.default(self, obj)
87 return json.JSONEncoder.default(self, obj)
88
88
89 class BaseModel(object):
89 class BaseModel(object):
90 """Base Model for all classess
90 """Base Model for all classess
91
91
92 """
92 """
93
93
94 @classmethod
94 @classmethod
95 def _get_keys(cls):
95 def _get_keys(cls):
96 """return column names for this model """
96 """return column names for this model """
97 return class_mapper(cls).c.keys()
97 return class_mapper(cls).c.keys()
98
98
99 def get_dict(self):
99 def get_dict(self):
100 """return dict with keys and values corresponding
100 """return dict with keys and values corresponding
101 to this model data """
101 to this model data """
102
102
103 d = {}
103 d = {}
104 for k in self._get_keys():
104 for k in self._get_keys():
105 d[k] = getattr(self, k)
105 d[k] = getattr(self, k)
106 return d
106 return d
107
107
108 def get_appstruct(self):
108 def get_appstruct(self):
109 """return list with keys and values tupples corresponding
109 """return list with keys and values tupples corresponding
110 to this model data """
110 to this model data """
111
111
112 l = []
112 l = []
113 for k in self._get_keys():
113 for k in self._get_keys():
114 l.append((k, getattr(self, k),))
114 l.append((k, getattr(self, k),))
115 return l
115 return l
116
116
117 def populate_obj(self, populate_dict):
117 def populate_obj(self, populate_dict):
118 """populate model with data from given populate_dict"""
118 """populate model with data from given populate_dict"""
119
119
120 for k in self._get_keys():
120 for k in self._get_keys():
121 if k in populate_dict:
121 if k in populate_dict:
122 setattr(self, k, populate_dict[k])
122 setattr(self, k, populate_dict[k])
123
123
124 @classmethod
124 @classmethod
125 def query(cls):
125 def query(cls):
126 return Session.query(cls)
126 return Session.query(cls)
127
127
128 @classmethod
128 @classmethod
129 def get(cls, id_):
129 def get(cls, id_):
130 if id_:
130 if id_:
131 return cls.query().get(id_)
131 return cls.query().get(id_)
132
132
133 @classmethod
133 @classmethod
134 def getAll(cls):
134 def getAll(cls):
135 return cls.query().all()
135 return cls.query().all()
136
136
137 @classmethod
137 @classmethod
138 def delete(cls, id_):
138 def delete(cls, id_):
139 obj = cls.query().get(id_)
139 obj = cls.query().get(id_)
140 Session.delete(obj)
140 Session.delete(obj)
141 Session.commit()
141 Session.commit()
142
142
143
143
144 class RhodeCodeSetting(Base, BaseModel):
144 class RhodeCodeSetting(Base, BaseModel):
145 __tablename__ = 'rhodecode_settings'
145 __tablename__ = 'rhodecode_settings'
146 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
146 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
147 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
148 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150
150
151 def __init__(self, k='', v=''):
151 def __init__(self, k='', v=''):
152 self.app_settings_name = k
152 self.app_settings_name = k
153 self.app_settings_value = v
153 self.app_settings_value = v
154
154
155
155
156 @validates('_app_settings_value')
156 @validates('_app_settings_value')
157 def validate_settings_value(self, key, val):
157 def validate_settings_value(self, key, val):
158 assert type(val) == unicode
158 assert type(val) == unicode
159 return val
159 return val
160
160
161 @hybrid_property
161 @hybrid_property
162 def app_settings_value(self):
162 def app_settings_value(self):
163 v = self._app_settings_value
163 v = self._app_settings_value
164 if v == 'ldap_active':
164 if v == 'ldap_active':
165 v = str2bool(v)
165 v = str2bool(v)
166 return v
166 return v
167
167
168 @app_settings_value.setter
168 @app_settings_value.setter
169 def app_settings_value(self, val):
169 def app_settings_value(self, val):
170 """
170 """
171 Setter that will always make sure we use unicode in app_settings_value
171 Setter that will always make sure we use unicode in app_settings_value
172
172
173 :param val:
173 :param val:
174 """
174 """
175 self._app_settings_value = safe_unicode(val)
175 self._app_settings_value = safe_unicode(val)
176
176
177 def __repr__(self):
177 def __repr__(self):
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 self.app_settings_name, self.app_settings_value)
179 self.app_settings_name, self.app_settings_value)
180
180
181
181
182 @classmethod
182 @classmethod
183 def get_by_name(cls, ldap_key):
183 def get_by_name(cls, ldap_key):
184 return cls.query()\
184 return cls.query()\
185 .filter(cls.app_settings_name == ldap_key).scalar()
185 .filter(cls.app_settings_name == ldap_key).scalar()
186
186
187 @classmethod
187 @classmethod
188 def get_app_settings(cls, cache=False):
188 def get_app_settings(cls, cache=False):
189
189
190 ret = cls.query()
190 ret = cls.query()
191
191
192 if cache:
192 if cache:
193 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
193 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
194
194
195 if not ret:
195 if not ret:
196 raise Exception('Could not get application settings !')
196 raise Exception('Could not get application settings !')
197 settings = {}
197 settings = {}
198 for each in ret:
198 for each in ret:
199 settings['rhodecode_' + each.app_settings_name] = \
199 settings['rhodecode_' + each.app_settings_name] = \
200 each.app_settings_value
200 each.app_settings_value
201
201
202 return settings
202 return settings
203
203
204 @classmethod
204 @classmethod
205 def get_ldap_settings(cls, cache=False):
205 def get_ldap_settings(cls, cache=False):
206 ret = cls.query()\
206 ret = cls.query()\
207 .filter(cls.app_settings_name.startswith('ldap_')).all()
207 .filter(cls.app_settings_name.startswith('ldap_')).all()
208 fd = {}
208 fd = {}
209 for row in ret:
209 for row in ret:
210 fd.update({row.app_settings_name:row.app_settings_value})
210 fd.update({row.app_settings_name:row.app_settings_value})
211
211
212 return fd
212 return fd
213
213
214
214
215 class RhodeCodeUi(Base, BaseModel):
215 class RhodeCodeUi(Base, BaseModel):
216 __tablename__ = 'rhodecode_ui'
216 __tablename__ = 'rhodecode_ui'
217 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
217 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
218
218
219 HOOK_UPDATE = 'changegroup.update'
219 HOOK_UPDATE = 'changegroup.update'
220 HOOK_REPO_SIZE = 'changegroup.repo_size'
220 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 HOOK_PUSH = 'pretxnchangegroup.push_logger'
221 HOOK_PUSH = 'pretxnchangegroup.push_logger'
222 HOOK_PULL = 'preoutgoing.pull_logger'
222 HOOK_PULL = 'preoutgoing.pull_logger'
223
223
224 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
224 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
225 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
228 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
229
229
230
230
231 @classmethod
231 @classmethod
232 def get_by_key(cls, key):
232 def get_by_key(cls, key):
233 return cls.query().filter(cls.ui_key == key)
233 return cls.query().filter(cls.ui_key == key)
234
234
235
235
236 @classmethod
236 @classmethod
237 def get_builtin_hooks(cls):
237 def get_builtin_hooks(cls):
238 q = cls.query()
238 q = cls.query()
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
240 cls.HOOK_REPO_SIZE,
240 cls.HOOK_REPO_SIZE,
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 return q.all()
242 return q.all()
243
243
244 @classmethod
244 @classmethod
245 def get_custom_hooks(cls):
245 def get_custom_hooks(cls):
246 q = cls.query()
246 q = cls.query()
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
248 cls.HOOK_REPO_SIZE,
248 cls.HOOK_REPO_SIZE,
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
250 q = q.filter(cls.ui_section == 'hooks')
250 q = q.filter(cls.ui_section == 'hooks')
251 return q.all()
251 return q.all()
252
252
253 @classmethod
253 @classmethod
254 def create_or_update_hook(cls, key, val):
254 def create_or_update_hook(cls, key, val):
255 new_ui = cls.get_by_key(key).scalar() or cls()
255 new_ui = cls.get_by_key(key).scalar() or cls()
256 new_ui.ui_section = 'hooks'
256 new_ui.ui_section = 'hooks'
257 new_ui.ui_active = True
257 new_ui.ui_active = True
258 new_ui.ui_key = key
258 new_ui.ui_key = key
259 new_ui.ui_value = val
259 new_ui.ui_value = val
260
260
261 Session.add(new_ui)
261 Session.add(new_ui)
262 Session.commit()
262 Session.commit()
263
263
264
264
265 class User(Base, BaseModel):
265 class User(Base, BaseModel):
266 __tablename__ = 'users'
266 __tablename__ = 'users'
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279
279
280 user_log = relationship('UserLog', cascade='all')
280 user_log = relationship('UserLog', cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
282
282
283 repositories = relationship('Repository')
283 repositories = relationship('Repository')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
286
286
287 group_member = relationship('UsersGroupMember', cascade='all')
287 group_member = relationship('UsersGroupMember', cascade='all')
288
288
289 @property
289 @property
290 def full_contact(self):
290 def full_contact(self):
291 return '%s %s <%s>' % (self.name, self.lastname, self.email)
291 return '%s %s <%s>' % (self.name, self.lastname, self.email)
292
292
293 @property
293 @property
294 def short_contact(self):
294 def short_contact(self):
295 return '%s %s' % (self.name, self.lastname)
295 return '%s %s' % (self.name, self.lastname)
296
296
297 @property
297 @property
298 def is_admin(self):
298 def is_admin(self):
299 return self.admin
299 return self.admin
300
300
301 def __repr__(self):
301 def __repr__(self):
302 try:
302 try:
303 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
303 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
304 self.user_id, self.username)
304 self.user_id, self.username)
305 except:
305 except:
306 return self.__class__.__name__
306 return self.__class__.__name__
307
307
308 @classmethod
308 @classmethod
309 def get_by_username(cls, username, case_insensitive=False, cache=False):
309 def get_by_username(cls, username, case_insensitive=False, cache=False):
310 if case_insensitive:
310 if case_insensitive:
311 q = cls.query().filter(cls.username.ilike(username))
311 q = cls.query().filter(cls.username.ilike(username))
312 else:
312 else:
313 q = cls.query().filter(cls.username == username)
313 q = cls.query().filter(cls.username == username)
314
314
315 if cache:
315 if cache:
316 q = q.options(FromCache("sql_cache_short",
316 q = q.options(FromCache("sql_cache_short",
317 "get_user_%s" % username))
317 "get_user_%s" % username))
318 return q.scalar()
318 return q.scalar()
319
319
320 @classmethod
320 @classmethod
321 def get_by_api_key(cls, api_key, cache=False):
321 def get_by_api_key(cls, api_key, cache=False):
322 q = cls.query().filter(cls.api_key == api_key)
322 q = cls.query().filter(cls.api_key == api_key)
323
323
324 if cache:
324 if cache:
325 q = q.options(FromCache("sql_cache_short",
325 q = q.options(FromCache("sql_cache_short",
326 "get_api_key_%s" % api_key))
326 "get_api_key_%s" % api_key))
327 q.one()
327 return q.scalar()
328
328
329 def update_lastlogin(self):
329 def update_lastlogin(self):
330 """Update user lastlogin"""
330 """Update user lastlogin"""
331
331
332 self.last_login = datetime.datetime.now()
332 self.last_login = datetime.datetime.now()
333 Session.add(self)
333 Session.add(self)
334 Session.commit()
334 Session.commit()
335 log.debug('updated user %s lastlogin', self.username)
335 log.debug('updated user %s lastlogin', self.username)
336
336
337 class UserLog(Base, BaseModel):
337 class UserLog(Base, BaseModel):
338 __tablename__ = 'user_logs'
338 __tablename__ = 'user_logs'
339 __table_args__ = {'extend_existing':True}
339 __table_args__ = {'extend_existing':True}
340 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
340 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
342 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
342 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
343 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
343 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
344 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
344 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
346 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
347
347
348 @property
348 @property
349 def action_as_day(self):
349 def action_as_day(self):
350 return date(*self.action_date.timetuple()[:3])
350 return date(*self.action_date.timetuple()[:3])
351
351
352 user = relationship('User')
352 user = relationship('User')
353 repository = relationship('Repository')
353 repository = relationship('Repository')
354
354
355
355
356 class UsersGroup(Base, BaseModel):
356 class UsersGroup(Base, BaseModel):
357 __tablename__ = 'users_groups'
357 __tablename__ = 'users_groups'
358 __table_args__ = {'extend_existing':True}
358 __table_args__ = {'extend_existing':True}
359
359
360 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
360 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
361 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
361 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
362 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
362 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
363
363
364 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
364 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
365
365
366 def __repr__(self):
366 def __repr__(self):
367 return '<userGroup(%s)>' % (self.users_group_name)
367 return '<userGroup(%s)>' % (self.users_group_name)
368
368
369 @classmethod
369 @classmethod
370 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
370 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
371 if case_insensitive:
371 if case_insensitive:
372 gr = cls.query()\
372 gr = cls.query()\
373 .filter(cls.users_group_name.ilike(group_name))
373 .filter(cls.users_group_name.ilike(group_name))
374 else:
374 else:
375 gr = cls.query()\
375 gr = cls.query()\
376 .filter(cls.users_group_name == group_name)
376 .filter(cls.users_group_name == group_name)
377 if cache:
377 if cache:
378 gr = gr.options(FromCache("sql_cache_short",
378 gr = gr.options(FromCache("sql_cache_short",
379 "get_user_%s" % group_name))
379 "get_user_%s" % group_name))
380 return gr.scalar()
380 return gr.scalar()
381
381
382
382
383 @classmethod
383 @classmethod
384 def get(cls, users_group_id, cache=False):
384 def get(cls, users_group_id, cache=False):
385 users_group = cls.query()
385 users_group = cls.query()
386 if cache:
386 if cache:
387 users_group = users_group.options(FromCache("sql_cache_short",
387 users_group = users_group.options(FromCache("sql_cache_short",
388 "get_users_group_%s" % users_group_id))
388 "get_users_group_%s" % users_group_id))
389 return users_group.get(users_group_id)
389 return users_group.get(users_group_id)
390
390
391 @classmethod
391 @classmethod
392 def create(cls, form_data):
392 def create(cls, form_data):
393 try:
393 try:
394 new_users_group = cls()
394 new_users_group = cls()
395 for k, v in form_data.items():
395 for k, v in form_data.items():
396 setattr(new_users_group, k, v)
396 setattr(new_users_group, k, v)
397
397
398 Session.add(new_users_group)
398 Session.add(new_users_group)
399 Session.commit()
399 Session.commit()
400 return new_users_group
400 return new_users_group
401 except:
401 except:
402 log.error(traceback.format_exc())
402 log.error(traceback.format_exc())
403 Session.rollback()
403 Session.rollback()
404 raise
404 raise
405
405
406 @classmethod
406 @classmethod
407 def update(cls, users_group_id, form_data):
407 def update(cls, users_group_id, form_data):
408
408
409 try:
409 try:
410 users_group = cls.get(users_group_id, cache=False)
410 users_group = cls.get(users_group_id, cache=False)
411
411
412 for k, v in form_data.items():
412 for k, v in form_data.items():
413 if k == 'users_group_members':
413 if k == 'users_group_members':
414 users_group.members = []
414 users_group.members = []
415 Session.flush()
415 Session.flush()
416 members_list = []
416 members_list = []
417 if v:
417 if v:
418 v = [v] if isinstance(v, basestring) else v
418 v = [v] if isinstance(v, basestring) else v
419 for u_id in set(v):
419 for u_id in set(v):
420 member = UsersGroupMember(users_group_id, u_id)
420 member = UsersGroupMember(users_group_id, u_id)
421 members_list.append(member)
421 members_list.append(member)
422 setattr(users_group, 'members', members_list)
422 setattr(users_group, 'members', members_list)
423 setattr(users_group, k, v)
423 setattr(users_group, k, v)
424
424
425 Session.add(users_group)
425 Session.add(users_group)
426 Session.commit()
426 Session.commit()
427 except:
427 except:
428 log.error(traceback.format_exc())
428 log.error(traceback.format_exc())
429 Session.rollback()
429 Session.rollback()
430 raise
430 raise
431
431
432 @classmethod
432 @classmethod
433 def delete(cls, users_group_id):
433 def delete(cls, users_group_id):
434 try:
434 try:
435
435
436 # check if this group is not assigned to repo
436 # check if this group is not assigned to repo
437 assigned_groups = UsersGroupRepoToPerm.query()\
437 assigned_groups = UsersGroupRepoToPerm.query()\
438 .filter(UsersGroupRepoToPerm.users_group_id ==
438 .filter(UsersGroupRepoToPerm.users_group_id ==
439 users_group_id).all()
439 users_group_id).all()
440
440
441 if assigned_groups:
441 if assigned_groups:
442 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
442 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
443 assigned_groups)
443 assigned_groups)
444
444
445 users_group = cls.get(users_group_id, cache=False)
445 users_group = cls.get(users_group_id, cache=False)
446 Session.delete(users_group)
446 Session.delete(users_group)
447 Session.commit()
447 Session.commit()
448 except:
448 except:
449 log.error(traceback.format_exc())
449 log.error(traceback.format_exc())
450 Session.rollback()
450 Session.rollback()
451 raise
451 raise
452
452
453 class UsersGroupMember(Base, BaseModel):
453 class UsersGroupMember(Base, BaseModel):
454 __tablename__ = 'users_groups_members'
454 __tablename__ = 'users_groups_members'
455 __table_args__ = {'extend_existing':True}
455 __table_args__ = {'extend_existing':True}
456
456
457 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
457 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
458 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
460
460
461 user = relationship('User', lazy='joined')
461 user = relationship('User', lazy='joined')
462 users_group = relationship('UsersGroup')
462 users_group = relationship('UsersGroup')
463
463
464 def __init__(self, gr_id='', u_id=''):
464 def __init__(self, gr_id='', u_id=''):
465 self.users_group_id = gr_id
465 self.users_group_id = gr_id
466 self.user_id = u_id
466 self.user_id = u_id
467
467
468 @staticmethod
468 @staticmethod
469 def add_user_to_group(group, user):
469 def add_user_to_group(group, user):
470 ugm = UsersGroupMember()
470 ugm = UsersGroupMember()
471 ugm.users_group = group
471 ugm.users_group = group
472 ugm.user = user
472 ugm.user = user
473 Session.add(ugm)
473 Session.add(ugm)
474 Session.commit()
474 Session.commit()
475 return ugm
475 return ugm
476
476
477 class Repository(Base, BaseModel):
477 class Repository(Base, BaseModel):
478 __tablename__ = 'repositories'
478 __tablename__ = 'repositories'
479 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
479 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
480
480
481 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
482 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
483 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
483 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
484 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
484 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
485 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
485 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
486 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
486 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
487 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
487 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
488 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
488 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
489 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
489 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
490 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
490 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
491
491
492 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
492 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
493 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
493 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
494
494
495
495
496 user = relationship('User')
496 user = relationship('User')
497 fork = relationship('Repository', remote_side=repo_id)
497 fork = relationship('Repository', remote_side=repo_id)
498 group = relationship('RepoGroup')
498 group = relationship('RepoGroup')
499 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
499 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
500 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
500 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
501 stats = relationship('Statistics', cascade='all', uselist=False)
501 stats = relationship('Statistics', cascade='all', uselist=False)
502
502
503 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
503 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
504
504
505 logs = relationship('UserLog', cascade='all')
505 logs = relationship('UserLog', cascade='all')
506
506
507 def __repr__(self):
507 def __repr__(self):
508 return "<%s('%s:%s')>" % (self.__class__.__name__,
508 return "<%s('%s:%s')>" % (self.__class__.__name__,
509 self.repo_id, self.repo_name)
509 self.repo_id, self.repo_name)
510
510
511 @classmethod
511 @classmethod
512 def url_sep(cls):
512 def url_sep(cls):
513 return '/'
513 return '/'
514
514
515 @classmethod
515 @classmethod
516 def get_by_repo_name(cls, repo_name):
516 def get_by_repo_name(cls, repo_name):
517 q = Session.query(cls).filter(cls.repo_name == repo_name)
517 q = Session.query(cls).filter(cls.repo_name == repo_name)
518 q = q.options(joinedload(Repository.fork))\
518 q = q.options(joinedload(Repository.fork))\
519 .options(joinedload(Repository.user))\
519 .options(joinedload(Repository.user))\
520 .options(joinedload(Repository.group))
520 .options(joinedload(Repository.group))
521 return q.one()
521 return q.one()
522
522
523 @classmethod
523 @classmethod
524 def get_repo_forks(cls, repo_id):
524 def get_repo_forks(cls, repo_id):
525 return cls.query().filter(Repository.fork_id == repo_id)
525 return cls.query().filter(Repository.fork_id == repo_id)
526
526
527 @classmethod
527 @classmethod
528 def base_path(cls):
528 def base_path(cls):
529 """
529 """
530 Returns base path when all repos are stored
530 Returns base path when all repos are stored
531
531
532 :param cls:
532 :param cls:
533 """
533 """
534 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
534 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
535 cls.url_sep())
535 cls.url_sep())
536 q.options(FromCache("sql_cache_short", "repository_repo_path"))
536 q.options(FromCache("sql_cache_short", "repository_repo_path"))
537 return q.one().ui_value
537 return q.one().ui_value
538
538
539 @property
539 @property
540 def just_name(self):
540 def just_name(self):
541 return self.repo_name.split(Repository.url_sep())[-1]
541 return self.repo_name.split(Repository.url_sep())[-1]
542
542
543 @property
543 @property
544 def groups_with_parents(self):
544 def groups_with_parents(self):
545 groups = []
545 groups = []
546 if self.group is None:
546 if self.group is None:
547 return groups
547 return groups
548
548
549 cur_gr = self.group
549 cur_gr = self.group
550 groups.insert(0, cur_gr)
550 groups.insert(0, cur_gr)
551 while 1:
551 while 1:
552 gr = getattr(cur_gr, 'parent_group', None)
552 gr = getattr(cur_gr, 'parent_group', None)
553 cur_gr = cur_gr.parent_group
553 cur_gr = cur_gr.parent_group
554 if gr is None:
554 if gr is None:
555 break
555 break
556 groups.insert(0, gr)
556 groups.insert(0, gr)
557
557
558 return groups
558 return groups
559
559
560 @property
560 @property
561 def groups_and_repo(self):
561 def groups_and_repo(self):
562 return self.groups_with_parents, self.just_name
562 return self.groups_with_parents, self.just_name
563
563
564 @LazyProperty
564 @LazyProperty
565 def repo_path(self):
565 def repo_path(self):
566 """
566 """
567 Returns base full path for that repository means where it actually
567 Returns base full path for that repository means where it actually
568 exists on a filesystem
568 exists on a filesystem
569 """
569 """
570 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
570 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
571 Repository.url_sep())
571 Repository.url_sep())
572 q.options(FromCache("sql_cache_short", "repository_repo_path"))
572 q.options(FromCache("sql_cache_short", "repository_repo_path"))
573 return q.one().ui_value
573 return q.one().ui_value
574
574
575 @property
575 @property
576 def repo_full_path(self):
576 def repo_full_path(self):
577 p = [self.repo_path]
577 p = [self.repo_path]
578 # we need to split the name by / since this is how we store the
578 # we need to split the name by / since this is how we store the
579 # names in the database, but that eventually needs to be converted
579 # names in the database, but that eventually needs to be converted
580 # into a valid system path
580 # into a valid system path
581 p += self.repo_name.split(Repository.url_sep())
581 p += self.repo_name.split(Repository.url_sep())
582 return os.path.join(*p)
582 return os.path.join(*p)
583
583
584 def get_new_name(self, repo_name):
584 def get_new_name(self, repo_name):
585 """
585 """
586 returns new full repository name based on assigned group and new new
586 returns new full repository name based on assigned group and new new
587
587
588 :param group_name:
588 :param group_name:
589 """
589 """
590 path_prefix = self.group.full_path_splitted if self.group else []
590 path_prefix = self.group.full_path_splitted if self.group else []
591 return Repository.url_sep().join(path_prefix + [repo_name])
591 return Repository.url_sep().join(path_prefix + [repo_name])
592
592
593 @property
593 @property
594 def _ui(self):
594 def _ui(self):
595 """
595 """
596 Creates an db based ui object for this repository
596 Creates an db based ui object for this repository
597 """
597 """
598 from mercurial import ui
598 from mercurial import ui
599 from mercurial import config
599 from mercurial import config
600 baseui = ui.ui()
600 baseui = ui.ui()
601
601
602 #clean the baseui object
602 #clean the baseui object
603 baseui._ocfg = config.config()
603 baseui._ocfg = config.config()
604 baseui._ucfg = config.config()
604 baseui._ucfg = config.config()
605 baseui._tcfg = config.config()
605 baseui._tcfg = config.config()
606
606
607
607
608 ret = RhodeCodeUi.query()\
608 ret = RhodeCodeUi.query()\
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
610
610
611 hg_ui = ret
611 hg_ui = ret
612 for ui_ in hg_ui:
612 for ui_ in hg_ui:
613 if ui_.ui_active:
613 if ui_.ui_active:
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
615 ui_.ui_key, ui_.ui_value)
615 ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
617
617
618 return baseui
618 return baseui
619
619
620 @classmethod
620 @classmethod
621 def is_valid(cls, repo_name):
621 def is_valid(cls, repo_name):
622 """
622 """
623 returns True if given repo name is a valid filesystem repository
623 returns True if given repo name is a valid filesystem repository
624
624
625 @param cls:
625 @param cls:
626 @param repo_name:
626 @param repo_name:
627 """
627 """
628 from rhodecode.lib.utils import is_valid_repo
628 from rhodecode.lib.utils import is_valid_repo
629
629
630 return is_valid_repo(repo_name, cls.base_path())
630 return is_valid_repo(repo_name, cls.base_path())
631
631
632
632
633 #==========================================================================
633 #==========================================================================
634 # SCM PROPERTIES
634 # SCM PROPERTIES
635 #==========================================================================
635 #==========================================================================
636
636
637 def get_changeset(self, rev):
637 def get_changeset(self, rev):
638 return get_changeset_safe(self.scm_instance, rev)
638 return get_changeset_safe(self.scm_instance, rev)
639
639
640 @property
640 @property
641 def tip(self):
641 def tip(self):
642 return self.get_changeset('tip')
642 return self.get_changeset('tip')
643
643
644 @property
644 @property
645 def author(self):
645 def author(self):
646 return self.tip.author
646 return self.tip.author
647
647
648 @property
648 @property
649 def last_change(self):
649 def last_change(self):
650 return self.scm_instance.last_change
650 return self.scm_instance.last_change
651
651
652 #==========================================================================
652 #==========================================================================
653 # SCM CACHE INSTANCE
653 # SCM CACHE INSTANCE
654 #==========================================================================
654 #==========================================================================
655
655
656 @property
656 @property
657 def invalidate(self):
657 def invalidate(self):
658 return CacheInvalidation.invalidate(self.repo_name)
658 return CacheInvalidation.invalidate(self.repo_name)
659
659
660 def set_invalidate(self):
660 def set_invalidate(self):
661 """
661 """
662 set a cache for invalidation for this instance
662 set a cache for invalidation for this instance
663 """
663 """
664 CacheInvalidation.set_invalidate(self.repo_name)
664 CacheInvalidation.set_invalidate(self.repo_name)
665
665
666 @LazyProperty
666 @LazyProperty
667 def scm_instance(self):
667 def scm_instance(self):
668 return self.__get_instance()
668 return self.__get_instance()
669
669
670 @property
670 @property
671 def scm_instance_cached(self):
671 def scm_instance_cached(self):
672 @cache_region('long_term')
672 @cache_region('long_term')
673 def _c(repo_name):
673 def _c(repo_name):
674 return self.__get_instance()
674 return self.__get_instance()
675 rn = self.repo_name
675 rn = self.repo_name
676
676
677 inv = self.invalidate
677 inv = self.invalidate
678 if inv is not None:
678 if inv is not None:
679 region_invalidate(_c, None, rn)
679 region_invalidate(_c, None, rn)
680 # update our cache
680 # update our cache
681 CacheInvalidation.set_valid(inv.cache_key)
681 CacheInvalidation.set_valid(inv.cache_key)
682 return _c(rn)
682 return _c(rn)
683
683
684 def __get_instance(self):
684 def __get_instance(self):
685
685
686 repo_full_path = self.repo_full_path
686 repo_full_path = self.repo_full_path
687
687
688 try:
688 try:
689 alias = get_scm(repo_full_path)[0]
689 alias = get_scm(repo_full_path)[0]
690 log.debug('Creating instance of %s repository', alias)
690 log.debug('Creating instance of %s repository', alias)
691 backend = get_backend(alias)
691 backend = get_backend(alias)
692 except VCSError:
692 except VCSError:
693 log.error(traceback.format_exc())
693 log.error(traceback.format_exc())
694 log.error('Perhaps this repository is in db and not in '
694 log.error('Perhaps this repository is in db and not in '
695 'filesystem run rescan repositories with '
695 'filesystem run rescan repositories with '
696 '"destroy old data " option from admin panel')
696 '"destroy old data " option from admin panel')
697 return
697 return
698
698
699 if alias == 'hg':
699 if alias == 'hg':
700
700
701 repo = backend(safe_str(repo_full_path), create=False,
701 repo = backend(safe_str(repo_full_path), create=False,
702 baseui=self._ui)
702 baseui=self._ui)
703 # skip hidden web repository
703 # skip hidden web repository
704 if repo._get_hidden():
704 if repo._get_hidden():
705 return
705 return
706 else:
706 else:
707 repo = backend(repo_full_path, create=False)
707 repo = backend(repo_full_path, create=False)
708
708
709 return repo
709 return repo
710
710
711
711
712 class RepoGroup(Base, BaseModel):
712 class RepoGroup(Base, BaseModel):
713 __tablename__ = 'groups'
713 __tablename__ = 'groups'
714 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
714 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
715 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
715 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
716 __mapper_args__ = {'order_by':'group_name'}
716 __mapper_args__ = {'order_by':'group_name'}
717
717
718 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
718 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
719 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
719 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
720 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
720 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
721 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
721 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
722
722
723 parent_group = relationship('RepoGroup', remote_side=group_id)
723 parent_group = relationship('RepoGroup', remote_side=group_id)
724
724
725
725
726 def __init__(self, group_name='', parent_group=None):
726 def __init__(self, group_name='', parent_group=None):
727 self.group_name = group_name
727 self.group_name = group_name
728 self.parent_group = parent_group
728 self.parent_group = parent_group
729
729
730 def __repr__(self):
730 def __repr__(self):
731 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
731 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
732 self.group_name)
732 self.group_name)
733
733
734 @classmethod
734 @classmethod
735 def groups_choices(cls):
735 def groups_choices(cls):
736 from webhelpers.html import literal as _literal
736 from webhelpers.html import literal as _literal
737 repo_groups = [('', '')]
737 repo_groups = [('', '')]
738 sep = ' &raquo; '
738 sep = ' &raquo; '
739 _name = lambda k: _literal(sep.join(k))
739 _name = lambda k: _literal(sep.join(k))
740
740
741 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
741 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
742 for x in cls.query().all()])
742 for x in cls.query().all()])
743
743
744 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
744 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
745 return repo_groups
745 return repo_groups
746
746
747 @classmethod
747 @classmethod
748 def url_sep(cls):
748 def url_sep(cls):
749 return '/'
749 return '/'
750
750
751 @classmethod
751 @classmethod
752 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
752 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
753 if case_insensitive:
753 if case_insensitive:
754 gr = cls.query()\
754 gr = cls.query()\
755 .filter(cls.group_name.ilike(group_name))
755 .filter(cls.group_name.ilike(group_name))
756 else:
756 else:
757 gr = cls.query()\
757 gr = cls.query()\
758 .filter(cls.group_name == group_name)
758 .filter(cls.group_name == group_name)
759 if cache:
759 if cache:
760 gr = gr.options(FromCache("sql_cache_short",
760 gr = gr.options(FromCache("sql_cache_short",
761 "get_group_%s" % group_name))
761 "get_group_%s" % group_name))
762 return gr.scalar()
762 return gr.scalar()
763
763
764 @property
764 @property
765 def parents(self):
765 def parents(self):
766 parents_recursion_limit = 5
766 parents_recursion_limit = 5
767 groups = []
767 groups = []
768 if self.parent_group is None:
768 if self.parent_group is None:
769 return groups
769 return groups
770 cur_gr = self.parent_group
770 cur_gr = self.parent_group
771 groups.insert(0, cur_gr)
771 groups.insert(0, cur_gr)
772 cnt = 0
772 cnt = 0
773 while 1:
773 while 1:
774 cnt += 1
774 cnt += 1
775 gr = getattr(cur_gr, 'parent_group', None)
775 gr = getattr(cur_gr, 'parent_group', None)
776 cur_gr = cur_gr.parent_group
776 cur_gr = cur_gr.parent_group
777 if gr is None:
777 if gr is None:
778 break
778 break
779 if cnt == parents_recursion_limit:
779 if cnt == parents_recursion_limit:
780 # this will prevent accidental infinit loops
780 # this will prevent accidental infinit loops
781 log.error('group nested more than %s' %
781 log.error('group nested more than %s' %
782 parents_recursion_limit)
782 parents_recursion_limit)
783 break
783 break
784
784
785 groups.insert(0, gr)
785 groups.insert(0, gr)
786 return groups
786 return groups
787
787
788 @property
788 @property
789 def children(self):
789 def children(self):
790 return RepoGroup.query().filter(RepoGroup.parent_group == self)
790 return RepoGroup.query().filter(RepoGroup.parent_group == self)
791
791
792 @property
792 @property
793 def name(self):
793 def name(self):
794 return self.group_name.split(RepoGroup.url_sep())[-1]
794 return self.group_name.split(RepoGroup.url_sep())[-1]
795
795
796 @property
796 @property
797 def full_path(self):
797 def full_path(self):
798 return self.group_name
798 return self.group_name
799
799
800 @property
800 @property
801 def full_path_splitted(self):
801 def full_path_splitted(self):
802 return self.group_name.split(RepoGroup.url_sep())
802 return self.group_name.split(RepoGroup.url_sep())
803
803
804 @property
804 @property
805 def repositories(self):
805 def repositories(self):
806 return Repository.query().filter(Repository.group == self)
806 return Repository.query().filter(Repository.group == self)
807
807
808 @property
808 @property
809 def repositories_recursive_count(self):
809 def repositories_recursive_count(self):
810 cnt = self.repositories.count()
810 cnt = self.repositories.count()
811
811
812 def children_count(group):
812 def children_count(group):
813 cnt = 0
813 cnt = 0
814 for child in group.children:
814 for child in group.children:
815 cnt += child.repositories.count()
815 cnt += child.repositories.count()
816 cnt += children_count(child)
816 cnt += children_count(child)
817 return cnt
817 return cnt
818
818
819 return cnt + children_count(self)
819 return cnt + children_count(self)
820
820
821
821
822 def get_new_name(self, group_name):
822 def get_new_name(self, group_name):
823 """
823 """
824 returns new full group name based on parent and new name
824 returns new full group name based on parent and new name
825
825
826 :param group_name:
826 :param group_name:
827 """
827 """
828 path_prefix = (self.parent_group.full_path_splitted if
828 path_prefix = (self.parent_group.full_path_splitted if
829 self.parent_group else [])
829 self.parent_group else [])
830 return RepoGroup.url_sep().join(path_prefix + [group_name])
830 return RepoGroup.url_sep().join(path_prefix + [group_name])
831
831
832
832
833 class Permission(Base, BaseModel):
833 class Permission(Base, BaseModel):
834 __tablename__ = 'permissions'
834 __tablename__ = 'permissions'
835 __table_args__ = {'extend_existing':True}
835 __table_args__ = {'extend_existing':True}
836 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
836 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
837 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
838 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
838 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
839
839
840 def __repr__(self):
840 def __repr__(self):
841 return "<%s('%s:%s')>" % (self.__class__.__name__,
841 return "<%s('%s:%s')>" % (self.__class__.__name__,
842 self.permission_id, self.permission_name)
842 self.permission_id, self.permission_name)
843
843
844 @classmethod
844 @classmethod
845 def get_by_key(cls, key):
845 def get_by_key(cls, key):
846 return cls.query().filter(cls.permission_name == key).scalar()
846 return cls.query().filter(cls.permission_name == key).scalar()
847
847
848 class UserRepoToPerm(Base, BaseModel):
848 class UserRepoToPerm(Base, BaseModel):
849 __tablename__ = 'repo_to_perm'
849 __tablename__ = 'repo_to_perm'
850 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
850 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
851 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
851 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
853 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
853 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
854 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
854 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
855
855
856 user = relationship('User')
856 user = relationship('User')
857 permission = relationship('Permission')
857 permission = relationship('Permission')
858 repository = relationship('Repository')
858 repository = relationship('Repository')
859
859
860 class UserToPerm(Base, BaseModel):
860 class UserToPerm(Base, BaseModel):
861 __tablename__ = 'user_to_perm'
861 __tablename__ = 'user_to_perm'
862 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
862 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
863 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
863 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
864 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
864 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
865 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
865 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
866
866
867 user = relationship('User')
867 user = relationship('User')
868 permission = relationship('Permission')
868 permission = relationship('Permission')
869
869
870 @classmethod
870 @classmethod
871 def has_perm(cls, user_id, perm):
871 def has_perm(cls, user_id, perm):
872 if not isinstance(perm, Permission):
872 if not isinstance(perm, Permission):
873 raise Exception('perm needs to be an instance of Permission class')
873 raise Exception('perm needs to be an instance of Permission class')
874
874
875 return cls.query().filter(cls.user_id == user_id)\
875 return cls.query().filter(cls.user_id == user_id)\
876 .filter(cls.permission == perm).scalar() is not None
876 .filter(cls.permission == perm).scalar() is not None
877
877
878 @classmethod
878 @classmethod
879 def grant_perm(cls, user_id, perm):
879 def grant_perm(cls, user_id, perm):
880 if not isinstance(perm, Permission):
880 if not isinstance(perm, Permission):
881 raise Exception('perm needs to be an instance of Permission class')
881 raise Exception('perm needs to be an instance of Permission class')
882
882
883 new = cls()
883 new = cls()
884 new.user_id = user_id
884 new.user_id = user_id
885 new.permission = perm
885 new.permission = perm
886 try:
886 try:
887 Session.add(new)
887 Session.add(new)
888 Session.commit()
888 Session.commit()
889 except:
889 except:
890 Session.rollback()
890 Session.rollback()
891
891
892
892
893 @classmethod
893 @classmethod
894 def revoke_perm(cls, user_id, perm):
894 def revoke_perm(cls, user_id, perm):
895 if not isinstance(perm, Permission):
895 if not isinstance(perm, Permission):
896 raise Exception('perm needs to be an instance of Permission class')
896 raise Exception('perm needs to be an instance of Permission class')
897
897
898 try:
898 try:
899 cls.query().filter(cls.user_id == user_id)\
899 cls.query().filter(cls.user_id == user_id)\
900 .filter(cls.permission == perm).delete()
900 .filter(cls.permission == perm).delete()
901 Session.commit()
901 Session.commit()
902 except:
902 except:
903 Session.rollback()
903 Session.rollback()
904
904
905 class UsersGroupRepoToPerm(Base, BaseModel):
905 class UsersGroupRepoToPerm(Base, BaseModel):
906 __tablename__ = 'users_group_repo_to_perm'
906 __tablename__ = 'users_group_repo_to_perm'
907 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
907 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
908 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
908 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
909 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
909 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
910 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
910 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
911 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
911 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
912
912
913 users_group = relationship('UsersGroup')
913 users_group = relationship('UsersGroup')
914 permission = relationship('Permission')
914 permission = relationship('Permission')
915 repository = relationship('Repository')
915 repository = relationship('Repository')
916
916
917 def __repr__(self):
917 def __repr__(self):
918 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
918 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
919
919
920 class UsersGroupToPerm(Base, BaseModel):
920 class UsersGroupToPerm(Base, BaseModel):
921 __tablename__ = 'users_group_to_perm'
921 __tablename__ = 'users_group_to_perm'
922 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
922 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
923 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
923 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
924 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
924 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
925
925
926 users_group = relationship('UsersGroup')
926 users_group = relationship('UsersGroup')
927 permission = relationship('Permission')
927 permission = relationship('Permission')
928
928
929
929
930 @classmethod
930 @classmethod
931 def has_perm(cls, users_group_id, perm):
931 def has_perm(cls, users_group_id, perm):
932 if not isinstance(perm, Permission):
932 if not isinstance(perm, Permission):
933 raise Exception('perm needs to be an instance of Permission class')
933 raise Exception('perm needs to be an instance of Permission class')
934
934
935 return cls.query().filter(cls.users_group_id ==
935 return cls.query().filter(cls.users_group_id ==
936 users_group_id)\
936 users_group_id)\
937 .filter(cls.permission == perm)\
937 .filter(cls.permission == perm)\
938 .scalar() is not None
938 .scalar() is not None
939
939
940 @classmethod
940 @classmethod
941 def grant_perm(cls, users_group_id, perm):
941 def grant_perm(cls, users_group_id, perm):
942 if not isinstance(perm, Permission):
942 if not isinstance(perm, Permission):
943 raise Exception('perm needs to be an instance of Permission class')
943 raise Exception('perm needs to be an instance of Permission class')
944
944
945 new = cls()
945 new = cls()
946 new.users_group_id = users_group_id
946 new.users_group_id = users_group_id
947 new.permission = perm
947 new.permission = perm
948 try:
948 try:
949 Session.add(new)
949 Session.add(new)
950 Session.commit()
950 Session.commit()
951 except:
951 except:
952 Session.rollback()
952 Session.rollback()
953
953
954
954
955 @classmethod
955 @classmethod
956 def revoke_perm(cls, users_group_id, perm):
956 def revoke_perm(cls, users_group_id, perm):
957 if not isinstance(perm, Permission):
957 if not isinstance(perm, Permission):
958 raise Exception('perm needs to be an instance of Permission class')
958 raise Exception('perm needs to be an instance of Permission class')
959
959
960 try:
960 try:
961 cls.query().filter(cls.users_group_id == users_group_id)\
961 cls.query().filter(cls.users_group_id == users_group_id)\
962 .filter(cls.permission == perm).delete()
962 .filter(cls.permission == perm).delete()
963 Session.commit()
963 Session.commit()
964 except:
964 except:
965 Session.rollback()
965 Session.rollback()
966
966
967
967
968 class UserRepoGroupToPerm(Base, BaseModel):
968 class UserRepoGroupToPerm(Base, BaseModel):
969 __tablename__ = 'group_to_perm'
969 __tablename__ = 'group_to_perm'
970 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
970 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
971
971
972 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
972 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
973 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
973 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
974 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
974 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
975 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
975 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
976
976
977 user = relationship('User')
977 user = relationship('User')
978 permission = relationship('Permission')
978 permission = relationship('Permission')
979 group = relationship('RepoGroup')
979 group = relationship('RepoGroup')
980
980
981 class UsersGroupRepoGroupToPerm(Base, BaseModel):
981 class UsersGroupRepoGroupToPerm(Base, BaseModel):
982 __tablename__ = 'users_group_repo_group_to_perm'
982 __tablename__ = 'users_group_repo_group_to_perm'
983 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
983 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
984
984
985 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
985 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
988 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
988 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
989
989
990 users_group = relationship('UsersGroup')
990 users_group = relationship('UsersGroup')
991 permission = relationship('Permission')
991 permission = relationship('Permission')
992 group = relationship('RepoGroup')
992 group = relationship('RepoGroup')
993
993
994 class Statistics(Base, BaseModel):
994 class Statistics(Base, BaseModel):
995 __tablename__ = 'statistics'
995 __tablename__ = 'statistics'
996 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
996 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
997 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
997 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
998 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
998 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
999 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
999 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1000 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1000 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1001 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1001 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1002 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1002 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1003
1003
1004 repository = relationship('Repository', single_parent=True)
1004 repository = relationship('Repository', single_parent=True)
1005
1005
1006 class UserFollowing(Base, BaseModel):
1006 class UserFollowing(Base, BaseModel):
1007 __tablename__ = 'user_followings'
1007 __tablename__ = 'user_followings'
1008 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1008 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1009 UniqueConstraint('user_id', 'follows_user_id')
1009 UniqueConstraint('user_id', 'follows_user_id')
1010 , {'extend_existing':True})
1010 , {'extend_existing':True})
1011
1011
1012 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1012 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1013 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1013 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1014 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1014 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1015 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1015 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1016 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1016 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1017
1017
1018 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1018 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1019
1019
1020 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1020 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1021 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1021 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1022
1022
1023
1023
1024 @classmethod
1024 @classmethod
1025 def get_repo_followers(cls, repo_id):
1025 def get_repo_followers(cls, repo_id):
1026 return cls.query().filter(cls.follows_repo_id == repo_id)
1026 return cls.query().filter(cls.follows_repo_id == repo_id)
1027
1027
1028 class CacheInvalidation(Base, BaseModel):
1028 class CacheInvalidation(Base, BaseModel):
1029 __tablename__ = 'cache_invalidation'
1029 __tablename__ = 'cache_invalidation'
1030 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1030 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1031 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1031 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1032 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1032 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1033 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1033 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1034 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1034 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1035
1035
1036
1036
1037 def __init__(self, cache_key, cache_args=''):
1037 def __init__(self, cache_key, cache_args=''):
1038 self.cache_key = cache_key
1038 self.cache_key = cache_key
1039 self.cache_args = cache_args
1039 self.cache_args = cache_args
1040 self.cache_active = False
1040 self.cache_active = False
1041
1041
1042 def __repr__(self):
1042 def __repr__(self):
1043 return "<%s('%s:%s')>" % (self.__class__.__name__,
1043 return "<%s('%s:%s')>" % (self.__class__.__name__,
1044 self.cache_id, self.cache_key)
1044 self.cache_id, self.cache_key)
1045
1045
1046 @classmethod
1046 @classmethod
1047 def invalidate(cls, key):
1047 def invalidate(cls, key):
1048 """
1048 """
1049 Returns Invalidation object if this given key should be invalidated
1049 Returns Invalidation object if this given key should be invalidated
1050 None otherwise. `cache_active = False` means that this cache
1050 None otherwise. `cache_active = False` means that this cache
1051 state is not valid and needs to be invalidated
1051 state is not valid and needs to be invalidated
1052
1052
1053 :param key:
1053 :param key:
1054 """
1054 """
1055 return cls.query()\
1055 return cls.query()\
1056 .filter(CacheInvalidation.cache_key == key)\
1056 .filter(CacheInvalidation.cache_key == key)\
1057 .filter(CacheInvalidation.cache_active == False)\
1057 .filter(CacheInvalidation.cache_active == False)\
1058 .scalar()
1058 .scalar()
1059
1059
1060 @classmethod
1060 @classmethod
1061 def set_invalidate(cls, key):
1061 def set_invalidate(cls, key):
1062 """
1062 """
1063 Mark this Cache key for invalidation
1063 Mark this Cache key for invalidation
1064
1064
1065 :param key:
1065 :param key:
1066 """
1066 """
1067
1067
1068 log.debug('marking %s for invalidation' % key)
1068 log.debug('marking %s for invalidation' % key)
1069 inv_obj = Session().query(cls)\
1069 inv_obj = Session().query(cls)\
1070 .filter(cls.cache_key == key).scalar()
1070 .filter(cls.cache_key == key).scalar()
1071 if inv_obj:
1071 if inv_obj:
1072 inv_obj.cache_active = False
1072 inv_obj.cache_active = False
1073 else:
1073 else:
1074 log.debug('cache key not found in invalidation db -> creating one')
1074 log.debug('cache key not found in invalidation db -> creating one')
1075 inv_obj = CacheInvalidation(key)
1075 inv_obj = CacheInvalidation(key)
1076
1076
1077 try:
1077 try:
1078 Session.add(inv_obj)
1078 Session.add(inv_obj)
1079 Session.commit()
1079 Session.commit()
1080 except Exception:
1080 except Exception:
1081 log.error(traceback.format_exc())
1081 log.error(traceback.format_exc())
1082 Session.rollback()
1082 Session.rollback()
1083
1083
1084 @classmethod
1084 @classmethod
1085 def set_valid(cls, key):
1085 def set_valid(cls, key):
1086 """
1086 """
1087 Mark this cache key as active and currently cached
1087 Mark this cache key as active and currently cached
1088
1088
1089 :param key:
1089 :param key:
1090 """
1090 """
1091 inv_obj = Session().query(CacheInvalidation)\
1091 inv_obj = Session().query(CacheInvalidation)\
1092 .filter(CacheInvalidation.cache_key == key).scalar()
1092 .filter(CacheInvalidation.cache_key == key).scalar()
1093 inv_obj.cache_active = True
1093 inv_obj.cache_active = True
1094 Session.add(inv_obj)
1094 Session.add(inv_obj)
1095 Session.commit()
1095 Session.commit()
1096
1096
1097
1097
1098 class ChangesetComment(Base, BaseModel):
1098 class ChangesetComment(Base, BaseModel):
1099 __tablename__ = 'changeset_comments'
1099 __tablename__ = 'changeset_comments'
1100 __table_args__ = ({'extend_existing':True},)
1100 __table_args__ = ({'extend_existing':True},)
1101 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1101 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1102 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1102 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1103 revision = Column('revision', String(40), nullable=False)
1103 revision = Column('revision', String(40), nullable=False)
1104 line_no = Column('line_no', Unicode(10), nullable=True)
1104 line_no = Column('line_no', Unicode(10), nullable=True)
1105 f_path = Column('f_path', Unicode(1000), nullable=True)
1105 f_path = Column('f_path', Unicode(1000), nullable=True)
1106 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1106 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1107 text = Column('text', Unicode(25000), nullable=False)
1107 text = Column('text', Unicode(25000), nullable=False)
1108 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1108 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1109
1109
1110 author = relationship('User')
1110 author = relationship('User')
1111 repo = relationship('Repository')
1111 repo = relationship('Repository')
1112
1112
1113
1113
1114 class DbMigrateVersion(Base, BaseModel):
1114 class DbMigrateVersion(Base, BaseModel):
1115 __tablename__ = 'db_migrate_version'
1115 __tablename__ = 'db_migrate_version'
1116 __table_args__ = {'extend_existing':True}
1116 __table_args__ = {'extend_existing':True}
1117 repository_id = Column('repository_id', String(250), primary_key=True)
1117 repository_id = Column('repository_id', String(250), primary_key=True)
1118 repository_path = Column('repository_path', Text)
1118 repository_path = Column('repository_path', Text)
1119 version = Column('version', Integer)
1119 version = Column('version', Integer)
1120
1120
@@ -1,480 +1,474 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 return User.get_by_api_key(api_key, cache)
74 user = self.sa.query(User)\
75 .filter(User.api_key == api_key)
76 if cache:
77 user = user.options(FromCache("sql_cache_short",
78 "get_user_%s" % api_key))
79 return user.scalar()
80
74
81 def create(self, form_data):
75 def create(self, form_data):
82 try:
76 try:
83 new_user = User()
77 new_user = User()
84 for k, v in form_data.items():
78 for k, v in form_data.items():
85 setattr(new_user, k, v)
79 setattr(new_user, k, v)
86
80
87 new_user.api_key = generate_api_key(form_data['username'])
81 new_user.api_key = generate_api_key(form_data['username'])
88 self.sa.add(new_user)
82 self.sa.add(new_user)
89 self.sa.commit()
83 self.sa.commit()
90 return new_user
84 return new_user
91 except:
85 except:
92 log.error(traceback.format_exc())
86 log.error(traceback.format_exc())
93 self.sa.rollback()
87 self.sa.rollback()
94 raise
88 raise
95
89
96
90
97 def create_or_update(self, username, password, email, name, lastname,
91 def create_or_update(self, username, password, email, name, lastname,
98 active=True, admin=False, ldap_dn=None):
92 active=True, admin=False, ldap_dn=None):
99 """
93 """
100 Creates a new instance if not found, or updates current one
94 Creates a new instance if not found, or updates current one
101
95
102 :param username:
96 :param username:
103 :param password:
97 :param password:
104 :param email:
98 :param email:
105 :param active:
99 :param active:
106 :param name:
100 :param name:
107 :param lastname:
101 :param lastname:
108 :param active:
102 :param active:
109 :param admin:
103 :param admin:
110 :param ldap_dn:
104 :param ldap_dn:
111 """
105 """
112
106
113 from rhodecode.lib.auth import get_crypt_password
107 from rhodecode.lib.auth import get_crypt_password
114
108
115 log.debug('Checking for %s account in RhodeCode database', username)
109 log.debug('Checking for %s account in RhodeCode database', username)
116 user = User.get_by_username(username, case_insensitive=True)
110 user = User.get_by_username(username, case_insensitive=True)
117 if user is None:
111 if user is None:
118 log.debug('creating new user %s', username)
112 log.debug('creating new user %s', username)
119 new_user = User()
113 new_user = User()
120 else:
114 else:
121 log.debug('updating user %s', username)
115 log.debug('updating user %s', username)
122 new_user = user
116 new_user = user
123
117
124 try:
118 try:
125 new_user.username = username
119 new_user.username = username
126 new_user.admin = admin
120 new_user.admin = admin
127 new_user.password = get_crypt_password(password)
121 new_user.password = get_crypt_password(password)
128 new_user.api_key = generate_api_key(username)
122 new_user.api_key = generate_api_key(username)
129 new_user.email = email
123 new_user.email = email
130 new_user.active = active
124 new_user.active = active
131 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
125 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
132 new_user.name = name
126 new_user.name = name
133 new_user.lastname = lastname
127 new_user.lastname = lastname
134
128
135 self.sa.add(new_user)
129 self.sa.add(new_user)
136 self.sa.commit()
130 self.sa.commit()
137 return new_user
131 return new_user
138 except (DatabaseError,):
132 except (DatabaseError,):
139 log.error(traceback.format_exc())
133 log.error(traceback.format_exc())
140 self.sa.rollback()
134 self.sa.rollback()
141 raise
135 raise
142
136
143
137
144 def create_for_container_auth(self, username, attrs):
138 def create_for_container_auth(self, username, attrs):
145 """
139 """
146 Creates the given user if it's not already in the database
140 Creates the given user if it's not already in the database
147
141
148 :param username:
142 :param username:
149 :param attrs:
143 :param attrs:
150 """
144 """
151 if self.get_by_username(username, case_insensitive=True) is None:
145 if self.get_by_username(username, case_insensitive=True) is None:
152
146
153 # autogenerate email for container account without one
147 # autogenerate email for container account without one
154 generate_email = lambda usr: '%s@container_auth.account' % usr
148 generate_email = lambda usr: '%s@container_auth.account' % usr
155
149
156 try:
150 try:
157 new_user = User()
151 new_user = User()
158 new_user.username = username
152 new_user.username = username
159 new_user.password = None
153 new_user.password = None
160 new_user.api_key = generate_api_key(username)
154 new_user.api_key = generate_api_key(username)
161 new_user.email = attrs['email']
155 new_user.email = attrs['email']
162 new_user.active = attrs.get('active', True)
156 new_user.active = attrs.get('active', True)
163 new_user.name = attrs['name'] or generate_email(username)
157 new_user.name = attrs['name'] or generate_email(username)
164 new_user.lastname = attrs['lastname']
158 new_user.lastname = attrs['lastname']
165
159
166 self.sa.add(new_user)
160 self.sa.add(new_user)
167 self.sa.commit()
161 self.sa.commit()
168 return new_user
162 return new_user
169 except (DatabaseError,):
163 except (DatabaseError,):
170 log.error(traceback.format_exc())
164 log.error(traceback.format_exc())
171 self.sa.rollback()
165 self.sa.rollback()
172 raise
166 raise
173 log.debug('User %s already exists. Skipping creation of account'
167 log.debug('User %s already exists. Skipping creation of account'
174 ' for container auth.', username)
168 ' for container auth.', username)
175 return None
169 return None
176
170
177 def create_ldap(self, username, password, user_dn, attrs):
171 def create_ldap(self, username, password, user_dn, attrs):
178 """
172 """
179 Checks if user is in database, if not creates this user marked
173 Checks if user is in database, if not creates this user marked
180 as ldap user
174 as ldap user
181
175
182 :param username:
176 :param username:
183 :param password:
177 :param password:
184 :param user_dn:
178 :param user_dn:
185 :param attrs:
179 :param attrs:
186 """
180 """
187 from rhodecode.lib.auth import get_crypt_password
181 from rhodecode.lib.auth import get_crypt_password
188 log.debug('Checking for such ldap account in RhodeCode database')
182 log.debug('Checking for such ldap account in RhodeCode database')
189 if self.get_by_username(username, case_insensitive=True) is None:
183 if self.get_by_username(username, case_insensitive=True) is None:
190
184
191 # autogenerate email for ldap account without one
185 # autogenerate email for ldap account without one
192 generate_email = lambda usr: '%s@ldap.account' % usr
186 generate_email = lambda usr: '%s@ldap.account' % usr
193
187
194 try:
188 try:
195 new_user = User()
189 new_user = User()
196 username = username.lower()
190 username = username.lower()
197 # add ldap account always lowercase
191 # add ldap account always lowercase
198 new_user.username = username
192 new_user.username = username
199 new_user.password = get_crypt_password(password)
193 new_user.password = get_crypt_password(password)
200 new_user.api_key = generate_api_key(username)
194 new_user.api_key = generate_api_key(username)
201 new_user.email = attrs['email'] or generate_email(username)
195 new_user.email = attrs['email'] or generate_email(username)
202 new_user.active = attrs.get('active', True)
196 new_user.active = attrs.get('active', True)
203 new_user.ldap_dn = safe_unicode(user_dn)
197 new_user.ldap_dn = safe_unicode(user_dn)
204 new_user.name = attrs['name']
198 new_user.name = attrs['name']
205 new_user.lastname = attrs['lastname']
199 new_user.lastname = attrs['lastname']
206
200
207 self.sa.add(new_user)
201 self.sa.add(new_user)
208 self.sa.commit()
202 self.sa.commit()
209 return new_user
203 return new_user
210 except (DatabaseError,):
204 except (DatabaseError,):
211 log.error(traceback.format_exc())
205 log.error(traceback.format_exc())
212 self.sa.rollback()
206 self.sa.rollback()
213 raise
207 raise
214 log.debug('this %s user exists skipping creation of ldap account',
208 log.debug('this %s user exists skipping creation of ldap account',
215 username)
209 username)
216 return None
210 return None
217
211
218 def create_registration(self, form_data):
212 def create_registration(self, form_data):
219 from rhodecode.lib.celerylib import tasks, run_task
213 from rhodecode.lib.celerylib import tasks, run_task
220 try:
214 try:
221 new_user = User()
215 new_user = User()
222 for k, v in form_data.items():
216 for k, v in form_data.items():
223 if k != 'admin':
217 if k != 'admin':
224 setattr(new_user, k, v)
218 setattr(new_user, k, v)
225
219
226 self.sa.add(new_user)
220 self.sa.add(new_user)
227 self.sa.commit()
221 self.sa.commit()
228 body = ('New user registration\n'
222 body = ('New user registration\n'
229 'username: %s\n'
223 'username: %s\n'
230 'email: %s\n')
224 'email: %s\n')
231 body = body % (form_data['username'], form_data['email'])
225 body = body % (form_data['username'], form_data['email'])
232
226
233 run_task(tasks.send_email, None,
227 run_task(tasks.send_email, None,
234 _('[RhodeCode] New User registration'),
228 _('[RhodeCode] New User registration'),
235 body)
229 body)
236 except:
230 except:
237 log.error(traceback.format_exc())
231 log.error(traceback.format_exc())
238 self.sa.rollback()
232 self.sa.rollback()
239 raise
233 raise
240
234
241 def update(self, user_id, form_data):
235 def update(self, user_id, form_data):
242 try:
236 try:
243 user = self.get(user_id, cache=False)
237 user = self.get(user_id, cache=False)
244 if user.username == 'default':
238 if user.username == 'default':
245 raise DefaultUserException(
239 raise DefaultUserException(
246 _("You can't Edit this user since it's"
240 _("You can't Edit this user since it's"
247 " crucial for entire application"))
241 " crucial for entire application"))
248
242
249 for k, v in form_data.items():
243 for k, v in form_data.items():
250 if k == 'new_password' and v != '':
244 if k == 'new_password' and v != '':
251 user.password = v
245 user.password = v
252 user.api_key = generate_api_key(user.username)
246 user.api_key = generate_api_key(user.username)
253 else:
247 else:
254 setattr(user, k, v)
248 setattr(user, k, v)
255
249
256 self.sa.add(user)
250 self.sa.add(user)
257 self.sa.commit()
251 self.sa.commit()
258 except:
252 except:
259 log.error(traceback.format_exc())
253 log.error(traceback.format_exc())
260 self.sa.rollback()
254 self.sa.rollback()
261 raise
255 raise
262
256
263 def update_my_account(self, user_id, form_data):
257 def update_my_account(self, user_id, form_data):
264 try:
258 try:
265 user = self.get(user_id, cache=False)
259 user = self.get(user_id, cache=False)
266 if user.username == 'default':
260 if user.username == 'default':
267 raise DefaultUserException(
261 raise DefaultUserException(
268 _("You can't Edit this user since it's"
262 _("You can't Edit this user since it's"
269 " crucial for entire application"))
263 " crucial for entire application"))
270 for k, v in form_data.items():
264 for k, v in form_data.items():
271 if k == 'new_password' and v != '':
265 if k == 'new_password' and v != '':
272 user.password = v
266 user.password = v
273 user.api_key = generate_api_key(user.username)
267 user.api_key = generate_api_key(user.username)
274 else:
268 else:
275 if k not in ['admin', 'active']:
269 if k not in ['admin', 'active']:
276 setattr(user, k, v)
270 setattr(user, k, v)
277
271
278 self.sa.add(user)
272 self.sa.add(user)
279 self.sa.commit()
273 self.sa.commit()
280 except:
274 except:
281 log.error(traceback.format_exc())
275 log.error(traceback.format_exc())
282 self.sa.rollback()
276 self.sa.rollback()
283 raise
277 raise
284
278
285 def delete(self, user_id):
279 def delete(self, user_id):
286 try:
280 try:
287 user = self.get(user_id, cache=False)
281 user = self.get(user_id, cache=False)
288 if user.username == 'default':
282 if user.username == 'default':
289 raise DefaultUserException(
283 raise DefaultUserException(
290 _("You can't remove this user since it's"
284 _("You can't remove this user since it's"
291 " crucial for entire application"))
285 " crucial for entire application"))
292 if user.repositories:
286 if user.repositories:
293 raise UserOwnsReposException(_('This user still owns %s '
287 raise UserOwnsReposException(_('This user still owns %s '
294 'repositories and cannot be '
288 'repositories and cannot be '
295 'removed. Switch owners or '
289 'removed. Switch owners or '
296 'remove those repositories') \
290 'remove those repositories') \
297 % user.repositories)
291 % user.repositories)
298 self.sa.delete(user)
292 self.sa.delete(user)
299 self.sa.commit()
293 self.sa.commit()
300 except:
294 except:
301 log.error(traceback.format_exc())
295 log.error(traceback.format_exc())
302 self.sa.rollback()
296 self.sa.rollback()
303 raise
297 raise
304
298
305 def reset_password_link(self, data):
299 def reset_password_link(self, data):
306 from rhodecode.lib.celerylib import tasks, run_task
300 from rhodecode.lib.celerylib import tasks, run_task
307 run_task(tasks.send_password_link, data['email'])
301 run_task(tasks.send_password_link, data['email'])
308
302
309 def reset_password(self, data):
303 def reset_password(self, data):
310 from rhodecode.lib.celerylib import tasks, run_task
304 from rhodecode.lib.celerylib import tasks, run_task
311 run_task(tasks.reset_user_password, data['email'])
305 run_task(tasks.reset_user_password, data['email'])
312
306
313 def fill_data(self, auth_user, user_id=None, api_key=None):
307 def fill_data(self, auth_user, user_id=None, api_key=None):
314 """
308 """
315 Fetches auth_user by user_id,or api_key if present.
309 Fetches auth_user by user_id,or api_key if present.
316 Fills auth_user attributes with those taken from database.
310 Fills auth_user attributes with those taken from database.
317 Additionally set's is_authenitated if lookup fails
311 Additionally set's is_authenitated if lookup fails
318 present in database
312 present in database
319
313
320 :param auth_user: instance of user to set attributes
314 :param auth_user: instance of user to set attributes
321 :param user_id: user id to fetch by
315 :param user_id: user id to fetch by
322 :param api_key: api key to fetch by
316 :param api_key: api key to fetch by
323 """
317 """
324 if user_id is None and api_key is None:
318 if user_id is None and api_key is None:
325 raise Exception('You need to pass user_id or api_key')
319 raise Exception('You need to pass user_id or api_key')
326
320
327 try:
321 try:
328 if api_key:
322 if api_key:
329 dbuser = self.get_by_api_key(api_key)
323 dbuser = self.get_by_api_key(api_key)
330 else:
324 else:
331 dbuser = self.get(user_id)
325 dbuser = self.get(user_id)
332
326
333 if dbuser is not None and dbuser.active:
327 if dbuser is not None and dbuser.active:
334 log.debug('filling %s data', dbuser)
328 log.debug('filling %s data', dbuser)
335 for k, v in dbuser.get_dict().items():
329 for k, v in dbuser.get_dict().items():
336 setattr(auth_user, k, v)
330 setattr(auth_user, k, v)
337 else:
331 else:
338 return False
332 return False
339
333
340 except:
334 except:
341 log.error(traceback.format_exc())
335 log.error(traceback.format_exc())
342 auth_user.is_authenticated = False
336 auth_user.is_authenticated = False
343 return False
337 return False
344
338
345 return True
339 return True
346
340
347 def fill_perms(self, user):
341 def fill_perms(self, user):
348 """
342 """
349 Fills user permission attribute with permissions taken from database
343 Fills user permission attribute with permissions taken from database
350 works for permissions given for repositories, and for permissions that
344 works for permissions given for repositories, and for permissions that
351 are granted to groups
345 are granted to groups
352
346
353 :param user: user instance to fill his perms
347 :param user: user instance to fill his perms
354 """
348 """
355
349
356 user.permissions['repositories'] = {}
350 user.permissions['repositories'] = {}
357 user.permissions['global'] = set()
351 user.permissions['global'] = set()
358
352
359 #======================================================================
353 #======================================================================
360 # fetch default permissions
354 # fetch default permissions
361 #======================================================================
355 #======================================================================
362 default_user = self.get_by_username('default', cache=True)
356 default_user = self.get_by_username('default', cache=True)
363
357
364 default_perms = self.sa.query(UserRepoToPerm, Repository, Permission)\
358 default_perms = self.sa.query(UserRepoToPerm, Repository, Permission)\
365 .join((Repository, UserRepoToPerm.repository_id ==
359 .join((Repository, UserRepoToPerm.repository_id ==
366 Repository.repo_id))\
360 Repository.repo_id))\
367 .join((Permission, UserRepoToPerm.permission_id ==
361 .join((Permission, UserRepoToPerm.permission_id ==
368 Permission.permission_id))\
362 Permission.permission_id))\
369 .filter(UserRepoToPerm.user == default_user).all()
363 .filter(UserRepoToPerm.user == default_user).all()
370
364
371 if user.is_admin:
365 if user.is_admin:
372 #==================================================================
366 #==================================================================
373 # #admin have all default rights set to admin
367 # #admin have all default rights set to admin
374 #==================================================================
368 #==================================================================
375 user.permissions['global'].add('hg.admin')
369 user.permissions['global'].add('hg.admin')
376
370
377 for perm in default_perms:
371 for perm in default_perms:
378 p = 'repository.admin'
372 p = 'repository.admin'
379 user.permissions['repositories'][perm.UserRepoToPerm.
373 user.permissions['repositories'][perm.UserRepoToPerm.
380 repository.repo_name] = p
374 repository.repo_name] = p
381
375
382 else:
376 else:
383 #==================================================================
377 #==================================================================
384 # set default permissions
378 # set default permissions
385 #==================================================================
379 #==================================================================
386 uid = user.user_id
380 uid = user.user_id
387
381
388 #default global
382 #default global
389 default_global_perms = self.sa.query(UserToPerm)\
383 default_global_perms = self.sa.query(UserToPerm)\
390 .filter(UserToPerm.user == default_user)
384 .filter(UserToPerm.user == default_user)
391
385
392 for perm in default_global_perms:
386 for perm in default_global_perms:
393 user.permissions['global'].add(perm.permission.permission_name)
387 user.permissions['global'].add(perm.permission.permission_name)
394
388
395 #default for repositories
389 #default for repositories
396 for perm in default_perms:
390 for perm in default_perms:
397 if perm.Repository.private and not (perm.Repository.user_id ==
391 if perm.Repository.private and not (perm.Repository.user_id ==
398 uid):
392 uid):
399 #diself.sable defaults for private repos,
393 #diself.sable defaults for private repos,
400 p = 'repository.none'
394 p = 'repository.none'
401 elif perm.Repository.user_id == uid:
395 elif perm.Repository.user_id == uid:
402 #set admin if owner
396 #set admin if owner
403 p = 'repository.admin'
397 p = 'repository.admin'
404 else:
398 else:
405 p = perm.Permission.permission_name
399 p = perm.Permission.permission_name
406
400
407 user.permissions['repositories'][perm.UserRepoToPerm.
401 user.permissions['repositories'][perm.UserRepoToPerm.
408 repository.repo_name] = p
402 repository.repo_name] = p
409
403
410 #==================================================================
404 #==================================================================
411 # overwrite default with user permissions if any
405 # overwrite default with user permissions if any
412 #==================================================================
406 #==================================================================
413
407
414 #user global
408 #user global
415 user_perms = self.sa.query(UserToPerm)\
409 user_perms = self.sa.query(UserToPerm)\
416 .options(joinedload(UserToPerm.permission))\
410 .options(joinedload(UserToPerm.permission))\
417 .filter(UserToPerm.user_id == uid).all()
411 .filter(UserToPerm.user_id == uid).all()
418
412
419 for perm in user_perms:
413 for perm in user_perms:
420 user.permissions['global'].add(perm.permission.
414 user.permissions['global'].add(perm.permission.
421 permission_name)
415 permission_name)
422
416
423 #user repositories
417 #user repositories
424 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
418 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
425 Repository)\
419 Repository)\
426 .join((Repository, UserRepoToPerm.repository_id ==
420 .join((Repository, UserRepoToPerm.repository_id ==
427 Repository.repo_id))\
421 Repository.repo_id))\
428 .join((Permission, UserRepoToPerm.permission_id ==
422 .join((Permission, UserRepoToPerm.permission_id ==
429 Permission.permission_id))\
423 Permission.permission_id))\
430 .filter(UserRepoToPerm.user_id == uid).all()
424 .filter(UserRepoToPerm.user_id == uid).all()
431
425
432 for perm in user_repo_perms:
426 for perm in user_repo_perms:
433 # set admin if owner
427 # set admin if owner
434 if perm.Repository.user_id == uid:
428 if perm.Repository.user_id == uid:
435 p = 'repository.admin'
429 p = 'repository.admin'
436 else:
430 else:
437 p = perm.Permission.permission_name
431 p = perm.Permission.permission_name
438 user.permissions['repositories'][perm.UserRepoToPerm.
432 user.permissions['repositories'][perm.UserRepoToPerm.
439 repository.repo_name] = p
433 repository.repo_name] = p
440
434
441 #==================================================================
435 #==================================================================
442 # check if user is part of groups for this repository and fill in
436 # check if user is part of groups for this repository and fill in
443 # (or replace with higher) permissions
437 # (or replace with higher) permissions
444 #==================================================================
438 #==================================================================
445
439
446 #users group global
440 #users group global
447 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
441 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
448 .options(joinedload(UsersGroupToPerm.permission))\
442 .options(joinedload(UsersGroupToPerm.permission))\
449 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
443 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
450 UsersGroupMember.users_group_id))\
444 UsersGroupMember.users_group_id))\
451 .filter(UsersGroupMember.user_id == uid).all()
445 .filter(UsersGroupMember.user_id == uid).all()
452
446
453 for perm in user_perms_from_users_groups:
447 for perm in user_perms_from_users_groups:
454 user.permissions['global'].add(perm.permission.permission_name)
448 user.permissions['global'].add(perm.permission.permission_name)
455
449
456 #users group repositories
450 #users group repositories
457 user_repo_perms_from_users_groups = self.sa.query(
451 user_repo_perms_from_users_groups = self.sa.query(
458 UsersGroupRepoToPerm,
452 UsersGroupRepoToPerm,
459 Permission, Repository,)\
453 Permission, Repository,)\
460 .join((Repository, UsersGroupRepoToPerm.repository_id ==
454 .join((Repository, UsersGroupRepoToPerm.repository_id ==
461 Repository.repo_id))\
455 Repository.repo_id))\
462 .join((Permission, UsersGroupRepoToPerm.permission_id ==
456 .join((Permission, UsersGroupRepoToPerm.permission_id ==
463 Permission.permission_id))\
457 Permission.permission_id))\
464 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
458 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
465 UsersGroupMember.users_group_id))\
459 UsersGroupMember.users_group_id))\
466 .filter(UsersGroupMember.user_id == uid).all()
460 .filter(UsersGroupMember.user_id == uid).all()
467
461
468 for perm in user_repo_perms_from_users_groups:
462 for perm in user_repo_perms_from_users_groups:
469 p = perm.Permission.permission_name
463 p = perm.Permission.permission_name
470 cur_perm = user.permissions['repositories'][perm.
464 cur_perm = user.permissions['repositories'][perm.
471 UsersGroupRepoToPerm.
465 UsersGroupRepoToPerm.
472 repository.repo_name]
466 repository.repo_name]
473 #overwrite permission only if it's greater than permission
467 #overwrite permission only if it's greater than permission
474 # given from other sources
468 # given from other sources
475 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
469 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
476 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
470 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
477 repository.repo_name] = p
471 repository.repo_name] = p
478
472
479 return user
473 return user
480
474
@@ -1,261 +1,260 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3 from rhodecode.model.db import User
3 from rhodecode.model.db import User
4 from rhodecode.lib import generate_api_key
4 from rhodecode.lib import generate_api_key
5 from rhodecode.lib.auth import check_password
5 from rhodecode.lib.auth import check_password
6
6
7
7
8 class TestLoginController(TestController):
8 class TestLoginController(TestController):
9
9
10 def test_index(self):
10 def test_index(self):
11 response = self.app.get(url(controller='login', action='index'))
11 response = self.app.get(url(controller='login', action='index'))
12 self.assertEqual(response.status, '200 OK')
12 self.assertEqual(response.status, '200 OK')
13 # Test response...
13 # Test response...
14
14
15 def test_login_admin_ok(self):
15 def test_login_admin_ok(self):
16 response = self.app.post(url(controller='login', action='index'),
16 response = self.app.post(url(controller='login', action='index'),
17 {'username':'test_admin',
17 {'username':'test_admin',
18 'password':'test12'})
18 'password':'test12'})
19 self.assertEqual(response.status, '302 Found')
19 self.assertEqual(response.status, '302 Found')
20 self.assertEqual(response.session['rhodecode_user'].username ,
20 self.assertEqual(response.session['rhodecode_user'].username ,
21 'test_admin')
21 'test_admin')
22 response = response.follow()
22 response = response.follow()
23 self.assertTrue('%s repository' % HG_REPO in response.body)
23 self.assertTrue('%s repository' % HG_REPO in response.body)
24
24
25 def test_login_regular_ok(self):
25 def test_login_regular_ok(self):
26 response = self.app.post(url(controller='login', action='index'),
26 response = self.app.post(url(controller='login', action='index'),
27 {'username':'test_regular',
27 {'username':'test_regular',
28 'password':'test12'})
28 'password':'test12'})
29
29
30 self.assertEqual(response.status, '302 Found')
30 self.assertEqual(response.status, '302 Found')
31 self.assertEqual(response.session['rhodecode_user'].username ,
31 self.assertEqual(response.session['rhodecode_user'].username ,
32 'test_regular')
32 'test_regular')
33 response = response.follow()
33 response = response.follow()
34 self.assertTrue('%s repository' % HG_REPO in response.body)
34 self.assertTrue('%s repository' % HG_REPO in response.body)
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
36
36
37 def test_login_ok_came_from(self):
37 def test_login_ok_came_from(self):
38 test_came_from = '/_admin/users'
38 test_came_from = '/_admin/users'
39 response = self.app.post(url(controller='login', action='index',
39 response = self.app.post(url(controller='login', action='index',
40 came_from=test_came_from),
40 came_from=test_came_from),
41 {'username':'test_admin',
41 {'username':'test_admin',
42 'password':'test12'})
42 'password':'test12'})
43 self.assertEqual(response.status, '302 Found')
43 self.assertEqual(response.status, '302 Found')
44 response = response.follow()
44 response = response.follow()
45
45
46 self.assertEqual(response.status, '200 OK')
46 self.assertEqual(response.status, '200 OK')
47 self.assertTrue('Users administration' in response.body)
47 self.assertTrue('Users administration' in response.body)
48
48
49
49
50 def test_login_short_password(self):
50 def test_login_short_password(self):
51 response = self.app.post(url(controller='login', action='index'),
51 response = self.app.post(url(controller='login', action='index'),
52 {'username':'test_admin',
52 {'username':'test_admin',
53 'password':'as'})
53 'password':'as'})
54 self.assertEqual(response.status, '200 OK')
54 self.assertEqual(response.status, '200 OK')
55
55
56 self.assertTrue('Enter 3 characters or more' in response.body)
56 self.assertTrue('Enter 3 characters or more' in response.body)
57
57
58 def test_login_wrong_username_password(self):
58 def test_login_wrong_username_password(self):
59 response = self.app.post(url(controller='login', action='index'),
59 response = self.app.post(url(controller='login', action='index'),
60 {'username':'error',
60 {'username':'error',
61 'password':'test12'})
61 'password':'test12'})
62 self.assertEqual(response.status , '200 OK')
62 self.assertEqual(response.status , '200 OK')
63
63
64 self.assertTrue('invalid user name' in response.body)
64 self.assertTrue('invalid user name' in response.body)
65 self.assertTrue('invalid password' in response.body)
65 self.assertTrue('invalid password' in response.body)
66
66
67 #==========================================================================
67 #==========================================================================
68 # REGISTRATIONS
68 # REGISTRATIONS
69 #==========================================================================
69 #==========================================================================
70 def test_register(self):
70 def test_register(self):
71 response = self.app.get(url(controller='login', action='register'))
71 response = self.app.get(url(controller='login', action='register'))
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
73
73
74 def test_register_err_same_username(self):
74 def test_register_err_same_username(self):
75 response = self.app.post(url(controller='login', action='register'),
75 response = self.app.post(url(controller='login', action='register'),
76 {'username':'test_admin',
76 {'username':'test_admin',
77 'password':'test12',
77 'password':'test12',
78 'password_confirmation':'test12',
78 'password_confirmation':'test12',
79 'email':'goodmail@domain.com',
79 'email':'goodmail@domain.com',
80 'name':'test',
80 'name':'test',
81 'lastname':'test'})
81 'lastname':'test'})
82
82
83 self.assertEqual(response.status , '200 OK')
83 self.assertEqual(response.status , '200 OK')
84 self.assertTrue('This username already exists' in response.body)
84 self.assertTrue('This username already exists' in response.body)
85
85
86 def test_register_err_same_email(self):
86 def test_register_err_same_email(self):
87 response = self.app.post(url(controller='login', action='register'),
87 response = self.app.post(url(controller='login', action='register'),
88 {'username':'test_admin_0',
88 {'username':'test_admin_0',
89 'password':'test12',
89 'password':'test12',
90 'password_confirmation':'test12',
90 'password_confirmation':'test12',
91 'email':'test_admin@mail.com',
91 'email':'test_admin@mail.com',
92 'name':'test',
92 'name':'test',
93 'lastname':'test'})
93 'lastname':'test'})
94
94
95 self.assertEqual(response.status , '200 OK')
95 self.assertEqual(response.status , '200 OK')
96 assert 'This e-mail address is already taken' in response.body
96 assert 'This e-mail address is already taken' in response.body
97
97
98 def test_register_err_same_email_case_sensitive(self):
98 def test_register_err_same_email_case_sensitive(self):
99 response = self.app.post(url(controller='login', action='register'),
99 response = self.app.post(url(controller='login', action='register'),
100 {'username':'test_admin_1',
100 {'username':'test_admin_1',
101 'password':'test12',
101 'password':'test12',
102 'password_confirmation':'test12',
102 'password_confirmation':'test12',
103 'email':'TesT_Admin@mail.COM',
103 'email':'TesT_Admin@mail.COM',
104 'name':'test',
104 'name':'test',
105 'lastname':'test'})
105 'lastname':'test'})
106 self.assertEqual(response.status , '200 OK')
106 self.assertEqual(response.status , '200 OK')
107 assert 'This e-mail address is already taken' in response.body
107 assert 'This e-mail address is already taken' in response.body
108
108
109 def test_register_err_wrong_data(self):
109 def test_register_err_wrong_data(self):
110 response = self.app.post(url(controller='login', action='register'),
110 response = self.app.post(url(controller='login', action='register'),
111 {'username':'xs',
111 {'username':'xs',
112 'password':'test',
112 'password':'test',
113 'password_confirmation':'test',
113 'password_confirmation':'test',
114 'email':'goodmailm',
114 'email':'goodmailm',
115 'name':'test',
115 'name':'test',
116 'lastname':'test'})
116 'lastname':'test'})
117 self.assertEqual(response.status , '200 OK')
117 self.assertEqual(response.status , '200 OK')
118 assert 'An email address must contain a single @' in response.body
118 assert 'An email address must contain a single @' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
120
120
121
121
122 def test_register_err_username(self):
122 def test_register_err_username(self):
123 response = self.app.post(url(controller='login', action='register'),
123 response = self.app.post(url(controller='login', action='register'),
124 {'username':'error user',
124 {'username':'error user',
125 'password':'test12',
125 'password':'test12',
126 'password_confirmation':'test12',
126 'password_confirmation':'test12',
127 'email':'goodmailm',
127 'email':'goodmailm',
128 'name':'test',
128 'name':'test',
129 'lastname':'test'})
129 'lastname':'test'})
130
130
131 self.assertEqual(response.status , '200 OK')
131 self.assertEqual(response.status , '200 OK')
132 assert 'An email address must contain a single @' in response.body
132 assert 'An email address must contain a single @' in response.body
133 assert ('Username may only contain '
133 assert ('Username may only contain '
134 'alphanumeric characters underscores, '
134 'alphanumeric characters underscores, '
135 'periods or dashes and must begin with '
135 'periods or dashes and must begin with '
136 'alphanumeric character') in response.body
136 'alphanumeric character') in response.body
137
137
138 def test_register_err_case_sensitive(self):
138 def test_register_err_case_sensitive(self):
139 response = self.app.post(url(controller='login', action='register'),
139 response = self.app.post(url(controller='login', action='register'),
140 {'username':'Test_Admin',
140 {'username':'Test_Admin',
141 'password':'test12',
141 'password':'test12',
142 'password_confirmation':'test12',
142 'password_confirmation':'test12',
143 'email':'goodmailm',
143 'email':'goodmailm',
144 'name':'test',
144 'name':'test',
145 'lastname':'test'})
145 'lastname':'test'})
146
146
147 self.assertEqual(response.status , '200 OK')
147 self.assertEqual(response.status , '200 OK')
148 self.assertTrue('An email address must contain a single @' in response.body)
148 self.assertTrue('An email address must contain a single @' in response.body)
149 self.assertTrue('This username already exists' in response.body)
149 self.assertTrue('This username already exists' in response.body)
150
150
151
151
152
152
153 def test_register_special_chars(self):
153 def test_register_special_chars(self):
154 response = self.app.post(url(controller='login', action='register'),
154 response = self.app.post(url(controller='login', action='register'),
155 {'username':'xxxaxn',
155 {'username':'xxxaxn',
156 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
156 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
157 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
157 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
158 'email':'goodmailm@test.plx',
158 'email':'goodmailm@test.plx',
159 'name':'test',
159 'name':'test',
160 'lastname':'test'})
160 'lastname':'test'})
161
161
162 self.assertEqual(response.status , '200 OK')
162 self.assertEqual(response.status , '200 OK')
163 self.assertTrue('Invalid characters in password' in response.body)
163 self.assertTrue('Invalid characters in password' in response.body)
164
164
165
165
166 def test_register_password_mismatch(self):
166 def test_register_password_mismatch(self):
167 response = self.app.post(url(controller='login', action='register'),
167 response = self.app.post(url(controller='login', action='register'),
168 {'username':'xs',
168 {'username':'xs',
169 'password':'123qwe',
169 'password':'123qwe',
170 'password_confirmation':'qwe123',
170 'password_confirmation':'qwe123',
171 'email':'goodmailm@test.plxa',
171 'email':'goodmailm@test.plxa',
172 'name':'test',
172 'name':'test',
173 'lastname':'test'})
173 'lastname':'test'})
174
174
175 self.assertEqual(response.status , '200 OK')
175 self.assertEqual(response.status , '200 OK')
176 assert 'Passwords do not match' in response.body
176 assert 'Passwords do not match' in response.body
177
177
178 def test_register_ok(self):
178 def test_register_ok(self):
179 username = 'test_regular4'
179 username = 'test_regular4'
180 password = 'qweqwe'
180 password = 'qweqwe'
181 email = 'marcin@test.com'
181 email = 'marcin@test.com'
182 name = 'testname'
182 name = 'testname'
183 lastname = 'testlastname'
183 lastname = 'testlastname'
184
184
185 response = self.app.post(url(controller='login', action='register'),
185 response = self.app.post(url(controller='login', action='register'),
186 {'username':username,
186 {'username':username,
187 'password':password,
187 'password':password,
188 'password_confirmation':password,
188 'password_confirmation':password,
189 'email':email,
189 'email':email,
190 'name':name,
190 'name':name,
191 'lastname':lastname})
191 'lastname':lastname})
192 self.assertEqual(response.status , '302 Found')
192 self.assertEqual(response.status , '302 Found')
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
194
194
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
196 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
196 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
197 assert check_password(password, ret.password) == True , 'password mismatch'
197 assert check_password(password, ret.password) == True , 'password mismatch'
198 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
198 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
199 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
199 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
200 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
200 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
201
201
202
202
203 def test_forgot_password_wrong_mail(self):
203 def test_forgot_password_wrong_mail(self):
204 response = self.app.post(url(controller='login', action='password_reset'),
204 response = self.app.post(url(controller='login', action='password_reset'),
205 {'email':'marcin@wrongmail.org', })
205 {'email':'marcin@wrongmail.org', })
206
206
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
208
208
209 def test_forgot_password(self):
209 def test_forgot_password(self):
210 response = self.app.get(url(controller='login',
210 response = self.app.get(url(controller='login',
211 action='password_reset'))
211 action='password_reset'))
212 self.assertEqual(response.status , '200 OK')
212 self.assertEqual(response.status , '200 OK')
213
213
214 username = 'test_password_reset_1'
214 username = 'test_password_reset_1'
215 password = 'qweqwe'
215 password = 'qweqwe'
216 email = 'marcin@python-works.com'
216 email = 'marcin@python-works.com'
217 name = 'passwd'
217 name = 'passwd'
218 lastname = 'reset'
218 lastname = 'reset'
219
219
220 new = User()
220 new = User()
221 new.username = username
221 new.username = username
222 new.password = password
222 new.password = password
223 new.email = email
223 new.email = email
224 new.name = name
224 new.name = name
225 new.lastname = lastname
225 new.lastname = lastname
226 new.api_key = generate_api_key(username)
226 new.api_key = generate_api_key(username)
227 self.sa.add(new)
227 self.sa.add(new)
228 self.sa.commit()
228 self.sa.commit()
229
229
230 response = self.app.post(url(controller='login',
230 response = self.app.post(url(controller='login',
231 action='password_reset'),
231 action='password_reset'),
232 {'email':email, })
232 {'email':email, })
233
233
234 self.checkSessionFlash(response, 'Your password reset link was sent')
234 self.checkSessionFlash(response, 'Your password reset link was sent')
235
235
236 response = response.follow()
236 response = response.follow()
237
237
238 # BAD KEY
238 # BAD KEY
239
239
240 key = "bad"
240 key = "bad"
241 response = self.app.get(url(controller='login',
241 response = self.app.get(url(controller='login',
242 action='password_reset_confirmation',
242 action='password_reset_confirmation',
243 key=key))
243 key=key))
244 self.assertEqual(response.status, '302 Found')
244 self.assertEqual(response.status, '302 Found')
245 self.assertTrue(response.location.endswith(url('reset_password')))
245 self.assertTrue(response.location.endswith(url('reset_password')))
246
246
247 # GOOD KEY
247 # GOOD KEY
248
248
249 key = User.get_by_username(username).api_key
249 key = User.get_by_username(username).api_key
250
251 response = self.app.get(url(controller='login',
250 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
251 action='password_reset_confirmation',
253 key=key))
252 key=key))
254 self.assertEqual(response.status, '302 Found')
253 self.assertEqual(response.status, '302 Found')
255 self.assertTrue(response.location.endswith(url('login_home')))
254 self.assertTrue(response.location.endswith(url('login_home')))
256
255
257 self.checkSessionFlash(response,
256 self.checkSessionFlash(response,
258 ('Your password reset was successful, '
257 ('Your password reset was successful, '
259 'new password has been sent to your email'))
258 'new password has been sent to your email'))
260
259
261 response = response.follow()
260 response = response.follow()
General Comments 0
You need to be logged in to leave comments. Login now