##// END OF EJS Templates
Extended API...
marcink -
r1500:256e729a beta
parent child Browse files
Show More
@@ -1,57 +1,111 b''
1 .. _api:
1 .. _api:
2
2
3
3
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's one schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request in RhodeCode is
10 with JSON protocol both ways. An url to send API request in RhodeCode is
11 <your-server>/_admin/api
11 <your_server>/_admin/api
12
12
13
13
14 Clients need to send JSON data in such format::
14 All clients need to send JSON data in such format::
15
15
16 {
16 {
17 "api_key":"<api_key>",
17 "api_key":"<api_key>",
18 "method":"<method_name>",
18 "method":"<method_name>",
19 "args":{"<arg_key>":"<arg_val>"}
19 "args":{"<arg_key>":"<arg_val>"}
20 }
20 }
21
21
22 Simply provide api_key for access and permission validation
22 Example call for autopulling remotes repos using curl::
23 method is name of method to call
23 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
24 and args is an key:value list of arguments to pass to method
24
25 Simply provide
26 - *api_key* for access and permission validation.
27 - *method* is name of method to call
28 - *args* is an key:value list of arguments to pass to method
25
29
26 .. note::
30 .. note::
27
31
28 api_key can be found in your user account page
32 api_key can be found in your user account page
29
33
30
34
31 And will receive JSON formatted answer::
35 RhodeCode API will return always a JSON formatted answer::
32
36
33 {
37 {
34 "result": "<result>",
38 "result": "<result>",
35 "error": null
39 "error": null
36 }
40 }
37
41
38 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
42 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
39 calling api **error** key from response will contain failure description
43 calling api *error* key from response will contain failure description
40 and result will be null.
44 and result will be null.
41
45
42 API METHODS
46 API METHODS
43 +++++++++++
47 +++++++++++
44
48
45
49
46 pull
50 pull
47 ----
51 ----
48
52
49 Pulls given repo from remote location. Can be used to automatically keep
53 Pulls given repo from remote location. Can be used to automatically keep
50 remote repos upto date. This command can be executed only using admin users
54 remote repos up to date. This command can be executed only using api_key
51 api_key
55 belonging to user with admin rights
52
56
53 ::
57 INPUT::
58
54 api_key:"<api_key>"
59 api_key:"<api_key>"
55 method: "pull"
60 method: "pull"
56 args: {"repo":<repo_name>}
61 args: {"repo":<repo_name>}
57
62
63 OUTPUT::
64
65 result:"Pulled from <repo_name>"
66 error:null
67
68
69 create_user
70 -----------
71
72 Creates new user in RhodeCode. This command can be executed only using api_key
73 belonging to user with admin rights
74
75 INPUT::
76
77 api_key:"<api_key>"
78 method: "create_user"
79 args: {"username": "<username>",
80 "password": "<password>",
81 "active": "<bool>",
82 "admin": "<bool>",
83 "name": "<firstname>",
84 "lastname": "<lastname>",
85 "email": "<useremail>"}
86
87 OUTPUT::
88
89 result:{"id": <newuserid>,
90 "msg":"created new user <username>"}
91 error:null
92
93
94 create_users_group
95 ------------------
96
97 creates new users group. This command can be executed only using api_key
98 belonging to user with admin rights
99
100 INPUT::
101
102 api_key:"<api_key>"
103 method: "create_user"
104 args: {"name": "<groupname>",
105 "active":"<bool>"}
106
107 OUTPUT::
108
109 result:{"id": <newusersgroupid>,
110 "msg":"created new users group <groupname>"}
111 error:null
@@ -1,225 +1,229 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 json
29 import json
30 import logging
30 import logging
31 import types
31 import types
32 import urllib
32 import urllib
33
33
34 from paste.response import replace_header
34 from paste.response import replace_header
35
35
36 from pylons.controllers import WSGIController
36 from pylons.controllers import WSGIController
37 from pylons.controllers.util import Response
37 from pylons.controllers.util import Response
38
38
39 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
39 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
40 HTTPBadRequest, HTTPError
40 HTTPBadRequest, HTTPError
41
41
42 from rhodecode.model.db import User
42 from rhodecode.model.db import User
43 from rhodecode.lib.auth import AuthUser
43 from rhodecode.lib.auth import AuthUser
44
44
45 log = logging.getLogger('JSONRPC')
45 log = logging.getLogger('JSONRPC')
46
46
47 class JSONRPCError(BaseException):
47 class JSONRPCError(BaseException):
48
48
49 def __init__(self, message):
49 def __init__(self, message):
50 self.message = message
50 self.message = message
51
51
52 def __str__(self):
52 def __str__(self):
53 return str(self.message)
53 return str(self.message)
54
54
55
55
56 def jsonrpc_error(message, code=None):
56 def jsonrpc_error(message, code=None):
57 """Generate a Response object with a JSON-RPC error body"""
57 """Generate a Response object with a JSON-RPC error body"""
58 return Response(body=json.dumps(dict(result=None,
58 return Response(body=json.dumps(dict(result=None,
59 error=message)))
59 error=message)))
60
60
61
61
62 class JSONRPCController(WSGIController):
62 class JSONRPCController(WSGIController):
63 """
63 """
64 A WSGI-speaking JSON-RPC controller class
64 A WSGI-speaking JSON-RPC controller class
65
65
66 See the specification:
66 See the specification:
67 <http://json-rpc.org/wiki/specification>`.
67 <http://json-rpc.org/wiki/specification>`.
68
68
69 Valid controller return values should be json-serializable objects.
69 Valid controller return values should be json-serializable objects.
70
70
71 Sub-classes should catch their exceptions and raise JSONRPCError
71 Sub-classes should catch their exceptions and raise JSONRPCError
72 if they want to pass meaningful errors to the client.
72 if they want to pass meaningful errors to the client.
73
73
74 """
74 """
75
75
76 def _get_method_args(self):
76 def _get_method_args(self):
77 """
77 """
78 Return `self._rpc_args` to dispatched controller method
78 Return `self._rpc_args` to dispatched controller method
79 chosen by __call__
79 chosen by __call__
80 """
80 """
81 return self._rpc_args
81 return self._rpc_args
82
82
83 def __call__(self, environ, start_response):
83 def __call__(self, environ, start_response):
84 """
84 """
85 Parse the request body as JSON, look up the method on the
85 Parse the request body as JSON, look up the method on the
86 controller and if it exists, dispatch to it.
86 controller and if it exists, dispatch to it.
87 """
87 """
88 if 'CONTENT_LENGTH' not in environ:
88 if 'CONTENT_LENGTH' not in environ:
89 log.debug("No Content-Length")
89 log.debug("No Content-Length")
90 return jsonrpc_error(message="No Content-Length in request")
90 return jsonrpc_error(message="No Content-Length in request")
91 else:
91 else:
92 length = environ['CONTENT_LENGTH'] or 0
92 length = environ['CONTENT_LENGTH'] or 0
93 length = int(environ['CONTENT_LENGTH'])
93 length = int(environ['CONTENT_LENGTH'])
94 log.debug('Content-Length: %s', length)
94 log.debug('Content-Length: %s', length)
95
95
96 if length == 0:
96 if length == 0:
97 log.debug("Content-Length is 0")
97 log.debug("Content-Length is 0")
98 return jsonrpc_error(message="Content-Length is 0")
98 return jsonrpc_error(message="Content-Length is 0")
99
99
100 raw_body = environ['wsgi.input'].read(length)
100 raw_body = environ['wsgi.input'].read(length)
101
101
102 try:
102 try:
103 json_body = json.loads(urllib.unquote_plus(raw_body))
103 json_body = json.loads(urllib.unquote_plus(raw_body))
104 except ValueError as e:
104 except ValueError as e:
105 #catch JSON errors Here
105 #catch JSON errors Here
106 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
106 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
107 % (e, urllib.unquote_plus(raw_body)))
107 % (e, urllib.unquote_plus(raw_body)))
108
108
109 #check AUTH based on API KEY
109 #check AUTH based on API KEY
110 try:
110 try:
111 self._req_api_key = json_body['api_key']
111 self._req_api_key = json_body['api_key']
112 self._req_method = json_body['method']
112 self._req_method = json_body['method']
113 self._req_params = json_body['args']
113 self._req_params = json_body['args']
114 log.debug('method: %s, params: %s',
114 log.debug('method: %s, params: %s',
115 self._req_method,
115 self._req_method,
116 self._req_params)
116 self._req_params)
117 except KeyError as e:
117 except KeyError as e:
118 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
118 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
119
119
120 #check if we can find this session using api_key
120 #check if we can find this session using api_key
121 try:
121 try:
122 u = User.get_by_api_key(self._req_api_key)
122 u = User.get_by_api_key(self._req_api_key)
123 auth_u = AuthUser(u.user_id, self._req_api_key)
123 auth_u = AuthUser(u.user_id, self._req_api_key)
124 except Exception as e:
124 except Exception as e:
125 return jsonrpc_error(message='Invalid API KEY')
125 return jsonrpc_error(message='Invalid API KEY')
126
126
127 self._error = None
127 self._error = None
128 try:
128 try:
129 self._func = self._find_method()
129 self._func = self._find_method()
130 except AttributeError, e:
130 except AttributeError, e:
131 return jsonrpc_error(message=str(e))
131 return jsonrpc_error(message=str(e))
132
132
133 # now that we have a method, add self._req_params to
133 # now that we have a method, add self._req_params to
134 # self.kargs and dispatch control to WGIController
134 # self.kargs and dispatch control to WGIController
135 arglist = inspect.getargspec(self._func)[0][1:]
135 arglist = inspect.getargspec(self._func)[0][1:]
136
136
137 # this is little trick to inject logged in user for
137 # this is little trick to inject logged in user for
138 # perms decorators to work they expect the controller class to have
138 # perms decorators to work they expect the controller class to have
139 # rhodecode_user set
139 # rhodecode_user attribute set
140 self.rhodecode_user = auth_u
140 self.rhodecode_user = auth_u
141
141
142 if 'user' not in arglist:
142 # This attribute will need to be first param of a method that uses
143 # api_key, which is translated to instance of user at that name
144 USER_SESSION_ATTR = 'apiuser'
145
146 if USER_SESSION_ATTR not in arglist:
143 return jsonrpc_error(message='This method [%s] does not support '
147 return jsonrpc_error(message='This method [%s] does not support '
144 'authentication (missing user param)' %
148 'authentication (missing %s param)' %
145 self._func.__name__)
149 (self._func.__name__, USER_SESSION_ATTR))
146
150
147 # get our arglist and check if we provided them as args
151 # get our arglist and check if we provided them as args
148 for arg in arglist:
152 for arg in arglist:
149 if arg == 'user':
153 if arg == USER_SESSION_ATTR:
150 # user is something translated from api key and this is
154 # USER_SESSION_ATTR is something translated from api key and
151 # checked before
155 # this is checked before so we don't need validate it
152 continue
156 continue
153
157
154 if not self._req_params or arg not in self._req_params:
158 if not self._req_params or arg not in self._req_params:
155 return jsonrpc_error(message='Missing %s arg in JSON DATA' % arg)
159 return jsonrpc_error(message='Missing %s arg in JSON DATA' % arg)
156
160
157 self._rpc_args = dict(user=u)
161 self._rpc_args = {USER_SESSION_ATTR:u}
158 self._rpc_args.update(self._req_params)
162 self._rpc_args.update(self._req_params)
159
163
160 self._rpc_args['action'] = self._req_method
164 self._rpc_args['action'] = self._req_method
161 self._rpc_args['environ'] = environ
165 self._rpc_args['environ'] = environ
162 self._rpc_args['start_response'] = start_response
166 self._rpc_args['start_response'] = start_response
163
167
164 status = []
168 status = []
165 headers = []
169 headers = []
166 exc_info = []
170 exc_info = []
167 def change_content(new_status, new_headers, new_exc_info=None):
171 def change_content(new_status, new_headers, new_exc_info=None):
168 status.append(new_status)
172 status.append(new_status)
169 headers.extend(new_headers)
173 headers.extend(new_headers)
170 exc_info.append(new_exc_info)
174 exc_info.append(new_exc_info)
171
175
172 output = WSGIController.__call__(self, environ, change_content)
176 output = WSGIController.__call__(self, environ, change_content)
173 output = list(output)
177 output = list(output)
174 headers.append(('Content-Length', str(len(output[0]))))
178 headers.append(('Content-Length', str(len(output[0]))))
175 replace_header(headers, 'Content-Type', 'application/json')
179 replace_header(headers, 'Content-Type', 'application/json')
176 start_response(status[0], headers, exc_info[0])
180 start_response(status[0], headers, exc_info[0])
177
181
178 return output
182 return output
179
183
180 def _dispatch_call(self):
184 def _dispatch_call(self):
181 """
185 """
182 Implement dispatch interface specified by WSGIController
186 Implement dispatch interface specified by WSGIController
183 """
187 """
184 try:
188 try:
185 raw_response = self._inspect_call(self._func)
189 raw_response = self._inspect_call(self._func)
186 print raw_response
187 if isinstance(raw_response, HTTPError):
190 if isinstance(raw_response, HTTPError):
188 self._error = str(raw_response)
191 self._error = str(raw_response)
189 except JSONRPCError as e:
192 except JSONRPCError as e:
190 self._error = str(e)
193 self._error = str(e)
191 except Exception as e:
194 except Exception as e:
192 log.debug('Encountered unhandled exception: %s', repr(e))
195 log.debug('Encountered unhandled exception: %s', repr(e))
193 json_exc = JSONRPCError('Internal server error')
196 json_exc = JSONRPCError('Internal server error')
194 self._error = str(json_exc)
197 self._error = str(json_exc)
195
198
196 if self._error is not None:
199 if self._error is not None:
197 raw_response = None
200 raw_response = None
198
201
199 response = dict(result=raw_response, error=self._error)
202 response = dict(result=raw_response, error=self._error)
200
203
201 try:
204 try:
202 return json.dumps(response)
205 return json.dumps(response)
203 except TypeError, e:
206 except TypeError, e:
204 log.debug('Error encoding response: %s', e)
207 log.debug('Error encoding response: %s', e)
205 return json.dumps(dict(result=None,
208 return json.dumps(dict(result=None,
206 error="Error encoding response"))
209 error="Error encoding response"))
207
210
208 def _find_method(self):
211 def _find_method(self):
209 """
212 """
210 Return method named by `self._req_method` in controller if able
213 Return method named by `self._req_method` in controller if able
211 """
214 """
212 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
215 log.debug('Trying to find JSON-RPC method: %s', self._req_method)
213 if self._req_method.startswith('_'):
216 if self._req_method.startswith('_'):
214 raise AttributeError("Method not allowed")
217 raise AttributeError("Method not allowed")
215
218
216 try:
219 try:
217 func = getattr(self, self._req_method, None)
220 func = getattr(self, self._req_method, None)
218 except UnicodeEncodeError:
221 except UnicodeEncodeError:
219 raise AttributeError("Problem decoding unicode in requested "
222 raise AttributeError("Problem decoding unicode in requested "
220 "method name.")
223 "method name.")
221
224
222 if isinstance(func, types.MethodType):
225 if isinstance(func, types.MethodType):
223 return func
226 return func
224 else:
227 else:
225 raise AttributeError("No such method: %s" % self._req_method)
228 raise AttributeError("No such method: %s" % self._req_method)
229
@@ -1,40 +1,95 b''
1 import traceback
2 import logging
3
1 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
4 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
2 from rhodecode.lib.auth import HasPermissionAllDecorator
5 from rhodecode.lib.auth import HasPermissionAllDecorator
3 from rhodecode.model.scm import ScmModel
6 from rhodecode.model.scm import ScmModel
4
7
8 from rhodecode.model.db import User, UsersGroup
9
10 log = logging.getLogger(__name__)
11
5
12
6 class ApiController(JSONRPCController):
13 class ApiController(JSONRPCController):
7 """
14 """
8 API Controller
15 API Controller
9
16
10
17
11 Each method needs to have USER as argument this is then based on given
18 Each method needs to have USER as argument this is then based on given
12 API_KEY propagated as instance of user object
19 API_KEY propagated as instance of user object
13
20
14 Preferably this should be first argument also
21 Preferably this should be first argument also
15
22
16
23
17 Each function should also **raise** JSONRPCError for any
24 Each function should also **raise** JSONRPCError for any
18 errors that happens
25 errors that happens
19
26
20 """
27 """
21
28
22 @HasPermissionAllDecorator('hg.admin')
29 @HasPermissionAllDecorator('hg.admin')
23 def pull(self, user, repo):
30 def pull(self, apiuser, repo):
24 """
31 """
25 Dispatch pull action on given repo
32 Dispatch pull action on given repo
26
33
27
34
28 param user:
35 :param user:
29 param repo:
36 :param repo:
30 """
37 """
31
38
32 try:
39 try:
33 ScmModel().pull_changes(repo, self.rhodecode_user.username)
40 ScmModel().pull_changes(repo, self.rhodecode_user.username)
34 return 'Pulled from %s' % repo
41 return 'Pulled from %s' % repo
35 except Exception:
42 except Exception:
36 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
43 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
37
44
38
45
46 @HasPermissionAllDecorator('hg.admin')
47 def create_user(self, apiuser, username, password, active, admin, name,
48 lastname, email):
49 """
50 Creates new user
51
52 :param apiuser:
53 :param username:
54 :param password:
55 :param active:
56 :param admin:
57 :param name:
58 :param lastname:
59 :param email:
60 """
61
62 form_data = dict(username=username,
63 password=password,
64 active=active,
65 admin=admin,
66 name=name,
67 lastname=lastname,
68 email=email)
69 try:
70 u = User.create(form_data)
71 return {'id':u.user_id,
72 'msg':'created new user %s' % name}
73 except Exception:
74 log.error(traceback.format_exc())
75 raise JSONRPCError('failed to create user %s' % name)
39
76
40
77
78 @HasPermissionAllDecorator('hg.admin')
79 def create_users_group(self, apiuser, name, active):
80 """
81 Creates an new usergroup
82
83 :param name:
84 :param active:
85 """
86 form_data = {'users_group_name':name,
87 'users_group_active':active}
88 try:
89 ug = UsersGroup.create(form_data)
90 return {'id':ug.users_group_id,
91 'msg':'created new users group %s' % name}
92 except Exception:
93 log.error(traceback.format_exc())
94 raise JSONRPCError('failed to create group %s' % name)
95 No newline at end of file
@@ -1,934 +1,955 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.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
44
43
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
44 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib import str2bool, json, safe_str, get_changeset_safe
45 from rhodecode.lib import str2bool, json, safe_str, get_changeset_safe,\
46 generate_api_key
47
47 from rhodecode.model.meta import Base, Session
48 from rhodecode.model.meta import Base, Session
48 from rhodecode.model.caching_query import FromCache
49 from rhodecode.model.caching_query import FromCache
49
50
50 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
51
52
52 #==============================================================================
53 #==============================================================================
53 # BASE CLASSES
54 # BASE CLASSES
54 #==============================================================================
55 #==============================================================================
55
56
56 class ModelSerializer(json.JSONEncoder):
57 class ModelSerializer(json.JSONEncoder):
57 """
58 """
58 Simple Serializer for JSON,
59 Simple Serializer for JSON,
59
60
60 usage::
61 usage::
61
62
62 to make object customized for serialization implement a __json__
63 to make object customized for serialization implement a __json__
63 method that will return a dict for serialization into json
64 method that will return a dict for serialization into json
64
65
65 example::
66 example::
66
67
67 class Task(object):
68 class Task(object):
68
69
69 def __init__(self, name, value):
70 def __init__(self, name, value):
70 self.name = name
71 self.name = name
71 self.value = value
72 self.value = value
72
73
73 def __json__(self):
74 def __json__(self):
74 return dict(name=self.name,
75 return dict(name=self.name,
75 value=self.value)
76 value=self.value)
76
77
77 """
78 """
78
79
79 def default(self, obj):
80 def default(self, obj):
80
81
81 if hasattr(obj, '__json__'):
82 if hasattr(obj, '__json__'):
82 return obj.__json__()
83 return obj.__json__()
83 else:
84 else:
84 return json.JSONEncoder.default(self, obj)
85 return json.JSONEncoder.default(self, obj)
85
86
86 class BaseModel(object):
87 class BaseModel(object):
87 """Base Model for all classess
88 """Base Model for all classess
88
89
89 """
90 """
90
91
91 @classmethod
92 @classmethod
92 def _get_keys(cls):
93 def _get_keys(cls):
93 """return column names for this model """
94 """return column names for this model """
94 return class_mapper(cls).c.keys()
95 return class_mapper(cls).c.keys()
95
96
96 def get_dict(self):
97 def get_dict(self):
97 """return dict with keys and values corresponding
98 """return dict with keys and values corresponding
98 to this model data """
99 to this model data """
99
100
100 d = {}
101 d = {}
101 for k in self._get_keys():
102 for k in self._get_keys():
102 d[k] = getattr(self, k)
103 d[k] = getattr(self, k)
103 return d
104 return d
104
105
105 def get_appstruct(self):
106 def get_appstruct(self):
106 """return list with keys and values tupples corresponding
107 """return list with keys and values tupples corresponding
107 to this model data """
108 to this model data """
108
109
109 l = []
110 l = []
110 for k in self._get_keys():
111 for k in self._get_keys():
111 l.append((k, getattr(self, k),))
112 l.append((k, getattr(self, k),))
112 return l
113 return l
113
114
114 def populate_obj(self, populate_dict):
115 def populate_obj(self, populate_dict):
115 """populate model with data from given populate_dict"""
116 """populate model with data from given populate_dict"""
116
117
117 for k in self._get_keys():
118 for k in self._get_keys():
118 if k in populate_dict:
119 if k in populate_dict:
119 setattr(self, k, populate_dict[k])
120 setattr(self, k, populate_dict[k])
120
121
121 @classmethod
122 @classmethod
122 def query(cls):
123 def query(cls):
123 return Session.query(cls)
124 return Session.query(cls)
124
125
125 @classmethod
126 @classmethod
126 def get(cls, id_):
127 def get(cls, id_):
127 return Session.query(cls).get(id_)
128 return Session.query(cls).get(id_)
128
129
129 @classmethod
130 @classmethod
130 def delete(cls, id_):
131 def delete(cls, id_):
131 obj = Session.query(cls).get(id_)
132 obj = Session.query(cls).get(id_)
132 Session.delete(obj)
133 Session.delete(obj)
133 Session.commit()
134 Session.commit()
134
135
135
136
136 class RhodeCodeSettings(Base, BaseModel):
137 class RhodeCodeSettings(Base, BaseModel):
137 __tablename__ = 'rhodecode_settings'
138 __tablename__ = 'rhodecode_settings'
138 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
139 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
139 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
140 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
140 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
141 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
141 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
142 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
142
143
143 def __init__(self, k='', v=''):
144 def __init__(self, k='', v=''):
144 self.app_settings_name = k
145 self.app_settings_name = k
145 self.app_settings_value = v
146 self.app_settings_value = v
146
147
147 def __repr__(self):
148 def __repr__(self):
148 return "<%s('%s:%s')>" % (self.__class__.__name__,
149 return "<%s('%s:%s')>" % (self.__class__.__name__,
149 self.app_settings_name, self.app_settings_value)
150 self.app_settings_name, self.app_settings_value)
150
151
151
152
152 @classmethod
153 @classmethod
153 def get_by_name(cls, ldap_key):
154 def get_by_name(cls, ldap_key):
154 return Session.query(cls)\
155 return Session.query(cls)\
155 .filter(cls.app_settings_name == ldap_key).scalar()
156 .filter(cls.app_settings_name == ldap_key).scalar()
156
157
157 @classmethod
158 @classmethod
158 def get_app_settings(cls, cache=False):
159 def get_app_settings(cls, cache=False):
159
160
160 ret = Session.query(cls)
161 ret = Session.query(cls)
161
162
162 if cache:
163 if cache:
163 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
164 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
164
165
165 if not ret:
166 if not ret:
166 raise Exception('Could not get application settings !')
167 raise Exception('Could not get application settings !')
167 settings = {}
168 settings = {}
168 for each in ret:
169 for each in ret:
169 settings['rhodecode_' + each.app_settings_name] = \
170 settings['rhodecode_' + each.app_settings_name] = \
170 each.app_settings_value
171 each.app_settings_value
171
172
172 return settings
173 return settings
173
174
174 @classmethod
175 @classmethod
175 def get_ldap_settings(cls, cache=False):
176 def get_ldap_settings(cls, cache=False):
176 ret = Session.query(cls)\
177 ret = Session.query(cls)\
177 .filter(cls.app_settings_name.startswith('ldap_'))\
178 .filter(cls.app_settings_name.startswith('ldap_'))\
178 .all()
179 .all()
179 fd = {}
180 fd = {}
180 for row in ret:
181 for row in ret:
181 fd.update({row.app_settings_name:row.app_settings_value})
182 fd.update({row.app_settings_name:row.app_settings_value})
182
183
183 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
184 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
184
185
185 return fd
186 return fd
186
187
187
188
188 class RhodeCodeUi(Base, BaseModel):
189 class RhodeCodeUi(Base, BaseModel):
189 __tablename__ = 'rhodecode_ui'
190 __tablename__ = 'rhodecode_ui'
190 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
191 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
191
192
192 HOOK_UPDATE = 'changegroup.update'
193 HOOK_UPDATE = 'changegroup.update'
193 HOOK_REPO_SIZE = 'changegroup.repo_size'
194 HOOK_REPO_SIZE = 'changegroup.repo_size'
194 HOOK_PUSH = 'pretxnchangegroup.push_logger'
195 HOOK_PUSH = 'pretxnchangegroup.push_logger'
195 HOOK_PULL = 'preoutgoing.pull_logger'
196 HOOK_PULL = 'preoutgoing.pull_logger'
196
197
197 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
198 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
198 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
199 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
199 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
202 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
202
203
203
204
204 @classmethod
205 @classmethod
205 def get_by_key(cls, key):
206 def get_by_key(cls, key):
206 return Session.query(cls).filter(cls.ui_key == key)
207 return Session.query(cls).filter(cls.ui_key == key)
207
208
208
209
209 @classmethod
210 @classmethod
210 def get_builtin_hooks(cls):
211 def get_builtin_hooks(cls):
211 q = cls.query()
212 q = cls.query()
212 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
213 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
213 cls.HOOK_REPO_SIZE,
214 cls.HOOK_REPO_SIZE,
214 cls.HOOK_PUSH, cls.HOOK_PULL]))
215 cls.HOOK_PUSH, cls.HOOK_PULL]))
215 return q.all()
216 return q.all()
216
217
217 @classmethod
218 @classmethod
218 def get_custom_hooks(cls):
219 def get_custom_hooks(cls):
219 q = cls.query()
220 q = cls.query()
220 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
221 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
221 cls.HOOK_REPO_SIZE,
222 cls.HOOK_REPO_SIZE,
222 cls.HOOK_PUSH, cls.HOOK_PULL]))
223 cls.HOOK_PUSH, cls.HOOK_PULL]))
223 q = q.filter(cls.ui_section == 'hooks')
224 q = q.filter(cls.ui_section == 'hooks')
224 return q.all()
225 return q.all()
225
226
226 @classmethod
227 @classmethod
227 def create_or_update_hook(cls, key, val):
228 def create_or_update_hook(cls, key, val):
228 new_ui = cls.get_by_key(key).scalar() or cls()
229 new_ui = cls.get_by_key(key).scalar() or cls()
229 new_ui.ui_section = 'hooks'
230 new_ui.ui_section = 'hooks'
230 new_ui.ui_active = True
231 new_ui.ui_active = True
231 new_ui.ui_key = key
232 new_ui.ui_key = key
232 new_ui.ui_value = val
233 new_ui.ui_value = val
233
234
234 Session.add(new_ui)
235 Session.add(new_ui)
235 Session.commit()
236 Session.commit()
236
237
237
238
238 class User(Base, BaseModel):
239 class User(Base, BaseModel):
239 __tablename__ = 'users'
240 __tablename__ = 'users'
240 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
241 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
241 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
242 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
242 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
243 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
243 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
245 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
245 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
246 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
246 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
250 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
250 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252
253
253 user_log = relationship('UserLog', cascade='all')
254 user_log = relationship('UserLog', cascade='all')
254 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
255 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
255
256
256 repositories = relationship('Repository')
257 repositories = relationship('Repository')
257 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
258 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
258 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
259 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
259
260
260 group_member = relationship('UsersGroupMember', cascade='all')
261 group_member = relationship('UsersGroupMember', cascade='all')
261
262
262 @property
263 @property
263 def full_contact(self):
264 def full_contact(self):
264 return '%s %s <%s>' % (self.name, self.lastname, self.email)
265 return '%s %s <%s>' % (self.name, self.lastname, self.email)
265
266
266 @property
267 @property
267 def short_contact(self):
268 def short_contact(self):
268 return '%s %s' % (self.name, self.lastname)
269 return '%s %s' % (self.name, self.lastname)
269
270
270 @property
271 @property
271 def is_admin(self):
272 def is_admin(self):
272 return self.admin
273 return self.admin
273
274
274 def __repr__(self):
275 def __repr__(self):
275 try:
276 try:
276 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
277 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
277 self.user_id, self.username)
278 self.user_id, self.username)
278 except:
279 except:
279 return self.__class__.__name__
280 return self.__class__.__name__
280
281
281 @classmethod
282 @classmethod
282 def by_username(cls, username, case_insensitive=False):
283 def by_username(cls, username, case_insensitive=False):
283 if case_insensitive:
284 if case_insensitive:
284 return Session.query(cls).filter(cls.username.like(username)).one()
285 return Session.query(cls).filter(cls.username.like(username)).one()
285 else:
286 else:
286 return Session.query(cls).filter(cls.username == username).one()
287 return Session.query(cls).filter(cls.username == username).one()
287
288
288 @classmethod
289 @classmethod
289 def get_by_api_key(cls, api_key):
290 def get_by_api_key(cls, api_key):
290 return Session.query(cls).filter(cls.api_key == api_key).one()
291 return Session.query(cls).filter(cls.api_key == api_key).one()
291
292
292
293
293 def update_lastlogin(self):
294 def update_lastlogin(self):
294 """Update user lastlogin"""
295 """Update user lastlogin"""
295
296
296 self.last_login = datetime.datetime.now()
297 self.last_login = datetime.datetime.now()
297 Session.add(self)
298 Session.add(self)
298 Session.commit()
299 Session.commit()
299 log.debug('updated user %s lastlogin', self.username)
300 log.debug('updated user %s lastlogin', self.username)
300
301
302 @classmethod
303 def create(cls, form_data):
304 from rhodecode.lib.auth import get_crypt_password
305
306 try:
307 new_user = cls()
308 for k, v in form_data.items():
309 if k == 'password':
310 v = get_crypt_password(v)
311 setattr(new_user, k, v)
312
313 new_user.api_key = generate_api_key(form_data['username'])
314 Session.add(new_user)
315 Session.commit()
316 return new_user
317 except:
318 log.error(traceback.format_exc())
319 Session.rollback()
320 raise
301
321
302 class UserLog(Base, BaseModel):
322 class UserLog(Base, BaseModel):
303 __tablename__ = 'user_logs'
323 __tablename__ = 'user_logs'
304 __table_args__ = {'extend_existing':True}
324 __table_args__ = {'extend_existing':True}
305 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
306 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
307 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
327 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
308 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
309 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
331 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
312
332
313 @property
333 @property
314 def action_as_day(self):
334 def action_as_day(self):
315 return date(*self.action_date.timetuple()[:3])
335 return date(*self.action_date.timetuple()[:3])
316
336
317 user = relationship('User')
337 user = relationship('User')
318 repository = relationship('Repository')
338 repository = relationship('Repository')
319
339
320
340
321 class UsersGroup(Base, BaseModel):
341 class UsersGroup(Base, BaseModel):
322 __tablename__ = 'users_groups'
342 __tablename__ = 'users_groups'
323 __table_args__ = {'extend_existing':True}
343 __table_args__ = {'extend_existing':True}
324
344
325 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
345 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
326 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
346 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
327 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
347 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
328
348
329 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
349 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
330
350
331 def __repr__(self):
351 def __repr__(self):
332 return '<userGroup(%s)>' % (self.users_group_name)
352 return '<userGroup(%s)>' % (self.users_group_name)
333
353
334 @classmethod
354 @classmethod
335 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
355 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
336 if case_insensitive:
356 if case_insensitive:
337 gr = Session.query(cls)\
357 gr = Session.query(cls)\
338 .filter(cls.users_group_name.ilike(group_name))
358 .filter(cls.users_group_name.ilike(group_name))
339 else:
359 else:
340 gr = Session.query(UsersGroup)\
360 gr = Session.query(UsersGroup)\
341 .filter(UsersGroup.users_group_name == group_name)
361 .filter(UsersGroup.users_group_name == group_name)
342 if cache:
362 if cache:
343 gr = gr.options(FromCache("sql_cache_short",
363 gr = gr.options(FromCache("sql_cache_short",
344 "get_user_%s" % group_name))
364 "get_user_%s" % group_name))
345 return gr.scalar()
365 return gr.scalar()
346
366
347
367
348 @classmethod
368 @classmethod
349 def get(cls, users_group_id, cache=False):
369 def get(cls, users_group_id, cache=False):
350 users_group = Session.query(cls)
370 users_group = Session.query(cls)
351 if cache:
371 if cache:
352 users_group = users_group.options(FromCache("sql_cache_short",
372 users_group = users_group.options(FromCache("sql_cache_short",
353 "get_users_group_%s" % users_group_id))
373 "get_users_group_%s" % users_group_id))
354 return users_group.get(users_group_id)
374 return users_group.get(users_group_id)
355
375
356 @classmethod
376 @classmethod
357 def create(cls, form_data):
377 def create(cls, form_data):
358 try:
378 try:
359 new_users_group = cls()
379 new_users_group = cls()
360 for k, v in form_data.items():
380 for k, v in form_data.items():
361 setattr(new_users_group, k, v)
381 setattr(new_users_group, k, v)
362
382
363 Session.add(new_users_group)
383 Session.add(new_users_group)
364 Session.commit()
384 Session.commit()
385 return new_users_group
365 except:
386 except:
366 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
367 Session.rollback()
388 Session.rollback()
368 raise
389 raise
369
390
370 @classmethod
391 @classmethod
371 def update(cls, users_group_id, form_data):
392 def update(cls, users_group_id, form_data):
372
393
373 try:
394 try:
374 users_group = cls.get(users_group_id, cache=False)
395 users_group = cls.get(users_group_id, cache=False)
375
396
376 for k, v in form_data.items():
397 for k, v in form_data.items():
377 if k == 'users_group_members':
398 if k == 'users_group_members':
378 users_group.members = []
399 users_group.members = []
379 Session.flush()
400 Session.flush()
380 members_list = []
401 members_list = []
381 if v:
402 if v:
382 for u_id in set(v):
403 for u_id in set(v):
383 members_list.append(UsersGroupMember(
404 members_list.append(UsersGroupMember(
384 users_group_id,
405 users_group_id,
385 u_id))
406 u_id))
386 setattr(users_group, 'members', members_list)
407 setattr(users_group, 'members', members_list)
387 setattr(users_group, k, v)
408 setattr(users_group, k, v)
388
409
389 Session.add(users_group)
410 Session.add(users_group)
390 Session.commit()
411 Session.commit()
391 except:
412 except:
392 log.error(traceback.format_exc())
413 log.error(traceback.format_exc())
393 Session.rollback()
414 Session.rollback()
394 raise
415 raise
395
416
396 @classmethod
417 @classmethod
397 def delete(cls, users_group_id):
418 def delete(cls, users_group_id):
398 try:
419 try:
399
420
400 # check if this group is not assigned to repo
421 # check if this group is not assigned to repo
401 assigned_groups = UsersGroupRepoToPerm.query()\
422 assigned_groups = UsersGroupRepoToPerm.query()\
402 .filter(UsersGroupRepoToPerm.users_group_id ==
423 .filter(UsersGroupRepoToPerm.users_group_id ==
403 users_group_id).all()
424 users_group_id).all()
404
425
405 if assigned_groups:
426 if assigned_groups:
406 raise UsersGroupsAssignedException('Group assigned to %s' %
427 raise UsersGroupsAssignedException('Group assigned to %s' %
407 assigned_groups)
428 assigned_groups)
408
429
409 users_group = cls.get(users_group_id, cache=False)
430 users_group = cls.get(users_group_id, cache=False)
410 Session.delete(users_group)
431 Session.delete(users_group)
411 Session.commit()
432 Session.commit()
412 except:
433 except:
413 log.error(traceback.format_exc())
434 log.error(traceback.format_exc())
414 Session.rollback()
435 Session.rollback()
415 raise
436 raise
416
437
417
438
418 class UsersGroupMember(Base, BaseModel):
439 class UsersGroupMember(Base, BaseModel):
419 __tablename__ = 'users_groups_members'
440 __tablename__ = 'users_groups_members'
420 __table_args__ = {'extend_existing':True}
441 __table_args__ = {'extend_existing':True}
421
442
422 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
443 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
423 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
444 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
424 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
445 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
425
446
426 user = relationship('User', lazy='joined')
447 user = relationship('User', lazy='joined')
427 users_group = relationship('UsersGroup')
448 users_group = relationship('UsersGroup')
428
449
429 def __init__(self, gr_id='', u_id=''):
450 def __init__(self, gr_id='', u_id=''):
430 self.users_group_id = gr_id
451 self.users_group_id = gr_id
431 self.user_id = u_id
452 self.user_id = u_id
432
453
433 class Repository(Base, BaseModel):
454 class Repository(Base, BaseModel):
434 __tablename__ = 'repositories'
455 __tablename__ = 'repositories'
435 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
456 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
436
457
437 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
459 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
439 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
460 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
440 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
461 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
441 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
442 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
463 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
443 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
464 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
444 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
465 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
445 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
446 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
467 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
447
468
448 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
469 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
449 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
470 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
450
471
451
472
452 user = relationship('User')
473 user = relationship('User')
453 fork = relationship('Repository', remote_side=repo_id)
474 fork = relationship('Repository', remote_side=repo_id)
454 group = relationship('Group')
475 group = relationship('Group')
455 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
476 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
456 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
477 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
457 stats = relationship('Statistics', cascade='all', uselist=False)
478 stats = relationship('Statistics', cascade='all', uselist=False)
458
479
459 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
480 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
460
481
461 logs = relationship('UserLog', cascade='all')
482 logs = relationship('UserLog', cascade='all')
462
483
463 def __repr__(self):
484 def __repr__(self):
464 return "<%s('%s:%s')>" % (self.__class__.__name__,
485 return "<%s('%s:%s')>" % (self.__class__.__name__,
465 self.repo_id, self.repo_name)
486 self.repo_id, self.repo_name)
466
487
467 @classmethod
488 @classmethod
468 def by_repo_name(cls, repo_name):
489 def by_repo_name(cls, repo_name):
469 q = Session.query(cls).filter(cls.repo_name == repo_name)
490 q = Session.query(cls).filter(cls.repo_name == repo_name)
470
491
471 q = q.options(joinedload(Repository.fork))\
492 q = q.options(joinedload(Repository.fork))\
472 .options(joinedload(Repository.user))\
493 .options(joinedload(Repository.user))\
473 .options(joinedload(Repository.group))\
494 .options(joinedload(Repository.group))\
474
495
475 return q.one()
496 return q.one()
476
497
477 @classmethod
498 @classmethod
478 def get_repo_forks(cls, repo_id):
499 def get_repo_forks(cls, repo_id):
479 return Session.query(cls).filter(Repository.fork_id == repo_id)
500 return Session.query(cls).filter(Repository.fork_id == repo_id)
480
501
481 @property
502 @property
482 def just_name(self):
503 def just_name(self):
483 return self.repo_name.split(os.sep)[-1]
504 return self.repo_name.split(os.sep)[-1]
484
505
485 @property
506 @property
486 def groups_with_parents(self):
507 def groups_with_parents(self):
487 groups = []
508 groups = []
488 if self.group is None:
509 if self.group is None:
489 return groups
510 return groups
490
511
491 cur_gr = self.group
512 cur_gr = self.group
492 groups.insert(0, cur_gr)
513 groups.insert(0, cur_gr)
493 while 1:
514 while 1:
494 gr = getattr(cur_gr, 'parent_group', None)
515 gr = getattr(cur_gr, 'parent_group', None)
495 cur_gr = cur_gr.parent_group
516 cur_gr = cur_gr.parent_group
496 if gr is None:
517 if gr is None:
497 break
518 break
498 groups.insert(0, gr)
519 groups.insert(0, gr)
499
520
500 return groups
521 return groups
501
522
502 @property
523 @property
503 def groups_and_repo(self):
524 def groups_and_repo(self):
504 return self.groups_with_parents, self.just_name
525 return self.groups_with_parents, self.just_name
505
526
506 @LazyProperty
527 @LazyProperty
507 def repo_path(self):
528 def repo_path(self):
508 """
529 """
509 Returns base full path for that repository means where it actually
530 Returns base full path for that repository means where it actually
510 exists on a filesystem
531 exists on a filesystem
511 """
532 """
512 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
533 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
513 q.options(FromCache("sql_cache_short", "repository_repo_path"))
534 q.options(FromCache("sql_cache_short", "repository_repo_path"))
514 return q.one().ui_value
535 return q.one().ui_value
515
536
516 @property
537 @property
517 def repo_full_path(self):
538 def repo_full_path(self):
518 p = [self.repo_path]
539 p = [self.repo_path]
519 # we need to split the name by / since this is how we store the
540 # we need to split the name by / since this is how we store the
520 # names in the database, but that eventually needs to be converted
541 # names in the database, but that eventually needs to be converted
521 # into a valid system path
542 # into a valid system path
522 p += self.repo_name.split('/')
543 p += self.repo_name.split('/')
523 return os.path.join(*p)
544 return os.path.join(*p)
524
545
525 @property
546 @property
526 def _ui(self):
547 def _ui(self):
527 """
548 """
528 Creates an db based ui object for this repository
549 Creates an db based ui object for this repository
529 """
550 """
530 from mercurial import ui
551 from mercurial import ui
531 from mercurial import config
552 from mercurial import config
532 baseui = ui.ui()
553 baseui = ui.ui()
533
554
534 #clean the baseui object
555 #clean the baseui object
535 baseui._ocfg = config.config()
556 baseui._ocfg = config.config()
536 baseui._ucfg = config.config()
557 baseui._ucfg = config.config()
537 baseui._tcfg = config.config()
558 baseui._tcfg = config.config()
538
559
539
560
540 ret = Session.query(RhodeCodeUi)\
561 ret = Session.query(RhodeCodeUi)\
541 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
562 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
542
563
543 hg_ui = ret
564 hg_ui = ret
544 for ui_ in hg_ui:
565 for ui_ in hg_ui:
545 if ui_.ui_active:
566 if ui_.ui_active:
546 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
567 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
547 ui_.ui_key, ui_.ui_value)
568 ui_.ui_key, ui_.ui_value)
548 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
569 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
549
570
550 return baseui
571 return baseui
551
572
552 #==========================================================================
573 #==========================================================================
553 # SCM PROPERTIES
574 # SCM PROPERTIES
554 #==========================================================================
575 #==========================================================================
555
576
556 def get_changeset(self, rev):
577 def get_changeset(self, rev):
557 return get_changeset_safe(self.scm_instance, rev)
578 return get_changeset_safe(self.scm_instance, rev)
558
579
559 @property
580 @property
560 def tip(self):
581 def tip(self):
561 return self.get_changeset('tip')
582 return self.get_changeset('tip')
562
583
563 @property
584 @property
564 def author(self):
585 def author(self):
565 return self.tip.author
586 return self.tip.author
566
587
567 @property
588 @property
568 def last_change(self):
589 def last_change(self):
569 return self.scm_instance.last_change
590 return self.scm_instance.last_change
570
591
571 #==========================================================================
592 #==========================================================================
572 # SCM CACHE INSTANCE
593 # SCM CACHE INSTANCE
573 #==========================================================================
594 #==========================================================================
574
595
575 @property
596 @property
576 def invalidate(self):
597 def invalidate(self):
577 """
598 """
578 Returns Invalidation object if this repo should be invalidated
599 Returns Invalidation object if this repo should be invalidated
579 None otherwise. `cache_active = False` means that this cache
600 None otherwise. `cache_active = False` means that this cache
580 state is not valid and needs to be invalidated
601 state is not valid and needs to be invalidated
581 """
602 """
582 return Session.query(CacheInvalidation)\
603 return Session.query(CacheInvalidation)\
583 .filter(CacheInvalidation.cache_key == self.repo_name)\
604 .filter(CacheInvalidation.cache_key == self.repo_name)\
584 .filter(CacheInvalidation.cache_active == False)\
605 .filter(CacheInvalidation.cache_active == False)\
585 .scalar()
606 .scalar()
586
607
587 def set_invalidate(self):
608 def set_invalidate(self):
588 """
609 """
589 set a cache for invalidation for this instance
610 set a cache for invalidation for this instance
590 """
611 """
591 inv = Session.query(CacheInvalidation)\
612 inv = Session.query(CacheInvalidation)\
592 .filter(CacheInvalidation.cache_key == self.repo_name)\
613 .filter(CacheInvalidation.cache_key == self.repo_name)\
593 .scalar()
614 .scalar()
594
615
595 if inv is None:
616 if inv is None:
596 inv = CacheInvalidation(self.repo_name)
617 inv = CacheInvalidation(self.repo_name)
597 inv.cache_active = True
618 inv.cache_active = True
598 Session.add(inv)
619 Session.add(inv)
599 Session.commit()
620 Session.commit()
600
621
601 @LazyProperty
622 @LazyProperty
602 def scm_instance(self):
623 def scm_instance(self):
603 return self.__get_instance()
624 return self.__get_instance()
604
625
605 @property
626 @property
606 def scm_instance_cached(self):
627 def scm_instance_cached(self):
607 @cache_region('long_term')
628 @cache_region('long_term')
608 def _c(repo_name):
629 def _c(repo_name):
609 return self.__get_instance()
630 return self.__get_instance()
610
631
611 # TODO: remove this trick when beaker 1.6 is released
632 # TODO: remove this trick when beaker 1.6 is released
612 # and have fixed this issue with not supporting unicode keys
633 # and have fixed this issue with not supporting unicode keys
613 rn = safe_str(self.repo_name)
634 rn = safe_str(self.repo_name)
614
635
615 inv = self.invalidate
636 inv = self.invalidate
616 if inv is not None:
637 if inv is not None:
617 region_invalidate(_c, None, rn)
638 region_invalidate(_c, None, rn)
618 # update our cache
639 # update our cache
619 inv.cache_active = True
640 inv.cache_active = True
620 Session.add(inv)
641 Session.add(inv)
621 Session.commit()
642 Session.commit()
622
643
623 return _c(rn)
644 return _c(rn)
624
645
625 def __get_instance(self):
646 def __get_instance(self):
626
647
627 repo_full_path = self.repo_full_path
648 repo_full_path = self.repo_full_path
628
649
629 try:
650 try:
630 alias = get_scm(repo_full_path)[0]
651 alias = get_scm(repo_full_path)[0]
631 log.debug('Creating instance of %s repository', alias)
652 log.debug('Creating instance of %s repository', alias)
632 backend = get_backend(alias)
653 backend = get_backend(alias)
633 except VCSError:
654 except VCSError:
634 log.error(traceback.format_exc())
655 log.error(traceback.format_exc())
635 log.error('Perhaps this repository is in db and not in '
656 log.error('Perhaps this repository is in db and not in '
636 'filesystem run rescan repositories with '
657 'filesystem run rescan repositories with '
637 '"destroy old data " option from admin panel')
658 '"destroy old data " option from admin panel')
638 return
659 return
639
660
640 if alias == 'hg':
661 if alias == 'hg':
641
662
642 repo = backend(safe_str(repo_full_path), create=False,
663 repo = backend(safe_str(repo_full_path), create=False,
643 baseui=self._ui)
664 baseui=self._ui)
644 #skip hidden web repository
665 #skip hidden web repository
645 if repo._get_hidden():
666 if repo._get_hidden():
646 return
667 return
647 else:
668 else:
648 repo = backend(repo_full_path, create=False)
669 repo = backend(repo_full_path, create=False)
649
670
650 return repo
671 return repo
651
672
652
673
653 class Group(Base, BaseModel):
674 class Group(Base, BaseModel):
654 __tablename__ = 'groups'
675 __tablename__ = 'groups'
655 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
676 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
656 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
677 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
657 __mapper_args__ = {'order_by':'group_name'}
678 __mapper_args__ = {'order_by':'group_name'}
658
679
659 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
680 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
660 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
681 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
661 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
682 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
662 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
683 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
663
684
664 parent_group = relationship('Group', remote_side=group_id)
685 parent_group = relationship('Group', remote_side=group_id)
665
686
666
687
667 def __init__(self, group_name='', parent_group=None):
688 def __init__(self, group_name='', parent_group=None):
668 self.group_name = group_name
689 self.group_name = group_name
669 self.parent_group = parent_group
690 self.parent_group = parent_group
670
691
671 def __repr__(self):
692 def __repr__(self):
672 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
693 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
673 self.group_name)
694 self.group_name)
674
695
675 @classmethod
696 @classmethod
676 def url_sep(cls):
697 def url_sep(cls):
677 return '/'
698 return '/'
678
699
679 @property
700 @property
680 def parents(self):
701 def parents(self):
681 parents_recursion_limit = 5
702 parents_recursion_limit = 5
682 groups = []
703 groups = []
683 if self.parent_group is None:
704 if self.parent_group is None:
684 return groups
705 return groups
685 cur_gr = self.parent_group
706 cur_gr = self.parent_group
686 groups.insert(0, cur_gr)
707 groups.insert(0, cur_gr)
687 cnt = 0
708 cnt = 0
688 while 1:
709 while 1:
689 cnt += 1
710 cnt += 1
690 gr = getattr(cur_gr, 'parent_group', None)
711 gr = getattr(cur_gr, 'parent_group', None)
691 cur_gr = cur_gr.parent_group
712 cur_gr = cur_gr.parent_group
692 if gr is None:
713 if gr is None:
693 break
714 break
694 if cnt == parents_recursion_limit:
715 if cnt == parents_recursion_limit:
695 # this will prevent accidental infinit loops
716 # this will prevent accidental infinit loops
696 log.error('group nested more than %s' %
717 log.error('group nested more than %s' %
697 parents_recursion_limit)
718 parents_recursion_limit)
698 break
719 break
699
720
700 groups.insert(0, gr)
721 groups.insert(0, gr)
701 return groups
722 return groups
702
723
703 @property
724 @property
704 def children(self):
725 def children(self):
705 return Session.query(Group).filter(Group.parent_group == self)
726 return Session.query(Group).filter(Group.parent_group == self)
706
727
707 @property
728 @property
708 def full_path(self):
729 def full_path(self):
709 return Group.url_sep().join([g.group_name for g in self.parents] +
730 return Group.url_sep().join([g.group_name for g in self.parents] +
710 [self.group_name])
731 [self.group_name])
711
732
712 @property
733 @property
713 def repositories(self):
734 def repositories(self):
714 return Session.query(Repository).filter(Repository.group == self)
735 return Session.query(Repository).filter(Repository.group == self)
715
736
716 @property
737 @property
717 def repositories_recursive_count(self):
738 def repositories_recursive_count(self):
718 cnt = self.repositories.count()
739 cnt = self.repositories.count()
719
740
720 def children_count(group):
741 def children_count(group):
721 cnt = 0
742 cnt = 0
722 for child in group.children:
743 for child in group.children:
723 cnt += child.repositories.count()
744 cnt += child.repositories.count()
724 cnt += children_count(child)
745 cnt += children_count(child)
725 return cnt
746 return cnt
726
747
727 return cnt + children_count(self)
748 return cnt + children_count(self)
728
749
729 class Permission(Base, BaseModel):
750 class Permission(Base, BaseModel):
730 __tablename__ = 'permissions'
751 __tablename__ = 'permissions'
731 __table_args__ = {'extend_existing':True}
752 __table_args__ = {'extend_existing':True}
732 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
753 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
733 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
754 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
734 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
755 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
735
756
736 def __repr__(self):
757 def __repr__(self):
737 return "<%s('%s:%s')>" % (self.__class__.__name__,
758 return "<%s('%s:%s')>" % (self.__class__.__name__,
738 self.permission_id, self.permission_name)
759 self.permission_id, self.permission_name)
739
760
740 @classmethod
761 @classmethod
741 def get_by_key(cls, key):
762 def get_by_key(cls, key):
742 return Session.query(cls).filter(cls.permission_name == key).scalar()
763 return Session.query(cls).filter(cls.permission_name == key).scalar()
743
764
744 class RepoToPerm(Base, BaseModel):
765 class RepoToPerm(Base, BaseModel):
745 __tablename__ = 'repo_to_perm'
766 __tablename__ = 'repo_to_perm'
746 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
767 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
747 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
768 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
748 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
769 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
749 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
770 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
750 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
771 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
751
772
752 user = relationship('User')
773 user = relationship('User')
753 permission = relationship('Permission')
774 permission = relationship('Permission')
754 repository = relationship('Repository')
775 repository = relationship('Repository')
755
776
756 class UserToPerm(Base, BaseModel):
777 class UserToPerm(Base, BaseModel):
757 __tablename__ = 'user_to_perm'
778 __tablename__ = 'user_to_perm'
758 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
779 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
759 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
780 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
760 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
781 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
761 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
782 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
762
783
763 user = relationship('User')
784 user = relationship('User')
764 permission = relationship('Permission')
785 permission = relationship('Permission')
765
786
766 @classmethod
787 @classmethod
767 def has_perm(cls, user_id, perm):
788 def has_perm(cls, user_id, perm):
768 if not isinstance(perm, Permission):
789 if not isinstance(perm, Permission):
769 raise Exception('perm needs to be an instance of Permission class')
790 raise Exception('perm needs to be an instance of Permission class')
770
791
771 return Session.query(cls).filter(cls.user_id == user_id)\
792 return Session.query(cls).filter(cls.user_id == user_id)\
772 .filter(cls.permission == perm).scalar() is not None
793 .filter(cls.permission == perm).scalar() is not None
773
794
774 @classmethod
795 @classmethod
775 def grant_perm(cls, user_id, perm):
796 def grant_perm(cls, user_id, perm):
776 if not isinstance(perm, Permission):
797 if not isinstance(perm, Permission):
777 raise Exception('perm needs to be an instance of Permission class')
798 raise Exception('perm needs to be an instance of Permission class')
778
799
779 new = cls()
800 new = cls()
780 new.user_id = user_id
801 new.user_id = user_id
781 new.permission = perm
802 new.permission = perm
782 try:
803 try:
783 Session.add(new)
804 Session.add(new)
784 Session.commit()
805 Session.commit()
785 except:
806 except:
786 Session.rollback()
807 Session.rollback()
787
808
788
809
789 @classmethod
810 @classmethod
790 def revoke_perm(cls, user_id, perm):
811 def revoke_perm(cls, user_id, perm):
791 if not isinstance(perm, Permission):
812 if not isinstance(perm, Permission):
792 raise Exception('perm needs to be an instance of Permission class')
813 raise Exception('perm needs to be an instance of Permission class')
793
814
794 try:
815 try:
795 Session.query(cls).filter(cls.user_id == user_id)\
816 Session.query(cls).filter(cls.user_id == user_id)\
796 .filter(cls.permission == perm).delete()
817 .filter(cls.permission == perm).delete()
797 Session.commit()
818 Session.commit()
798 except:
819 except:
799 Session.rollback()
820 Session.rollback()
800
821
801 class UsersGroupRepoToPerm(Base, BaseModel):
822 class UsersGroupRepoToPerm(Base, BaseModel):
802 __tablename__ = 'users_group_repo_to_perm'
823 __tablename__ = 'users_group_repo_to_perm'
803 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
824 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
804 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
825 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
805 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
826 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
806 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
827 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
807 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
828 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
808
829
809 users_group = relationship('UsersGroup')
830 users_group = relationship('UsersGroup')
810 permission = relationship('Permission')
831 permission = relationship('Permission')
811 repository = relationship('Repository')
832 repository = relationship('Repository')
812
833
813 def __repr__(self):
834 def __repr__(self):
814 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
835 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
815
836
816 class UsersGroupToPerm(Base, BaseModel):
837 class UsersGroupToPerm(Base, BaseModel):
817 __tablename__ = 'users_group_to_perm'
838 __tablename__ = 'users_group_to_perm'
818 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
839 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
819 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
840 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
820 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
841 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
821
842
822 users_group = relationship('UsersGroup')
843 users_group = relationship('UsersGroup')
823 permission = relationship('Permission')
844 permission = relationship('Permission')
824
845
825
846
826 @classmethod
847 @classmethod
827 def has_perm(cls, users_group_id, perm):
848 def has_perm(cls, users_group_id, perm):
828 if not isinstance(perm, Permission):
849 if not isinstance(perm, Permission):
829 raise Exception('perm needs to be an instance of Permission class')
850 raise Exception('perm needs to be an instance of Permission class')
830
851
831 return Session.query(cls).filter(cls.users_group_id ==
852 return Session.query(cls).filter(cls.users_group_id ==
832 users_group_id)\
853 users_group_id)\
833 .filter(cls.permission == perm)\
854 .filter(cls.permission == perm)\
834 .scalar() is not None
855 .scalar() is not None
835
856
836 @classmethod
857 @classmethod
837 def grant_perm(cls, users_group_id, perm):
858 def grant_perm(cls, users_group_id, perm):
838 if not isinstance(perm, Permission):
859 if not isinstance(perm, Permission):
839 raise Exception('perm needs to be an instance of Permission class')
860 raise Exception('perm needs to be an instance of Permission class')
840
861
841 new = cls()
862 new = cls()
842 new.users_group_id = users_group_id
863 new.users_group_id = users_group_id
843 new.permission = perm
864 new.permission = perm
844 try:
865 try:
845 Session.add(new)
866 Session.add(new)
846 Session.commit()
867 Session.commit()
847 except:
868 except:
848 Session.rollback()
869 Session.rollback()
849
870
850
871
851 @classmethod
872 @classmethod
852 def revoke_perm(cls, users_group_id, perm):
873 def revoke_perm(cls, users_group_id, perm):
853 if not isinstance(perm, Permission):
874 if not isinstance(perm, Permission):
854 raise Exception('perm needs to be an instance of Permission class')
875 raise Exception('perm needs to be an instance of Permission class')
855
876
856 try:
877 try:
857 Session.query(cls).filter(cls.users_group_id == users_group_id)\
878 Session.query(cls).filter(cls.users_group_id == users_group_id)\
858 .filter(cls.permission == perm).delete()
879 .filter(cls.permission == perm).delete()
859 Session.commit()
880 Session.commit()
860 except:
881 except:
861 Session.rollback()
882 Session.rollback()
862
883
863
884
864 class GroupToPerm(Base, BaseModel):
885 class GroupToPerm(Base, BaseModel):
865 __tablename__ = 'group_to_perm'
886 __tablename__ = 'group_to_perm'
866 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
887 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
867
888
868 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
889 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
869 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
890 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
870 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
891 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
871 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
892 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
872
893
873 user = relationship('User')
894 user = relationship('User')
874 permission = relationship('Permission')
895 permission = relationship('Permission')
875 group = relationship('Group')
896 group = relationship('Group')
876
897
877 class Statistics(Base, BaseModel):
898 class Statistics(Base, BaseModel):
878 __tablename__ = 'statistics'
899 __tablename__ = 'statistics'
879 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
900 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
880 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
901 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
881 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
902 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
882 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
903 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
883 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
904 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
884 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
905 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
885 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
906 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
886
907
887 repository = relationship('Repository', single_parent=True)
908 repository = relationship('Repository', single_parent=True)
888
909
889 class UserFollowing(Base, BaseModel):
910 class UserFollowing(Base, BaseModel):
890 __tablename__ = 'user_followings'
911 __tablename__ = 'user_followings'
891 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
912 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
892 UniqueConstraint('user_id', 'follows_user_id')
913 UniqueConstraint('user_id', 'follows_user_id')
893 , {'extend_existing':True})
914 , {'extend_existing':True})
894
915
895 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
916 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
917 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
897 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
918 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
898 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
919 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
899 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
920 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
900
921
901 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
922 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
902
923
903 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
924 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
904 follows_repository = relationship('Repository', order_by='Repository.repo_name')
925 follows_repository = relationship('Repository', order_by='Repository.repo_name')
905
926
906
927
907 @classmethod
928 @classmethod
908 def get_repo_followers(cls, repo_id):
929 def get_repo_followers(cls, repo_id):
909 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
930 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
910
931
911 class CacheInvalidation(Base, BaseModel):
932 class CacheInvalidation(Base, BaseModel):
912 __tablename__ = 'cache_invalidation'
933 __tablename__ = 'cache_invalidation'
913 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
934 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
914 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
935 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
936 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
916 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
937 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
917 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
938 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
918
939
919
940
920 def __init__(self, cache_key, cache_args=''):
941 def __init__(self, cache_key, cache_args=''):
921 self.cache_key = cache_key
942 self.cache_key = cache_key
922 self.cache_args = cache_args
943 self.cache_args = cache_args
923 self.cache_active = False
944 self.cache_active = False
924
945
925 def __repr__(self):
946 def __repr__(self):
926 return "<%s('%s:%s')>" % (self.__class__.__name__,
947 return "<%s('%s:%s')>" % (self.__class__.__name__,
927 self.cache_id, self.cache_key)
948 self.cache_id, self.cache_key)
928
949
929 class DbMigrateVersion(Base, BaseModel):
950 class DbMigrateVersion(Base, BaseModel):
930 __tablename__ = 'db_migrate_version'
951 __tablename__ = 'db_migrate_version'
931 __table_args__ = {'extend_existing':True}
952 __table_args__ = {'extend_existing':True}
932 repository_id = Column('repository_id', String(250), primary_key=True)
953 repository_id = Column('repository_id', String(250), primary_key=True)
933 repository_path = Column('repository_path', Text)
954 repository_path = Column('repository_path', Text)
934 version = Column('version', Integer)
955 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now