Mercurial > kallithea
changeset 1614:59ae82850e76 beta
Merge with upstream
author | Liad Shani <liadff@gmail.com> |
---|---|
date | Sun, 09 Oct 2011 23:49:00 +0200 |
parents | 6cab36e31f09 (current diff) c307c920f94c (diff) |
children | 51bd5404529c |
files | rhodecode/lib/auth.py rhodecode/lib/base.py rhodecode/lib/middleware/simplehg.py rhodecode/lib/odict.py rhodecode/lib/oset.py |
diffstat | 50 files changed, 1307 insertions(+), 749 deletions(-) [+] |
line wrap: on
line diff
--- a/README.rst Tue Sep 27 22:20:24 2011 +0300 +++ b/README.rst Sun Oct 09 23:49:00 2011 +0200 @@ -2,10 +2,11 @@ Welcome to RhodeCode (RhodiumCode) documentation! ================================================= -``RhodeCode`` (formerly hg-app) is a Pylons framework based Mercurial repository +``RhodeCode`` is a Pylons framework based Mercurial repository browser/management tool with a built in push/pull server and full text search. It works on http/https and has a built in permission/authentication system with -the ability to authenticate via LDAP. +the ability to authenticate via LDAP or ActiveDirectory. RhodeCode also supports +simple API so it's easy integrable with existing systems. RhodeCode is similar in some respects to github or bitbucket_, however RhodeCode can be run as standalone hosted application on your own server.
--- a/docs/api/api.rst Tue Sep 27 22:20:24 2011 +0300 +++ b/docs/api/api.rst Sun Oct 09 23:49:00 2011 +0200 @@ -6,11 +6,12 @@ Starting from RhodeCode version 1.2 a simple API was implemented. -There's one schema for calling all api methods. API is implemented -with JSON protocol both ways. +There's a single schema for calling all api methods. API is implemented +with JSON protocol both ways. An url to send API request in RhodeCode is +<your_server>/_admin/api -Clients need to send JSON data in such format:: +All clients need to send JSON data in such format:: { "api_key":"<api_key>", @@ -18,16 +19,20 @@ "args":{"<arg_key>":"<arg_val>"} } -Simply provide api_key for access and permission validation -method is name of method to call -and args is an key:value list of arguments to pass to method +Example call for autopulling remotes repos using curl:: + curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}' + +Simply provide + - *api_key* for access and permission validation. + - *method* is name of method to call + - *args* is an key:value list of arguments to pass to method .. note:: api_key can be found in your user account page -And will receive JSON formatted answer:: +RhodeCode API will return always a JSON formatted answer:: { "result": "<result>", @@ -35,7 +40,7 @@ } All responses from API will be `HTTP/1.0 200 OK`, if there's an error while -calling api **error** key from response will contain failure description +calling api *error* key from response will contain failure description and result will be null. API METHODS @@ -46,11 +51,61 @@ ---- Pulls given repo from remote location. Can be used to automatically keep -remote repos upto date. This command can be executed only using admin users -api_key +remote repos up to date. This command can be executed only using api_key +belonging to user with admin rights -:: - +INPUT:: + + api_key:"<api_key>" method: "pull" args: {"repo":<repo_name>} +OUTPUT:: + + result:"Pulled from <repo_name>" + error:null + + +create_user +----------- + +Creates new user in RhodeCode. This command can be executed only using api_key +belonging to user with admin rights + +INPUT:: + + api_key:"<api_key>" + method: "create_user" + args: {"username": "<username>", + "password": "<password>", + "active": "<bool>", + "admin": "<bool>", + "name": "<firstname>", + "lastname": "<lastname>", + "email": "<useremail>"} + +OUTPUT:: + + result:{"id": <newuserid>, + "msg":"created new user <username>"} + error:null + + +create_users_group +------------------ + +creates new users group. This command can be executed only using api_key +belonging to user with admin rights + +INPUT:: + + api_key:"<api_key>" + method: "create_user" + args: {"name": "<groupname>", + "active":"<bool>"} + +OUTPUT:: + + result:{"id": <newusersgroupid>, + "msg":"created new users group <groupname>"} + error:null
--- a/docs/changelog.rst Tue Sep 27 22:20:24 2011 +0300 +++ b/docs/changelog.rst Sun Oct 09 23:49:00 2011 +0200 @@ -3,7 +3,8 @@ Changelog ========= -1.2.0 (**2011-XX-XX**) + +1.3.0 (**XXXX-XX-XX**) ====================== :status: in-progress @@ -12,6 +13,29 @@ news ---- +fixes +----- + +1.2.1 (**2011-10-08**) +====================== + +news +---- + + +fixes +----- + +- fixed problems with basic auth and push problems +- gui fixes +- fixed logger + +1.2.0 (**2011-10-07**) +====================== + +news +---- + - implemented #47 repository groups - implemented #89 Can setup google analytics code from settings menu - implemented #91 added nicer looking archive urls with more download options
--- a/rhodecode/__init__.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/__init__.py Sun Oct 09 23:49:00 2011 +0200 @@ -25,9 +25,9 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import platform -VERSION = (1, 2, 0, 'beta') +VERSION = (1, 3, 0, 'beta') __version__ = '.'.join((str(each) for each in VERSION[:4])) -__dbversion__ = 3 #defines current db version for migrations +__dbversion__ = 4 #defines current db version for migrations __platform__ = platform.system() __license__ = 'GPLv3'
--- a/rhodecode/config/middleware.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/config/middleware.py Sun Oct 09 23:49:00 2011 +0200 @@ -51,6 +51,9 @@ from rhodecode.lib.profiler import ProfilingMiddleware app = ProfilingMiddleware(app) + + # we want our low level middleware to get to the request ASAP. We don't + # need any pylons stack middleware in them app = SimpleHg(app, config) app = SimpleGit(app, config) @@ -77,6 +80,7 @@ app = Cascade([static_app, app]) app = make_gzip_middleware(app, global_conf, compress_level=1) + app.config = config return app
--- a/rhodecode/config/routing.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/config/routing.py Sun Oct 09 23:49:00 2011 +0200 @@ -7,7 +7,7 @@ """ from __future__ import with_statement from routes import Mapper -from rhodecode.lib.utils import check_repo_fast as cr + # prefix for non repository related links needs to be prefixed with `/` ADMIN_PREFIX = '/_admin' @@ -19,23 +19,36 @@ always_scan=config['debug']) rmap.minimization = False rmap.explicit = False - + + from rhodecode.lib.utils import is_valid_repo + from rhodecode.lib.utils import is_valid_repos_group + def check_repo(environ, match_dict): """ check for valid repository for proper 404 handling + :param environ: :param match_dict: """ + repo_name = match_dict.get('repo_name') - return not cr(repo_name, config['base_path']) + return is_valid_repo(repo_name, config['base_path']) + + def check_group(environ, match_dict): + """ + check for valid repositories group for proper 404 handling + + :param environ: + :param match_dict: + """ + repos_group_name = match_dict.get('group_name') + + return is_valid_repos_group(repos_group_name, config['base_path']) def check_int(environ, match_dict): return match_dict.get('id').isdigit() - - - # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved rmap.connect('/error/{action}', controller='error') @@ -319,6 +332,14 @@ #========================================================================== # REPOSITORY ROUTES #========================================================================== + rmap.connect('summary_home', '/{repo_name:.*}', + controller='summary', + conditions=dict(function=check_repo)) + +# rmap.connect('repo_group_home', '/{group_name:.*}', +# controller='admin/repos_groups',action="show_by_name", +# conditions=dict(function=check_group)) + rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}', controller='changeset', revision='tip', conditions=dict(function=check_repo)) @@ -328,9 +349,6 @@ controller='changeset', action='raw_changeset', revision='tip', conditions=dict(function=check_repo)) - rmap.connect('summary_home', '/{repo_name:.*}', - controller='summary', conditions=dict(function=check_repo)) - rmap.connect('summary_home', '/{repo_name:.*}/summary', controller='summary', conditions=dict(function=check_repo))
--- a/rhodecode/controllers/admin/permissions.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/admin/permissions.py Sun Oct 09 23:49:00 2011 +0200 @@ -23,19 +23,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import logging +import traceback +import formencode from formencode import htmlfill + from pylons import request, session, tmpl_context as c, url from pylons.controllers.util import abort, redirect from pylons.i18n.translation import _ + from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator from rhodecode.lib.base import BaseController, render -from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm +from rhodecode.model.forms import DefaultPermissionsForm from rhodecode.model.permission import PermissionModel -from rhodecode.model.user import UserModel -import formencode -import logging -import traceback +from rhodecode.model.db import User log = logging.getLogger(__name__) @@ -142,7 +144,7 @@ c.create_choices = self.create_choices if id == 'default': - default_user = UserModel().get_by_username('default') + default_user = User.get_by_username('default') defaults = {'_method': 'put', 'anonymous': default_user.active}
--- a/rhodecode/controllers/admin/repos.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/admin/repos.py Sun Oct 09 23:49:00 2011 +0200 @@ -89,7 +89,7 @@ """ self.__load_defaults() - c.repo_info = db_repo = Repository.by_repo_name(repo_name) + c.repo_info = db_repo = Repository.get_by_repo_name(repo_name) repo = scm_repo = db_repo.scm_instance if c.repo_info is None: @@ -101,7 +101,7 @@ return redirect(url('repos')) - c.default_user_id = User.by_username('default').user_id + c.default_user_id = User.get_by_username('default').user_id c.in_public_journal = self.sa.query(UserFollowing)\ .filter(UserFollowing.user_id == c.default_user_id)\ .filter(UserFollowing.follows_repository == c.repo_info).scalar() @@ -381,8 +381,8 @@ token = get_token() if cur_token == token: try: - repo_id = Repository.by_repo_name(repo_name).repo_id - user_id = User.by_username('default').user_id + repo_id = Repository.get_by_repo_name(repo_name).repo_id + user_id = User.get_by_username('default').user_id self.scm_model.toggle_following_repo(repo_id, user_id) h.flash(_('Updated repository visibility in public journal'), category='success')
--- a/rhodecode/controllers/admin/settings.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/admin/settings.py Sun Oct 09 23:49:00 2011 +0200 @@ -46,6 +46,7 @@ ApplicationUiSettingsForm from rhodecode.model.scm import ScmModel from rhodecode.model.user import UserModel +from rhodecode.model.db import User log = logging.getLogger(__name__) @@ -299,7 +300,7 @@ """ # url('admin_settings_my_account') - c.user = UserModel().get(self.rhodecode_user.user_id, cache=False) + c.user = User.get(self.rhodecode_user.user_id) all_repos = self.sa.query(Repository)\ .filter(Repository.user_id == c.user.user_id)\ .order_by(func.lower(Repository.repo_name)).all() @@ -340,8 +341,7 @@ category='success') except formencode.Invalid, errors: - c.user = user_model.get(self.rhodecode_user.user_id, cache=False) - c.user = UserModel().get(self.rhodecode_user.user_id, cache=False) + c.user = User.get(self.rhodecode_user.user_id) all_repos = self.sa.query(Repository)\ .filter(Repository.user_id == c.user.user_id)\ .order_by(func.lower(Repository.repo_name))\
--- a/rhodecode/controllers/api/__init__.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/api/__init__.py Sun Oct 09 23:49:00 2011 +0200 @@ -26,10 +26,12 @@ # MA 02110-1301, USA. import inspect -import json import logging import types import urllib +import traceback + +from rhodecode.lib.compat import izip_longest, json from paste.response import replace_header @@ -39,7 +41,7 @@ from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \ HTTPBadRequest, HTTPError -from rhodecode.model.user import User +from rhodecode.model.db import User from rhodecode.lib.auth import AuthUser log = logging.getLogger('JSONRPC') @@ -85,10 +87,9 @@ Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ - if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") - return jsonrpc_error(0, "No Content-Length") + return jsonrpc_error(message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) @@ -96,20 +97,18 @@ if length == 0: log.debug("Content-Length is 0") - return jsonrpc_error(0, "Content-Length is 0") + return jsonrpc_error(message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(urllib.unquote_plus(raw_body)) - except ValueError as e: + except ValueError, e: #catch JSON errors Here - return jsonrpc_error("JSON parse error ERR:%s RAW:%r" \ + return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \ % (e, urllib.unquote_plus(raw_body))) - #check AUTH based on API KEY - try: self._req_api_key = json_body['api_key'] self._req_method = json_body['method'] @@ -117,47 +116,61 @@ log.debug('method: %s, params: %s', self._req_method, self._req_params) - except KeyError as e: + except KeyError, e: return jsonrpc_error(message='Incorrect JSON query missing %s' % e) #check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) auth_u = AuthUser(u.user_id, self._req_api_key) - except Exception as e: + except Exception, e: return jsonrpc_error(message='Invalid API KEY') self._error = None try: self._func = self._find_method() except AttributeError, e: - return jsonrpc_error(str(e)) + return jsonrpc_error(message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController - arglist = inspect.getargspec(self._func)[0][1:] + argspec = inspect.getargspec(self._func) + arglist = argspec[0][1:] + defaults = argspec[3] or [] + default_empty = types.NotImplementedType + + kwarglist = list(izip_longest(reversed(arglist), reversed(defaults), + fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have - # rhodecode_user set + # rhodecode_user attribute set self.rhodecode_user = auth_u - if 'user' not in arglist: - return jsonrpc_error('This method [%s] does not support ' - 'authentication (missing user param)' % - self._func.__name__) + # This attribute will need to be first param of a method that uses + # api_key, which is translated to instance of user at that name + USER_SESSION_ATTR = 'apiuser' + + if USER_SESSION_ATTR not in arglist: + return jsonrpc_error(message='This method [%s] does not support ' + 'authentication (missing %s param)' % + (self._func.__name__, USER_SESSION_ATTR)) # get our arglist and check if we provided them as args - for arg in arglist: - if arg == 'user': - # user is something translated from api key and this is - # checked before + for arg, default in kwarglist: + if arg == USER_SESSION_ATTR: + # USER_SESSION_ATTR is something translated from api key and + # this is checked before so we don't need validate it continue - if not self._req_params or arg not in self._req_params: - return jsonrpc_error('Missing %s arg in JSON DATA' % arg) + # skip the required param check if it's default value is + # NotImplementedType (default_empty) + if not self._req_params or (type(default) == default_empty + and arg not in self._req_params): + return jsonrpc_error(message=('Missing non optional %s arg ' + 'in JSON DATA') % arg) - self._rpc_args = dict(user=u) + self._rpc_args = {USER_SESSION_ATTR:u} self._rpc_args.update(self._req_params) self._rpc_args['action'] = self._req_method @@ -186,13 +199,13 @@ """ try: raw_response = self._inspect_call(self._func) - print raw_response if isinstance(raw_response, HTTPError): self._error = str(raw_response) - except JSONRPCError as e: + except JSONRPCError, e: self._error = str(e) - except Exception as e: - log.debug('Encountered unhandled exception: %s', repr(e)) + except Exception, e: + log.error('Encountered unhandled exception: %s' \ + % traceback.format_exc()) json_exc = JSONRPCError('Internal server error') self._error = str(json_exc) @@ -226,3 +239,4 @@ return func else: raise AttributeError("No such method: %s" % self._req_method) +
--- a/rhodecode/controllers/api/api.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/api/api.py Sun Oct 09 23:49:00 2011 +0200 @@ -1,7 +1,14 @@ +import traceback +import logging + from rhodecode.controllers.api import JSONRPCController, JSONRPCError from rhodecode.lib.auth import HasPermissionAllDecorator from rhodecode.model.scm import ScmModel +from rhodecode.model.db import User, UsersGroup, Repository + +log = logging.getLogger(__name__) + class ApiController(JSONRPCController): """ @@ -20,15 +27,18 @@ """ @HasPermissionAllDecorator('hg.admin') - def pull(self, user, repo): + def pull(self, apiuser, repo): """ Dispatch pull action on given repo - param user: - param repo: + :param user: + :param repo: """ + if Repository.is_valid(repo) is False: + raise JSONRPCError('Unknown repo "%s"' % repo) + try: ScmModel().pull_changes(repo, self.rhodecode_user.username) return 'Pulled from %s' % repo @@ -36,5 +46,53 @@ raise JSONRPCError('Unable to pull changes from "%s"' % repo) + @HasPermissionAllDecorator('hg.admin') + def create_user(self, apiuser, username, password, active, admin, name, + lastname, email): + """ + Creates new user + + :param apiuser: + :param username: + :param password: + :param active: + :param admin: + :param name: + :param lastname: + :param email: + """ + + form_data = dict(username=username, + password=password, + active=active, + admin=admin, + name=name, + lastname=lastname, + email=email) + try: + u = User.create(form_data) + return {'id':u.user_id, + 'msg':'created new user %s' % name} + except Exception: + log.error(traceback.format_exc()) + raise JSONRPCError('failed to create user %s' % name) + @HasPermissionAllDecorator('hg.admin') + def create_users_group(self, apiuser, name, active): + """ + Creates an new usergroup + + :param name: + :param active: + """ + form_data = {'users_group_name':name, + 'users_group_active':active} + try: + ug = UsersGroup.create(form_data) + return {'id':ug.users_group_id, + 'msg':'created new users group %s' % name} + except Exception: + log.error(traceback.format_exc()) + raise JSONRPCError('failed to create group %s' % name) + \ No newline at end of file
--- a/rhodecode/controllers/branches.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/branches.py Sun Oct 09 23:49:00 2011 +0200 @@ -30,7 +30,7 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.odict import OrderedDict +from rhodecode.lib.compat import OrderedDict from rhodecode.lib import safe_unicode log = logging.getLogger(__name__)
--- a/rhodecode/controllers/changelog.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/changelog.py Sun Oct 09 23:49:00 2011 +0200 @@ -25,18 +25,13 @@ import logging -try: - import json -except ImportError: - #python 2.5 compatibility - import simplejson as json - from mercurial import graphmod from pylons import request, session, tmpl_context as c from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render from rhodecode.lib.helpers import RepoPage +from rhodecode.lib.compat import json log = logging.getLogger(__name__)
--- a/rhodecode/controllers/changeset.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/changeset.py Sun Oct 09 23:49:00 2011 +0200 @@ -34,7 +34,7 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render from rhodecode.lib.utils import EmptyChangeset -from rhodecode.lib.odict import OrderedDict +from rhodecode.lib.compat import OrderedDict from vcs.exceptions import RepositoryError, ChangesetError, \ ChangesetDoesNotExistError
--- a/rhodecode/controllers/files.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/files.py Sun Oct 09 23:49:00 2011 +0200 @@ -316,6 +316,13 @@ filename = file_obj.filename content = file_obj.file + #TODO: REMOVE THIS !! + ################################ + import ipdb;ipdb.set_trace() + print 'setting ipdb debuggin for rhodecode.controllers.files.FilesController.add' + ################################ + + node_path = os.path.join(location, filename) author = self.rhodecode_user.full_contact
--- a/rhodecode/controllers/login.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/login.py Sun Oct 09 23:49:00 2011 +0200 @@ -64,8 +64,7 @@ c.form_result = login_form.to_python(dict(request.POST)) #form checks for username/password, now we're authenticated username = c.form_result['username'] - user = User.by_username(username, - case_insensitive=True) + user = User.get_by_username(username, case_insensitive=True) auth_user = AuthUser(user.user_id) auth_user.set_authenticated() session['rhodecode_user'] = auth_user @@ -95,8 +94,7 @@ def register(self): user_model = UserModel() c.auto_active = False - for perm in user_model.get_by_username('default', - cache=False).user_perms: + for perm in User.get_by_username('default').user_perms: if perm.permission.permission_name == 'hg.register.auto_activate': c.auto_active = True break
--- a/rhodecode/controllers/summary.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/summary.py Sun Oct 09 23:49:00 2011 +0200 @@ -39,18 +39,13 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render from rhodecode.lib.utils import EmptyChangeset -from rhodecode.lib.odict import OrderedDict from rhodecode.lib.celerylib import run_task from rhodecode.lib.celerylib.tasks import get_commits_stats, \ LANGUAGES_EXTENSIONS_MAP from rhodecode.lib.helpers import RepoPage +from rhodecode.lib.compat import json, OrderedDict -try: - import json -except ImportError: - #python 2.5 compatibility - import simplejson as json log = logging.getLogger(__name__) @@ -139,9 +134,9 @@ c.commit_data = stats.commit_activity c.overview_data = stats.commit_activity_combined - lang_stats = [(x, {"count": y, + lang_stats = ((x, {"count": y, "desc": LANGUAGES_EXTENSIONS_MAP.get(x)}) - for x, y in lang_stats_d.items()] + for x, y in lang_stats_d.items()) c.trending_languages = json.dumps(OrderedDict( sorted(lang_stats, reverse=True,
--- a/rhodecode/controllers/tags.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/controllers/tags.py Sun Oct 09 23:49:00 2011 +0200 @@ -28,7 +28,7 @@ from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.odict import OrderedDict +from rhodecode.lib.compat import OrderedDict log = logging.getLogger(__name__)
--- a/rhodecode/lib/__init__.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/__init__.py Sun Oct 09 23:49:00 2011 +0200 @@ -23,14 +23,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - -try: - import json -except ImportError: - #python 2.5 compatibility - import simplejson as json - - def __get_lem(): from pygments import lexers from string import lower @@ -157,44 +149,69 @@ return hashlib.sha1(username + salt).hexdigest() -def safe_unicode(_str, from_encoding='utf8'): +def safe_unicode(str_, from_encoding='utf8'): """ - safe unicode function. In case of UnicodeDecode error we try to return - unicode with errors replaceed + safe unicode function. Does few trick to turn str_ into unicode + + In case of UnicodeDecode error we try to return it with encoding detected + by chardet library if it fails fallback to unicode with errors replaced - :param _str: string to decode + :param str_: string to decode :rtype: unicode :returns: unicode object """ + if isinstance(str_, unicode): + return str_ - if isinstance(_str, unicode): - return _str + try: + return unicode(str_) + except UnicodeDecodeError: + pass + + try: + return unicode(str_, from_encoding) + except UnicodeDecodeError: + pass try: - u_str = unicode(_str, from_encoding) - except UnicodeDecodeError: - u_str = unicode(_str, from_encoding, 'replace') - - return u_str - + import chardet + encoding = chardet.detect(str_)['encoding'] + if encoding is None: + raise Exception() + return str_.decode(encoding) + except (ImportError, UnicodeDecodeError, Exception): + return unicode(str_, from_encoding, 'replace') -def safe_str(_unicode, to_encoding='utf8'): +def safe_str(unicode_, to_encoding='utf8'): """ - safe str function. In case of UnicodeEncode error we try to return - str with errors replaceed + safe str function. Does few trick to turn unicode_ into string + + In case of UnicodeEncodeError we try to return it with encoding detected + by chardet library if it fails fallback to string with errors replaced - :param _unicode: unicode to encode + :param unicode_: unicode to encode :rtype: str :returns: str object """ - if isinstance(_unicode, str): - return _unicode + if isinstance(unicode_, str): + return unicode_ + + try: + return unicode_.encode(to_encoding) + except UnicodeEncodeError: + pass try: - safe_str = str(_unicode) - except UnicodeEncodeError: - safe_str = _unicode.encode(to_encoding, 'replace') + import chardet + encoding = chardet.detect(unicode_)['encoding'] + print encoding + if encoding is None: + raise UnicodeEncodeError() + + return unicode_.encode(encoding) + except (ImportError, UnicodeEncodeError): + return unicode_.encode(to_encoding, 'replace') return safe_str @@ -361,4 +378,4 @@ except RepositoryError: from rhodecode.lib.utils import EmptyChangeset cs = EmptyChangeset(requested_revision=rev) - return cs \ No newline at end of file + return cs
--- a/rhodecode/lib/auth.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/auth.py Sun Oct 09 23:49:00 2011 +0200 @@ -6,8 +6,8 @@ authentication and permission libraries :created_on: Apr 4, 2010 - :copyright: (c) 2010 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ from rhodecode.model import meta from rhodecode.model.user import UserModel -from rhodecode.model.db import Permission, RhodeCodeSettings +from rhodecode.model.db import Permission, RhodeCodeSettings, User log = logging.getLogger(__name__) @@ -151,7 +151,7 @@ """ user_model = UserModel() - user = user_model.get_by_username(username, cache=False) + user = User.get_by_username(username) log.debug('Authenticating user using RhodeCode account') if user is not None and not user.ldap_dn: @@ -170,8 +170,7 @@ else: log.debug('Regular authentication failed') - user_obj = user_model.get_by_username(username, cache=False, - case_insensitive=True) + user_obj = User.get_by_username(username, case_insensitive=True) if user_obj is not None and not user_obj.ldap_dn: log.debug('this user already exists as non ldap') @@ -252,7 +251,7 @@ def propagate_data(self): user_model = UserModel() - self.anonymous_user = user_model.get_by_username('default', cache=True) + self.anonymous_user = User.get_by_username('default') is_user_loaded = False if self._api_key and self._api_key != self.anonymous_user.api_key: #try go get user by api key @@ -269,7 +268,7 @@ self.username = self.username.partition('@')[0] log.debug('Auth User lookup by USER NAME %s', self.username) - dbuser = user_model.get_by_username(self.username) + dbuser = User.get_by_username(self.username) if dbuser is not None and dbuser.active: for k, v in dbuser.get_dict().items(): setattr(self, k, v)
--- a/rhodecode/lib/backup_manager.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/backup_manager.py Sun Oct 09 23:49:00 2011 +0200 @@ -7,8 +7,8 @@ repositories and send it to backup server using RSA key via ssh. :created_on: Feb 28, 2010 - :copyright: (c) 2010 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by
--- a/rhodecode/lib/base.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/base.py Sun Oct 09 23:49:00 2011 +0200 @@ -76,7 +76,7 @@ super(BaseRepoController, self).__before__() if c.repo_name: - c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name) + c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name) c.rhodecode_repo = c.rhodecode_db_repo.scm_instance if c.rhodecode_repo is None:
--- a/rhodecode/lib/celerylib/tasks.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/celerylib/tasks.py Sun Oct 09 23:49:00 2011 +0200 @@ -43,7 +43,8 @@ from rhodecode.lib.helpers import person from rhodecode.lib.smtp_mailer import SmtpMailer from rhodecode.lib.utils import add_cache -from rhodecode.lib.odict import OrderedDict +from rhodecode.lib.compat import json, OrderedDict + from rhodecode.model import init_model from rhodecode.model import meta from rhodecode.model.db import RhodeCodeUi, Statistics, Repository @@ -54,11 +55,7 @@ add_cache(config) -try: - import json -except ImportError: - #python 2.5 compatibility - import simplejson as json + __all__ = ['whoosh_index', 'get_commits_stats', 'reset_user_password', 'send_email']
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/compat.py Sun Oct 09 23:49:00 2011 +0200 @@ -0,0 +1,360 @@ +# -*- coding: utf-8 -*- +""" + rhodecode.lib.compat + ~~~~~~~~~~~~~~~~~~~~ + + Python backward compatibility functions and common libs + + + :created_on: Oct 7, 2011 + :author: marcink + :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. +""" +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +#============================================================================== +# json +#============================================================================== +try: + import json +except ImportError: + import simplejson as json + + +#============================================================================== +# izip_longest +#============================================================================== +try: + from itertools import izip_longest +except ImportError: + import itertools + + def izip_longest(*args, **kwds): # noqa + fillvalue = kwds.get("fillvalue") + + def sentinel(counter=([fillvalue] * (len(args) - 1)).pop): + yield counter() # yields the fillvalue, or raises IndexError + + fillers = itertools.repeat(fillvalue) + iters = [itertools.chain(it, sentinel(), fillers) + for it in args] + try: + for tup in itertools.izip(*iters): + yield tup + except IndexError: + pass + + +#============================================================================== +# OrderedDict +#============================================================================== + +# Python Software Foundation License + +# XXX: it feels like using the class with "is" and "is not" instead of "==" and +# "!=" should be faster. +class _Nil(object): + + def __repr__(self): + return "nil" + + def __eq__(self, other): + if (isinstance(other, _Nil)): + return True + else: + return NotImplemented + + def __ne__(self, other): + if (isinstance(other, _Nil)): + return False + else: + return NotImplemented + +_nil = _Nil() + +class _odict(object): + """Ordered dict data structure, with O(1) complexity for dict operations + that modify one element. + + Overwriting values doesn't change their original sequential order. + """ + + def _dict_impl(self): + return None + + def __init__(self, data=(), **kwds): + """This doesn't accept keyword initialization as normal dicts to avoid + a trap - inside a function or method the keyword args are accessible + only as a dict, without a defined order, so their original order is + lost. + """ + if kwds: + raise TypeError("__init__() of ordered dict takes no keyword " + "arguments to avoid an ordering trap.") + self._dict_impl().__init__(self) + # If you give a normal dict, then the order of elements is undefined + if hasattr(data, "iteritems"): + for key, val in data.iteritems(): + self[key] = val + else: + for key, val in data: + self[key] = val + + # Double-linked list header + def _get_lh(self): + dict_impl = self._dict_impl() + if not hasattr(self, '_lh'): + dict_impl.__setattr__(self, '_lh', _nil) + return dict_impl.__getattribute__(self, '_lh') + + def _set_lh(self, val): + self._dict_impl().__setattr__(self, '_lh', val) + + lh = property(_get_lh, _set_lh) + + # Double-linked list tail + def _get_lt(self): + dict_impl = self._dict_impl() + if not hasattr(self, '_lt'): + dict_impl.__setattr__(self, '_lt', _nil) + return dict_impl.__getattribute__(self, '_lt') + + def _set_lt(self, val): + self._dict_impl().__setattr__(self, '_lt', val) + + lt = property(_get_lt, _set_lt) + + def __getitem__(self, key): + return self._dict_impl().__getitem__(self, key)[1] + + def __setitem__(self, key, val): + dict_impl = self._dict_impl() + try: + dict_impl.__getitem__(self, key)[1] = val + except KeyError, e: + new = [dict_impl.__getattribute__(self, 'lt'), val, _nil] + dict_impl.__setitem__(self, key, new) + if dict_impl.__getattribute__(self, 'lt') == _nil: + dict_impl.__setattr__(self, 'lh', key) + else: + dict_impl.__getitem__( + self, dict_impl.__getattribute__(self, 'lt'))[2] = key + dict_impl.__setattr__(self, 'lt', key) + + def __delitem__(self, key): + dict_impl = self._dict_impl() + pred, _ , succ = self._dict_impl().__getitem__(self, key) + if pred == _nil: + dict_impl.__setattr__(self, 'lh', succ) + else: + dict_impl.__getitem__(self, pred)[2] = succ + if succ == _nil: + dict_impl.__setattr__(self, 'lt', pred) + else: + dict_impl.__getitem__(self, succ)[0] = pred + dict_impl.__delitem__(self, key) + + def __contains__(self, key): + return key in self.keys() + + def __len__(self): + return len(self.keys()) + + def __str__(self): + pairs = ("%r: %r" % (k, v) for k, v in self.iteritems()) + return "{%s}" % ", ".join(pairs) + + def __repr__(self): + if self: + pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems()) + return "odict([%s])" % ", ".join(pairs) + else: + return "odict()" + + def get(self, k, x=None): + if k in self: + return self._dict_impl().__getitem__(self, k)[1] + else: + return x + + def __iter__(self): + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lh') + while curr_key != _nil: + yield curr_key + curr_key = dict_impl.__getitem__(self, curr_key)[2] + + iterkeys = __iter__ + + def keys(self): + return list(self.iterkeys()) + + def itervalues(self): + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lh') + while curr_key != _nil: + _, val, curr_key = dict_impl.__getitem__(self, curr_key) + yield val + + def values(self): + return list(self.itervalues()) + + def iteritems(self): + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lh') + while curr_key != _nil: + _, val, next_key = dict_impl.__getitem__(self, curr_key) + yield curr_key, val + curr_key = next_key + + def items(self): + return list(self.iteritems()) + + def sort(self, cmp=None, key=None, reverse=False): + items = [(k, v) for k, v in self.items()] + if cmp is not None: + items = sorted(items, cmp=cmp) + elif key is not None: + items = sorted(items, key=key) + else: + items = sorted(items, key=lambda x: x[1]) + if reverse: + items.reverse() + self.clear() + self.__init__(items) + + def clear(self): + dict_impl = self._dict_impl() + dict_impl.clear(self) + dict_impl.__setattr__(self, 'lh', _nil) + dict_impl.__setattr__(self, 'lt', _nil) + + def copy(self): + return self.__class__(self) + + def update(self, data=(), **kwds): + if kwds: + raise TypeError("update() of ordered dict takes no keyword " + "arguments to avoid an ordering trap.") + if hasattr(data, "iteritems"): + data = data.iteritems() + for key, val in data: + self[key] = val + + def setdefault(self, k, x=None): + try: + return self[k] + except KeyError: + self[k] = x + return x + + def pop(self, k, x=_nil): + try: + val = self[k] + del self[k] + return val + except KeyError: + if x == _nil: + raise + return x + + def popitem(self): + try: + dict_impl = self._dict_impl() + key = dict_impl.__getattribute__(self, 'lt') + return key, self.pop(key) + except KeyError: + raise KeyError("'popitem(): ordered dictionary is empty'") + + def riterkeys(self): + """To iterate on keys in reversed order. + """ + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lt') + while curr_key != _nil: + yield curr_key + curr_key = dict_impl.__getitem__(self, curr_key)[0] + + __reversed__ = riterkeys + + def rkeys(self): + """List of the keys in reversed order. + """ + return list(self.riterkeys()) + + def ritervalues(self): + """To iterate on values in reversed order. + """ + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lt') + while curr_key != _nil: + curr_key, val, _ = dict_impl.__getitem__(self, curr_key) + yield val + + def rvalues(self): + """List of the values in reversed order. + """ + return list(self.ritervalues()) + + def riteritems(self): + """To iterate on (key, value) in reversed order. + """ + dict_impl = self._dict_impl() + curr_key = dict_impl.__getattribute__(self, 'lt') + while curr_key != _nil: + pred_key, val, _ = dict_impl.__getitem__(self, curr_key) + yield curr_key, val + curr_key = pred_key + + def ritems(self): + """List of the (key, value) in reversed order. + """ + return list(self.riteritems()) + + def firstkey(self): + if self: + return self._dict_impl().__getattribute__(self, 'lh') + else: + raise KeyError("'firstkey(): ordered dictionary is empty'") + + def lastkey(self): + if self: + return self._dict_impl().__getattribute__(self, 'lt') + else: + raise KeyError("'lastkey(): ordered dictionary is empty'") + + def as_dict(self): + return self._dict_impl()(self.items()) + + def _repr(self): + """_repr(): low level repr of the whole data contained in the odict. + Useful for debugging. + """ + dict_impl = self._dict_impl() + form = "odict low level repr lh,lt,data: %r, %r, %s" + return form % (dict_impl.__getattribute__(self, 'lh'), + dict_impl.__getattribute__(self, 'lt'), + dict_impl.__repr__(self)) + +class OrderedDict(_odict, dict): + + def _dict_impl(self): + return dict + + +#============================================================================== +# OrderedSet +#============================================================================== +from sqlalchemy.util import OrderedSet
--- a/rhodecode/lib/db_manage.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/db_manage.py Sun Oct 09 23:49:00 2011 +0200 @@ -160,7 +160,9 @@ def step_3(self): print ('Adding additional settings into RhodeCode db') self.klass.fix_settings() - + print ('Adding ldap defaults') + self.klass.create_ldap_options(skip_existing=True) + upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1) #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE @@ -307,7 +309,7 @@ self.sa.rollback() raise - def create_ldap_options(self): + def create_ldap_options(self,skip_existing=False): """Creates ldap settings""" try: @@ -319,6 +321,9 @@ ('ldap_attr_login', ''), ('ldap_attr_firstname', ''), ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]: + if skip_existing and RhodeCodeSettings.get_by_name(k) != None: + log.debug('Skipping option %s' % k) + continue setting = RhodeCodeSettings(k, v) self.sa.add(setting) self.sa.commit() @@ -411,42 +416,30 @@ def create_user(self, username, password, email='', admin=False): log.info('creating administrator user %s', username) - new_user = User() - new_user.username = username - new_user.password = get_crypt_password(password) - new_user.api_key = generate_api_key(username) - new_user.name = 'RhodeCode' - new_user.lastname = 'Admin' - new_user.email = email - new_user.admin = admin - new_user.active = True + + form_data = dict(username=username, + password=password, + active=True, + admin=admin, + name='RhodeCode', + lastname='Admin', + email=email) + User.create(form_data) - try: - self.sa.add(new_user) - self.sa.commit() - except: - self.sa.rollback() - raise def create_default_user(self): log.info('creating default user') #create default user for handling default permissions. - def_user = User() - def_user.username = 'default' - def_user.password = get_crypt_password(str(uuid.uuid1())[:8]) - def_user.api_key = generate_api_key('default') - def_user.name = 'Anonymous' - def_user.lastname = 'User' - def_user.email = 'anonymous@rhodecode.org' - def_user.admin = False - def_user.active = False - try: - self.sa.add(def_user) - self.sa.commit() - except: - self.sa.rollback() - raise + form_data = dict(username='default', + password=str(uuid.uuid1())[:8], + active=False, + admin=False, + name='Anonymous', + lastname='User', + email='anonymous@rhodecode.org') + User.create(form_data) + def create_permissions(self): #module.(access|create|change|delete)_[name] #module.(read|write|owner)
--- a/rhodecode/lib/dbmigrate/versions/003_version_1_2_0.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/dbmigrate/versions/003_version_1_2_0.py Sun Oct 09 23:49:00 2011 +0200 @@ -76,6 +76,14 @@ #========================================================================== from rhodecode.model.db import Repository + #ADD clone_uri column# + + clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, + assert_unicode=None), + nullable=True, unique=False, default=None) + + clone_uri.create(Repository().__table__) + #ADD downloads column# enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True) enable_downloads.create(Repository().__table__) @@ -92,21 +100,16 @@ group_id.create(Repository().__table__) - #ADD clone_uri column# - - clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, - assert_unicode=None), - nullable=True, unique=False, default=None) - - clone_uri.create(Repository().__table__) - - #========================================================================== # Upgrade of `user_followings` table #========================================================================== - follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) - follows_from.create(Repository().__table__) + from rhodecode.model.db import UserFollowing + + follows_from = Column('follows_from', DateTime(timezone=False), + nullable=True, unique=None, + default=datetime.datetime.now) + follows_from.create(UserFollowing().__table__) return
--- a/rhodecode/lib/exceptions.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/exceptions.py Sun Oct 09 23:49:00 2011 +0200 @@ -6,8 +6,8 @@ Set of custom exceptions used in RhodeCode :created_on: Nov 17, 2010 - :copyright: (c) 2010 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by
--- a/rhodecode/lib/middleware/simplegit.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/middleware/simplegit.py Sun Oct 09 23:49:00 2011 +0200 @@ -44,11 +44,11 @@ get_tagged=self.get_tagged) # Do they want any objects? - if len(objects_iter) == 0: + if objects_iter is None or len(objects_iter) == 0: return self.progress("counting objects: %d, done.\n" % len(objects_iter)) - dulserver.write_pack_data(dulserver.ProtocolFile(None, write), + dulserver.write_pack_objects(dulserver.ProtocolFile(None, write), objects_iter, len(objects_iter)) messages = [] messages.append('thank you for using rhodecode') @@ -71,8 +71,8 @@ from rhodecode.lib import safe_str from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware -from rhodecode.lib.utils import invalidate_cache, check_repo_fast -from rhodecode.model.user import UserModel +from rhodecode.lib.utils import invalidate_cache, is_valid_repo +from rhodecode.model.db import User from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError @@ -96,12 +96,10 @@ def __init__(self, application, config): self.application = application self.config = config - #authenticate this git request using + # base path of repo locations + self.basepath = self.config['base_path'] + #authenticate this mercurial request using authfunc self.authenticate = AuthBasicAuthenticator('', authfunc) - self.ipaddr = '0.0.0.0' - self.repo_name = None - self.username = None - self.action = None def __call__(self, environ, start_response): if not is_git(environ): @@ -109,31 +107,34 @@ proxy_key = 'HTTP_X_REAL_IP' def_key = 'REMOTE_ADDR' - self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) + ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) + username = None # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== + # EXTRACT REPOSITORY NAME FROM ENV + #====================================================================== + try: + repo_name = self.__get_repository(environ) + log.debug('Extracted repo name is %s' % repo_name) + except: + return HTTPInternalServerError()(environ, start_response) + + #====================================================================== # GET ACTION PULL or PUSH #====================================================================== - self.action = self.__get_action(environ) - try: - #================================================================== - # GET REPOSITORY NAME - #================================================================== - self.repo_name = self.__get_repository(environ) - except: - return HTTPInternalServerError()(environ, start_response) + action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== - if self.action in ['pull', 'push']: + if action in ['pull', 'push']: anonymous_user = self.__get_user('default') - self.username = anonymous_user.username - anonymous_perm = self.__check_permission(self.action, + username = anonymous_user.username + anonymous_perm = self.__check_permission(action, anonymous_user, - self.repo_name) + repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: @@ -162,56 +163,66 @@ # BASIC AUTH #============================================================== - if self.action in ['pull', 'push']: + if action in ['pull', 'push']: username = REMOTE_USER(environ) try: user = self.__get_user(username) - self.username = user.username + username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository - perm = self.__check_permission(self.action, user, - self.repo_name) + perm = self.__check_permission(action, user, + repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) - self.extras = {'ip': self.ipaddr, - 'username': self.username, - 'action': self.action, - 'repository': self.repo_name} + extras = {'ip': ipaddr, + 'username': username, + 'action': action, + 'repository': repo_name} #=================================================================== # GIT REQUEST HANDLING #=================================================================== - self.basepath = self.config['base_path'] - self.repo_path = os.path.join(self.basepath, self.repo_name) - #quick check if that dir exists... - if check_repo_fast(self.repo_name, self.basepath): + + repo_path = safe_str(os.path.join(self.basepath, repo_name)) + log.debug('Repository path is %s' % repo_path) + + # quick check if that dir exists... + if is_valid_repo(repo_name, self.basepath) is False: return HTTPNotFound()(environ, start_response) + try: - app = self.__make_app() - except: + #invalidate cache on push + if action == 'push': + self.__invalidate_cache(repo_name) + + app = self.__make_app(repo_name, repo_path) + return app(environ, start_response) + except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) - #invalidate cache on push - if self.action == 'push': - self.__invalidate_cache(self.repo_name) + def __make_app(self, repo_name, repo_path): + """ + Make an wsgi application using dulserver + + :param repo_name: name of the repository + :param repo_path: full path to the repository + """ - return app(environ, start_response) - - def __make_app(self): - _d = {'/' + self.repo_name: Repo(self.repo_path)} + _d = {'/' + repo_name: Repo(repo_path)} backend = dulserver.DictBackend(_d) gitserve = HTTPGitApplication(backend) return gitserve def __check_permission(self, action, user, repo_name): - """Checks permissions using action (push/pull) user and repository + """ + Checks permissions using action (push/pull) user and repository name :param action: push or pull action @@ -235,7 +246,8 @@ return True def __get_repository(self, environ): - """Get's repository name out of PATH_INFO header + """ + Get's repository name out of PATH_INFO header :param environ: environ where PATH_INFO is stored """ @@ -250,7 +262,7 @@ return repo_name def __get_user(self, username): - return UserModel().get_by_username(username, cache=True) + return User.get_by_username(username) def __get_action(self, environ): """Maps git request commands into a pull or push command. @@ -274,3 +286,4 @@ invalidate the cache to see the changes right away but only for push requests""" invalidate_cache('get_repo_cached_%s' % repo_name) +
--- a/rhodecode/lib/middleware/simplehg.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/middleware/simplehg.py Sun Oct 09 23:49:00 2011 +0200 @@ -29,8 +29,7 @@ import traceback from mercurial.error import RepoError -from mercurial.hgweb import hgweb -from mercurial.hgweb.request import wsgiapplication +from mercurial.hgweb import hgweb_mod from paste.auth.basic import AuthBasicAuthenticator from paste.httpheaders import REMOTE_USER, AUTH_TYPE @@ -38,8 +37,8 @@ from rhodecode.lib import safe_str from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware from rhodecode.lib.utils import make_ui, invalidate_cache, \ - check_repo_fast, ui_sections -from rhodecode.model.user import UserModel + is_valid_repo, ui_sections +from rhodecode.model.db import User from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError @@ -61,12 +60,11 @@ def __init__(self, application, config): self.application = application self.config = config + # base path of repo locations + self.basepath = self.config['base_path'] #authenticate this mercurial request using authfunc self.authenticate = AuthBasicAuthenticator('', authfunc) self.ipaddr = '0.0.0.0' - self.repo_name = None - self.username = None - self.action = None def __call__(self, environ, start_response): if not is_mercurial(environ): @@ -74,31 +72,35 @@ proxy_key = 'HTTP_X_REAL_IP' def_key = 'REMOTE_ADDR' - self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) + ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) + # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== + # EXTRACT REPOSITORY NAME FROM ENV + #====================================================================== + try: + repo_name = environ['REPO_NAME'] = self.__get_repository(environ) + log.debug('Extracted repo name is %s' % repo_name) + except: + return HTTPInternalServerError()(environ, start_response) + + #====================================================================== # GET ACTION PULL or PUSH #====================================================================== - self.action = self.__get_action(environ) - try: - #================================================================== - # GET REPOSITORY NAME - #================================================================== - self.repo_name = self.__get_repository(environ) - except: - return HTTPInternalServerError()(environ, start_response) + action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== - if self.action in ['pull', 'push']: + if action in ['pull', 'push']: anonymous_user = self.__get_user('default') - self.username = anonymous_user.username - anonymous_perm = self.__check_permission(self.action, + + username = anonymous_user.username + anonymous_perm = self.__check_permission(action, anonymous_user, - self.repo_name) + repo_name) if anonymous_perm is not True or anonymous_user.active is False: if anonymous_perm is not True: @@ -127,43 +129,52 @@ # BASIC AUTH #============================================================== - if self.action in ['pull', 'push']: + if action in ['pull', 'push']: #Removing realm from username username = REMOTE_USER(environ).partition('@')[0] try: user = self.__get_user(username) if user is None: return HTTPForbidden()(environ, start_response) - self.username = user.username + username = user.username except: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository - perm = self.__check_permission(self.action, user, - self.repo_name) + perm = self.__check_permission(action, user, + repo_name) if perm is not True: return HTTPForbidden()(environ, start_response) - self.extras = {'ip': self.ipaddr, - 'username': self.username, - 'action': self.action, - 'repository': self.repo_name} + extras = {'ip': ipaddr, + 'username': username, + 'action': action, + 'repository': repo_name} #====================================================================== # MERCURIAL REQUEST HANDLING #====================================================================== - environ['PATH_INFO'] = '/' # since we wrap into hgweb, reset the path - self.baseui = make_ui('db') - self.basepath = self.config['base_path'] - self.repo_path = os.path.join(self.basepath, self.repo_name) + + repo_path = safe_str(os.path.join(self.basepath, repo_name)) + log.debug('Repository path is %s' % repo_path) + + baseui = make_ui('db') + self.__inject_extras(repo_path, baseui, extras) + - #quick check if that dir exists... - if check_repo_fast(self.repo_name, self.basepath): + # quick check if that dir exists... + if is_valid_repo(repo_name, self.basepath) is False: return HTTPNotFound()(environ, start_response) + try: - app = wsgiapplication(self.__make_app) + #invalidate cache on push + if action == 'push': + self.__invalidate_cache(repo_name) + + app = self.__make_app(repo_path, baseui, extras) + return app(environ, start_response) except RepoError, e: if str(e).find('not found') != -1: return HTTPNotFound()(environ, start_response) @@ -171,19 +182,12 @@ log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) - #invalidate cache on push - if self.action == 'push': - self.__invalidate_cache(self.repo_name) - - return app(environ, start_response) - - def __make_app(self): + def __make_app(self, repo_name, baseui, extras): """ Make an wsgi application using hgweb, and inject generated baseui instance, additionally inject some extras into ui object """ - self.__inject_extras(self.baseui, self.extras) - return hgweb(str(self.repo_path), baseui=self.baseui) + return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui) def __check_permission(self, action, user, repo_name): @@ -228,7 +232,7 @@ return repo_name def __get_user(self, username): - return UserModel().get_by_username(username, cache=True) + return User.get_by_username(username) def __get_action(self, environ): """ @@ -257,7 +261,7 @@ push requests""" invalidate_cache('get_repo_cached_%s' % repo_name) - def __inject_extras(self, baseui, extras={}): + def __inject_extras(self, repo_path, baseui, extras={}): """ Injects some extra params into baseui instance @@ -267,7 +271,10 @@ :param extras: dict with extra params to put into baseui """ - hgrc = os.path.join(self.repo_path, '.hg', 'hgrc') + hgrc = os.path.join(repo_path, '.hg', 'hgrc') + + # make our hgweb quiet so it doesn't print output + baseui.setconfig('ui', 'quiet', 'true') #inject some additional parameters that will be available in ui #for hooks @@ -281,3 +288,4 @@ for section in ui_sections: for k, v in repoui.configitems(section): baseui.setconfig(section, k, v) +
--- a/rhodecode/lib/odict.py Tue Sep 27 22:20:24 2011 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,291 +0,0 @@ -# Python Software Foundation License - -# XXX: it feels like using the class with "is" and "is not" instead of "==" and -# "!=" should be faster. -class _Nil(object): - - def __repr__(self): - return "nil" - - def __eq__(self, other): - if (isinstance(other, _Nil)): - return True - else: - return NotImplemented - - def __ne__(self, other): - if (isinstance(other, _Nil)): - return False - else: - return NotImplemented - -_nil = _Nil() - -class _odict(object): - """Ordered dict data structure, with O(1) complexity for dict operations - that modify one element. - - Overwriting values doesn't change their original sequential order. - """ - - def _dict_impl(self): - return None - - def __init__(self, data=(), **kwds): - """This doesn't accept keyword initialization as normal dicts to avoid - a trap - inside a function or method the keyword args are accessible - only as a dict, without a defined order, so their original order is - lost. - """ - if kwds: - raise TypeError("__init__() of ordered dict takes no keyword " - "arguments to avoid an ordering trap.") - self._dict_impl().__init__(self) - # If you give a normal dict, then the order of elements is undefined - if hasattr(data, "iteritems"): - for key, val in data.iteritems(): - self[key] = val - else: - for key, val in data: - self[key] = val - - # Double-linked list header - def _get_lh(self): - dict_impl = self._dict_impl() - if not hasattr(self, '_lh'): - dict_impl.__setattr__(self, '_lh', _nil) - return dict_impl.__getattribute__(self, '_lh') - - def _set_lh(self, val): - self._dict_impl().__setattr__(self, '_lh', val) - - lh = property(_get_lh, _set_lh) - - # Double-linked list tail - def _get_lt(self): - dict_impl = self._dict_impl() - if not hasattr(self, '_lt'): - dict_impl.__setattr__(self, '_lt', _nil) - return dict_impl.__getattribute__(self, '_lt') - - def _set_lt(self, val): - self._dict_impl().__setattr__(self, '_lt', val) - - lt = property(_get_lt, _set_lt) - - def __getitem__(self, key): - return self._dict_impl().__getitem__(self, key)[1] - - def __setitem__(self, key, val): - dict_impl = self._dict_impl() - try: - dict_impl.__getitem__(self, key)[1] = val - except KeyError, e: - new = [dict_impl.__getattribute__(self, 'lt'), val, _nil] - dict_impl.__setitem__(self, key, new) - if dict_impl.__getattribute__(self, 'lt') == _nil: - dict_impl.__setattr__(self, 'lh', key) - else: - dict_impl.__getitem__( - self, dict_impl.__getattribute__(self, 'lt'))[2] = key - dict_impl.__setattr__(self, 'lt', key) - - def __delitem__(self, key): - dict_impl = self._dict_impl() - pred, _ , succ = self._dict_impl().__getitem__(self, key) - if pred == _nil: - dict_impl.__setattr__(self, 'lh', succ) - else: - dict_impl.__getitem__(self, pred)[2] = succ - if succ == _nil: - dict_impl.__setattr__(self, 'lt', pred) - else: - dict_impl.__getitem__(self, succ)[0] = pred - dict_impl.__delitem__(self, key) - - def __contains__(self, key): - return key in self.keys() - - def __len__(self): - return len(self.keys()) - - def __str__(self): - pairs = ("%r: %r" % (k, v) for k, v in self.iteritems()) - return "{%s}" % ", ".join(pairs) - - def __repr__(self): - if self: - pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems()) - return "odict([%s])" % ", ".join(pairs) - else: - return "odict()" - - def get(self, k, x=None): - if k in self: - return self._dict_impl().__getitem__(self, k)[1] - else: - return x - - def __iter__(self): - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lh') - while curr_key != _nil: - yield curr_key - curr_key = dict_impl.__getitem__(self, curr_key)[2] - - iterkeys = __iter__ - - def keys(self): - return list(self.iterkeys()) - - def itervalues(self): - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lh') - while curr_key != _nil: - _, val, curr_key = dict_impl.__getitem__(self, curr_key) - yield val - - def values(self): - return list(self.itervalues()) - - def iteritems(self): - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lh') - while curr_key != _nil: - _, val, next_key = dict_impl.__getitem__(self, curr_key) - yield curr_key, val - curr_key = next_key - - def items(self): - return list(self.iteritems()) - - def sort(self, cmp=None, key=None, reverse=False): - items = [(k, v) for k, v in self.items()] - if cmp is not None: - items = sorted(items, cmp=cmp) - elif key is not None: - items = sorted(items, key=key) - else: - items = sorted(items, key=lambda x: x[1]) - if reverse: - items.reverse() - self.clear() - self.__init__(items) - - def clear(self): - dict_impl = self._dict_impl() - dict_impl.clear(self) - dict_impl.__setattr__(self, 'lh', _nil) - dict_impl.__setattr__(self, 'lt', _nil) - - def copy(self): - return self.__class__(self) - - def update(self, data=(), **kwds): - if kwds: - raise TypeError("update() of ordered dict takes no keyword " - "arguments to avoid an ordering trap.") - if hasattr(data, "iteritems"): - data = data.iteritems() - for key, val in data: - self[key] = val - - def setdefault(self, k, x=None): - try: - return self[k] - except KeyError: - self[k] = x - return x - - def pop(self, k, x=_nil): - try: - val = self[k] - del self[k] - return val - except KeyError: - if x == _nil: - raise - return x - - def popitem(self): - try: - dict_impl = self._dict_impl() - key = dict_impl.__getattribute__(self, 'lt') - return key, self.pop(key) - except KeyError: - raise KeyError("'popitem(): ordered dictionary is empty'") - - def riterkeys(self): - """To iterate on keys in reversed order. - """ - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lt') - while curr_key != _nil: - yield curr_key - curr_key = dict_impl.__getitem__(self, curr_key)[0] - - __reversed__ = riterkeys - - def rkeys(self): - """List of the keys in reversed order. - """ - return list(self.riterkeys()) - - def ritervalues(self): - """To iterate on values in reversed order. - """ - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lt') - while curr_key != _nil: - curr_key, val, _ = dict_impl.__getitem__(self, curr_key) - yield val - - def rvalues(self): - """List of the values in reversed order. - """ - return list(self.ritervalues()) - - def riteritems(self): - """To iterate on (key, value) in reversed order. - """ - dict_impl = self._dict_impl() - curr_key = dict_impl.__getattribute__(self, 'lt') - while curr_key != _nil: - pred_key, val, _ = dict_impl.__getitem__(self, curr_key) - yield curr_key, val - curr_key = pred_key - - def ritems(self): - """List of the (key, value) in reversed order. - """ - return list(self.riteritems()) - - def firstkey(self): - if self: - return self._dict_impl().__getattribute__(self, 'lh') - else: - raise KeyError("'firstkey(): ordered dictionary is empty'") - - def lastkey(self): - if self: - return self._dict_impl().__getattribute__(self, 'lt') - else: - raise KeyError("'lastkey(): ordered dictionary is empty'") - - def as_dict(self): - return self._dict_impl()(self.items()) - - def _repr(self): - """_repr(): low level repr of the whole data contained in the odict. - Useful for debugging. - """ - dict_impl = self._dict_impl() - form = "odict low level repr lh,lt,data: %r, %r, %s" - return form % (dict_impl.__getattribute__(self, 'lh'), - dict_impl.__getattribute__(self, 'lt'), - dict_impl.__repr__(self)) - -class OrderedDict(_odict, dict): - - def _dict_impl(self): - return dict
--- a/rhodecode/lib/oset.py Tue Sep 27 22:20:24 2011 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -KEY, PREV, NEXT = range(3) -import collections - -class OrderedSet(collections.MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[PREV] - curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[NEXT] = next - next[PREV] = prev - - def __iter__(self): - end = self.end - curr = end[NEXT] - while curr is not end: - yield curr[KEY] - curr = curr[NEXT] - - def __reversed__(self): - end = self.end - curr = end[PREV] - while curr is not end: - yield curr[KEY] - curr = curr[PREV] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = next(reversed(self)) if last else next(iter(self)) - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) - - def __del__(self): - self.clear() # remove circular references -
--- a/rhodecode/lib/smtp_mailer.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/smtp_mailer.py Sun Oct 09 23:49:00 2011 +0200 @@ -6,9 +6,21 @@ Simple smtp mailer used in RhodeCode :created_on: Sep 13, 2010 - :copyright: (c) 2011 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. import logging import smtplib
--- a/rhodecode/lib/utils.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/lib/utils.py Sun Oct 09 23:49:00 2011 +0200 @@ -33,23 +33,21 @@ from paste.script.command import Command, BadCommand -from UserDict import DictMixin - -from mercurial import ui, config, hg -from mercurial.error import RepoError +from mercurial import ui, config from webhelpers.text import collapse, remove_formatting, strip_tags +from vcs import get_backend from vcs.backends.base import BaseChangeset from vcs.utils.lazy import LazyProperty -from vcs import get_backend +from vcs.utils.helpers import get_scm +from vcs.exceptions import VCSError from rhodecode.model import meta from rhodecode.model.caching_query import FromCache from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \ RhodeCodeSettings from rhodecode.model.repo import RepoModel -from rhodecode.model.user import UserModel log = logging.getLogger(__name__) @@ -111,11 +109,10 @@ sa = meta.Session() try: - um = UserModel() if hasattr(user, 'user_id'): user_obj = user elif isinstance(user, basestring): - user_obj = um.get_by_username(user, cache=False) + user_obj = User.get_by_username(user) else: raise Exception('You have to provide user object or username') @@ -183,37 +180,40 @@ return _get_repos(path) -def check_repo_fast(repo_name, base_path): +def is_valid_repo(repo_name, base_path): """ - Check given path for existence of directory + Returns True if given path is a valid repository False otherwise :param repo_name: :param base_path: - :return False: if this directory is present + :return True: if given path is a valid repository """ - if os.path.isdir(os.path.join(base_path, repo_name)): - return False - return True - - -def check_repo(repo_name, base_path, verify=True): - - repo_path = os.path.join(base_path, repo_name) + full_path = os.path.join(base_path, repo_name) try: - if not check_repo_fast(repo_name, base_path): - return False - r = hg.repository(ui.ui(), repo_path) - if verify: - hg.verify(r) - #here we hnow that repo exists it was verified - log.info('%s repo is already created', repo_name) + get_scm(full_path) + return True + except VCSError: return False - except RepoError: - #it means that there is no valid repo there... - log.info('%s repo is free for creation', repo_name) + +def is_valid_repos_group(repos_group_name, base_path): + """ + Returns True if given path is a repos group False otherwise + + :param repo_name: + :param base_path: + """ + full_path = os.path.join(base_path, repos_group_name) + + # check if it's not a repo + if is_valid_repo(repos_group_name, base_path): + return False + + # check if it's a valid path + if os.path.isdir(full_path): return True + return False def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): while True:
--- a/rhodecode/model/db.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/model/db.py Sun Oct 09 23:49:00 2011 +0200 @@ -38,12 +38,14 @@ from vcs import get_backend from vcs.utils.helpers import get_scm -from vcs.exceptions import RepositoryError, VCSError +from vcs.exceptions import VCSError from vcs.utils.lazy import LazyProperty -from vcs.nodes import FileNode +from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \ + generate_api_key from rhodecode.lib.exceptions import UsersGroupsAssignedException -from rhodecode.lib import str2bool, json, safe_str, get_changeset_safe +from rhodecode.lib.compat import json + from rhodecode.model.meta import Base, Session from rhodecode.model.caching_query import FromCache @@ -279,17 +281,16 @@ return self.__class__.__name__ @classmethod - def by_username(cls, username, case_insensitive=False): + def get_by_username(cls, username, case_insensitive=False): if case_insensitive: - return Session.query(cls).filter(cls.username.like(username)).one() + return Session.query(cls).filter(cls.username.like(username)).scalar() else: - return Session.query(cls).filter(cls.username == username).one() + return Session.query(cls).filter(cls.username == username).scalar() @classmethod def get_by_api_key(cls, api_key): return Session.query(cls).filter(cls.api_key == api_key).one() - def update_lastlogin(self): """Update user lastlogin""" @@ -298,6 +299,25 @@ Session.commit() log.debug('updated user %s lastlogin', self.username) + @classmethod + def create(cls, form_data): + from rhodecode.lib.auth import get_crypt_password + + try: + new_user = cls() + for k, v in form_data.items(): + if k == 'password': + v = get_crypt_password(v) + setattr(new_user, k, v) + + new_user.api_key = generate_api_key(form_data['username']) + Session.add(new_user) + Session.commit() + return new_user + except: + log.error(traceback.format_exc()) + Session.rollback() + raise class UserLog(Base, BaseModel): __tablename__ = 'user_logs' @@ -362,6 +382,7 @@ Session.add(new_users_group) Session.commit() + return new_users_group except: log.error(traceback.format_exc()) Session.rollback() @@ -465,7 +486,7 @@ self.repo_id, self.repo_name) @classmethod - def by_repo_name(cls, repo_name): + def get_by_repo_name(cls, repo_name): q = Session.query(cls).filter(cls.repo_name == repo_name) q = q.options(joinedload(Repository.fork))\ @@ -478,6 +499,17 @@ def get_repo_forks(cls, repo_id): return Session.query(cls).filter(Repository.fork_id == repo_id) + @classmethod + def base_path(cls): + """ + Returns base path when all repos are stored + + :param cls: + """ + q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/') + q.options(FromCache("sql_cache_short", "repository_repo_path")) + return q.one().ui_value + @property def just_name(self): return self.repo_name.split(os.sep)[-1] @@ -549,6 +581,19 @@ return baseui + @classmethod + def is_valid(cls, repo_name): + """ + returns True if given repo name is a valid filesystem repository + + @param cls: + @param repo_name: + """ + from rhodecode.lib.utils import is_valid_repo + + return is_valid_repo(repo_name, cls.base_path()) + + #========================================================================== # SCM PROPERTIES #==========================================================================
--- a/rhodecode/model/forms.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/model/forms.py Sun Oct 09 23:49:00 2011 +0200 @@ -32,6 +32,7 @@ from pylons.i18n.translation import _ from webhelpers.pylonslib.secure_form import authentication_token +from rhodecode.config.routing import ADMIN_PREFIX from rhodecode.lib.utils import repo_name_slug from rhodecode.lib.auth import authenticate, get_crypt_password from rhodecode.lib.exceptions import LdapImportError @@ -70,8 +71,7 @@ old_un = UserModel().get(old_data.get('user_id')).username if old_un != value or not edit: - if UserModel().get_by_username(value, cache=False, - case_insensitive=True): + if User.get_by_username(value, case_insensitive=True): raise formencode.Invalid(_('This username already ' 'exists') , value, state) @@ -206,7 +206,7 @@ def validate_python(self, value, state): password = value['password'] username = value['username'] - user = UserModel().get_by_username(username) + user = User.get_by_username(username) if authenticate(username, password): return value @@ -241,7 +241,7 @@ repo_name = value.get('repo_name') slug = repo_name_slug(repo_name) - if slug in ['_admin', '']: + if slug in [ADMIN_PREFIX, '']: e_dict = {'repo_name': _('This repository name is disallowed')} raise formencode.Invalid('', value, state, error_dict=e_dict) @@ -283,6 +283,19 @@ def ValidForkName(): class _ValidForkName(formencode.validators.FancyValidator): def to_python(self, value, state): + + repo_name = value.get('fork_name') + + slug = repo_name_slug(repo_name) + if slug in [ADMIN_PREFIX, '']: + e_dict = {'repo_name': _('This repository name is disallowed')} + raise formencode.Invalid('', value, state, error_dict=e_dict) + + if RepoModel().get_by_repo_name(repo_name): + e_dict = {'fork_name':_('This repository ' + 'already exists')} + raise formencode.Invalid('', value, state, + error_dict=e_dict) return value return _ValidForkName
--- a/rhodecode/model/repo.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/model/repo.py Sun Oct 09 23:49:00 2011 +0200 @@ -102,7 +102,7 @@ for member, perm, member_type in form_data['perms_updates']: if member_type == 'user': r2p = self.sa.query(RepoToPerm)\ - .filter(RepoToPerm.user == User.by_username(member))\ + .filter(RepoToPerm.user == User.get_by_username(member))\ .filter(RepoToPerm.repository == cur_repo)\ .one() @@ -127,7 +127,7 @@ if member_type == 'user': r2p = RepoToPerm() r2p.repository = cur_repo - r2p.user = User.by_username(member) + r2p.user = User.get_by_username(member) r2p.permission = self.sa.query(Permission)\ .filter(Permission. @@ -147,7 +147,7 @@ #update current repo for k, v in form_data.items(): if k == 'user': - cur_repo.user = User.by_username(v) + cur_repo.user = User.get_by_username(v) elif k == 'repo_name': cur_repo.repo_name = form_data['repo_name_full'] elif k == 'repo_group': @@ -192,6 +192,9 @@ if k == 'repo_group': k = 'group_id' + if k == 'description': + v = v or repo_name + setattr(new_repo, k, v) if fork: @@ -205,8 +208,7 @@ #create default permission repo_to_perm = RepoToPerm() default = 'repository.read' - for p in UserModel(self.sa).get_by_username('default', - cache=False).user_perms: + for p in User.get_by_username('default').user_perms: if p.permission.permission_name.startswith('repository.'): default = p.permission.permission_name break @@ -218,8 +220,7 @@ .one().permission_id repo_to_perm.repository = new_repo - repo_to_perm.user_id = UserModel(self.sa)\ - .get_by_username('default', cache=False).user_id + repo_to_perm.user_id = User.get_by_username('default').user_id self.sa.add(repo_to_perm) @@ -301,7 +302,7 @@ :param parent_id: :param clone_uri: """ - from rhodecode.lib.utils import check_repo + from rhodecode.lib.utils import is_valid_repo if new_parent_id: paths = Group.get(new_parent_id).full_path.split(Group.url_sep()) @@ -312,7 +313,7 @@ repo_path = os.path.join(*map(lambda x:safe_str(x), [self.repos_path, new_parent_path, repo_name])) - if check_repo(repo_path, self.repos_path): + if is_valid_repo(repo_path, self.repos_path) is False: log.info('creating repo %s in %s @ %s', repo_name, repo_path, clone_uri) backend = get_backend(alias) @@ -332,8 +333,8 @@ old_path = os.path.join(self.repos_path, old) new_path = os.path.join(self.repos_path, new) if os.path.isdir(new_path): - raise Exception('Was trying to rename to already existing dir %s', - new_path) + raise Exception('Was trying to rename to already existing dir %s' \ + % new_path) shutil.move(old_path, new_path) def __delete_repo(self, repo):
--- a/rhodecode/model/scm.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/model/scm.py Sun Oct 09 23:49:00 2011 +0200 @@ -22,7 +22,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import time import traceback import logging @@ -41,9 +40,8 @@ from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \ action_logger, EmptyChangeset from rhodecode.model import BaseModel -from rhodecode.model.user import UserModel from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ - UserFollowing, UserLog + UserFollowing, UserLog, User log = logging.getLogger(__name__) @@ -283,7 +281,7 @@ return f is not None def is_following_user(self, username, user_id, cache=False): - u = UserModel(self.sa).get_by_username(username) + u = User.get_by_username(username) f = self.sa.query(UserFollowing)\ .filter(UserFollowing.follows_user == u)\ @@ -293,20 +291,24 @@ def get_followers(self, repo_id): if not isinstance(repo_id, int): - repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id') + repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id') return self.sa.query(UserFollowing)\ .filter(UserFollowing.follows_repo_id == repo_id).count() def get_forks(self, repo_id): if not isinstance(repo_id, int): - repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id') + repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id') return self.sa.query(Repository)\ .filter(Repository.fork_id == repo_id).count() def pull_changes(self, repo_name, username): - dbrepo = Repository.by_repo_name(repo_name) + dbrepo = Repository.get_by_repo_name(repo_name) + clone_uri = dbrepo.clone_uri + if not clone_uri: + raise Exception("This repository doesn't have a clone uri") + repo = dbrepo.scm_instance try: extras = {'ip': '', @@ -318,13 +320,12 @@ for k, v in extras.items(): repo._repo.ui.setconfig('rhodecode_extras', k, v) - repo.pull(dbrepo.clone_uri) + repo.pull(clone_uri) self.mark_for_invalidation(repo_name) except: log.error(traceback.format_exc()) raise - def commit_change(self, repo, repo_name, cs, user, author, message, content, f_path): @@ -360,12 +361,12 @@ from vcs.backends.git import GitInMemoryChangeset as IMC # decoding here will force that we have proper encoded values # in any other case this will throw exceptions and deny commit - - if isinstance(content,(basestring,)): + + if isinstance(content, (basestring,)): content = safe_str(content) - elif isinstance(content,file): + elif isinstance(content, file): content = content.read() - + message = safe_str(message) path = safe_str(f_path) author = safe_str(author)
--- a/rhodecode/model/user.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/model/user.py Sun Oct 09 23:49:00 2011 +0200 @@ -28,6 +28,7 @@ from pylons.i18n.translation import _ +from rhodecode.lib import safe_unicode from rhodecode.model import BaseModel from rhodecode.model.caching_query import FromCache from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \ @@ -111,7 +112,7 @@ new_user.api_key = generate_api_key(username) new_user.email = attrs['email'] new_user.active = True - new_user.ldap_dn = user_dn + new_user.ldap_dn = safe_unicode(user_dn) new_user.name = attrs['name'] new_user.lastname = attrs['lastname']
--- a/rhodecode/public/css/style.css Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/public/css/style.css Sun Oct 09 23:49:00 2011 +0200 @@ -1466,7 +1466,7 @@ overflow:hidden; text-align:right; margin:0; -padding:10px 14px 3px 5px; +padding:10px 14px 0px 5px; } #quick_login div.form div.links { @@ -2555,7 +2555,7 @@ border-left:1px solid #c6c6c6; border-right:1px solid #DDD; border-bottom:1px solid #c6c6c6; -color:#515151; +color:#515151 !important; outline:none; margin:0; padding:6px 12px;
--- a/rhodecode/tests/functional/test_login.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/tests/functional/test_login.py Sun Oct 09 23:49:00 2011 +0200 @@ -246,7 +246,7 @@ # GOOD KEY - key = User.by_username(username).api_key + key = User.get_by_username(username).api_key response = self.app.get(url(controller='login', action='password_reset_confirmation',
--- a/rhodecode/tests/functional/test_summary.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/tests/functional/test_summary.py Sun Oct 09 23:49:00 2011 +0200 @@ -41,7 +41,7 @@ def _enable_stats(self): - r = Repository.by_repo_name(HG_REPO) + r = Repository.get_by_repo_name(HG_REPO) r.enable_statistics = True self.sa.add(r) self.sa.commit()
--- a/rhodecode/tests/rhodecode_crawler.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/tests/rhodecode_crawler.py Sun Oct 09 23:49:00 2011 +0200 @@ -102,7 +102,7 @@ repo = vcs.get_repo(jn(PROJECT_PATH, PROJECT)) - from rhodecode.lib.oset import OrderedSet + from rhodecode.lib.compat import OrderedSet paths_ = OrderedSet(['']) try:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/tests/test_concurency.py Sun Oct 09 23:49:00 2011 +0200 @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +""" + rhodecode.tests.test_hg_operations + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Test suite for making push/pull operations + + :created_on: Dec 30, 2010 + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. +""" +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys +import shutil +import logging +from os.path import join as jn +from os.path import dirname as dn + +from tempfile import _RandomNameSequence +from subprocess import Popen, PIPE + +from paste.deploy import appconfig +from pylons import config +from sqlalchemy import engine_from_config + +from rhodecode.lib.utils import add_cache +from rhodecode.model import init_model +from rhodecode.model import meta +from rhodecode.model.db import User, Repository +from rhodecode.lib.auth import get_crypt_password + +from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO +from rhodecode.config.environment import load_environment + +rel_path = dn(dn(dn(os.path.abspath(__file__)))) +conf = appconfig('config:development.ini', relative_to=rel_path) +load_environment(conf.global_conf, conf.local_conf) + +add_cache(conf) + +USER = 'test_admin' +PASS = 'test12' +HOST = '127.0.0.1:5000' +DEBUG = True +log = logging.getLogger(__name__) + + +class Command(object): + + def __init__(self, cwd): + self.cwd = cwd + + def execute(self, cmd, *args): + """Runs command on the system with given ``args``. + """ + + command = cmd + ' ' + ' '.join(args) + log.debug('Executing %s' % command) + if DEBUG: + print command + p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd) + stdout, stderr = p.communicate() + if DEBUG: + print stdout, stderr + return stdout, stderr + +def get_session(): + engine = engine_from_config(conf, 'sqlalchemy.db1.') + init_model(engine) + sa = meta.Session() + return sa + + +def create_test_user(force=True): + print 'creating test user' + sa = get_session() + + user = sa.query(User).filter(User.username == USER).scalar() + + if force and user is not None: + print 'removing current user' + for repo in sa.query(Repository).filter(Repository.user == user).all(): + sa.delete(repo) + sa.delete(user) + sa.commit() + + if user is None or force: + print 'creating new one' + new_usr = User() + new_usr.username = USER + new_usr.password = get_crypt_password(PASS) + new_usr.email = 'mail@mail.com' + new_usr.name = 'test' + new_usr.lastname = 'lasttestname' + new_usr.active = True + new_usr.admin = True + sa.add(new_usr) + sa.commit() + + print 'done' + + +def create_test_repo(force=True): + print 'creating test repo' + from rhodecode.model.repo import RepoModel + sa = get_session() + + user = sa.query(User).filter(User.username == USER).scalar() + if user is None: + raise Exception('user not found') + + + repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar() + + if repo is None: + print 'repo not found creating' + + form_data = {'repo_name':HG_REPO, + 'repo_type':'hg', + 'private':False, + 'clone_uri':'' } + rm = RepoModel(sa) + rm.base_path = '/home/hg' + rm.create(form_data, user) + + print 'done' + +def set_anonymous_access(enable=True): + sa = get_session() + user = sa.query(User).filter(User.username == 'default').one() + user.active = enable + sa.add(user) + sa.commit() + +def get_anonymous_access(): + sa = get_session() + return sa.query(User).filter(User.username == 'default').one().active + + +#============================================================================== +# TESTS +#============================================================================== +def test_clone_with_credentials(no_errors=False, repo=HG_REPO): + cwd = path = jn(TESTS_TMP_PATH, repo) + + + try: + shutil.rmtree(path, ignore_errors=True) + os.makedirs(path) + #print 'made dirs %s' % jn(path) + except OSError: + raise + + + clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ + {'user':USER, + 'pass':PASS, + 'host':HOST, + 'cloned_repo':repo, + 'dest':path + _RandomNameSequence().next()} + + stdout, stderr = Command(cwd).execute('hg clone', clone_url) + + if no_errors is False: + assert """adding file changes""" in stdout, 'no messages about cloning' + assert """abort""" not in stderr , 'got error from clone' + +if __name__ == '__main__': + try: + create_test_user(force=False) + + for i in range(int(sys.argv[2])): + test_clone_with_credentials(repo=sys.argv[1]) + + except Exception, e: + sys.exit('stop on %s' % e)
--- a/rhodecode/tests/test_hg_operations.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/tests/test_hg_operations.py Sun Oct 09 23:49:00 2011 +0200 @@ -6,13 +6,28 @@ Test suite for making push/pull operations :created_on: Dec 30, 2010 - :copyright: (c) 2010 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. import os +import time +import sys import shutil import logging + from os.path import join as jn from os.path import dirname as dn @@ -26,7 +41,7 @@ from rhodecode.lib.utils import add_cache from rhodecode.model import init_model from rhodecode.model import meta -from rhodecode.model.db import User, Repository +from rhodecode.model.db import User, Repository, UserLog from rhodecode.lib.auth import get_crypt_password from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO @@ -41,7 +56,8 @@ USER = 'test_admin' PASS = 'test12' HOST = '127.0.0.1:5000' -DEBUG = True +DEBUG = True if sys.argv[1:] else False +print 'DEBUG:', DEBUG log = logging.getLogger(__name__) @@ -64,28 +80,44 @@ print stdout, stderr return stdout, stderr + +def test_wrapp(func): + + def __wrapp(*args, **kwargs): + print '>>>%s' % func.__name__ + try: + res = func(*args, **kwargs) + except Exception, e: + print ('###############\n-' + '--%s failed %s--\n' + '###############\n' % (func.__name__, e)) + sys.exit() + print '++OK++' + return res + return __wrapp + def get_session(): engine = engine_from_config(conf, 'sqlalchemy.db1.') init_model(engine) - sa = meta.Session() + sa = meta.Session return sa def create_test_user(force=True): - print 'creating test user' + print '\tcreating test user' sa = get_session() user = sa.query(User).filter(User.username == USER).scalar() if force and user is not None: - print 'removing current user' + print '\tremoving current user' for repo in sa.query(Repository).filter(Repository.user == user).all(): sa.delete(repo) sa.delete(user) sa.commit() if user is None or force: - print 'creating new one' + print '\tcreating new one' new_usr = User() new_usr.username = USER new_usr.password = get_crypt_password(PASS) @@ -97,7 +129,7 @@ sa.add(new_usr) sa.commit() - print 'done' + print '\tdone' def create_test_repo(force=True): @@ -112,7 +144,7 @@ repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar() if repo is None: - print 'repo not found creating' + print '\trepo not found creating' form_data = {'repo_name':HG_REPO, 'repo_type':'hg', @@ -126,19 +158,27 @@ def set_anonymous_access(enable=True): sa = get_session() user = sa.query(User).filter(User.username == 'default').one() + sa.expire(user) user.active = enable sa.add(user) sa.commit() + sa.remove() + import time;time.sleep(3) + print '\tanonymous access is now:', enable + def get_anonymous_access(): sa = get_session() - return sa.query(User).filter(User.username == 'default').one().active + obj1 = sa.query(User).filter(User.username == 'default').one() + sa.expire(obj1) + return obj1.active #============================================================================== # TESTS #============================================================================== -def test_clone(no_errors=False): +@test_wrapp +def test_clone_with_credentials(no_errors=False): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) try: @@ -148,6 +188,12 @@ except OSError: raise + print '\tchecking if anonymous access is enabled' + anonymous_access = get_anonymous_access() + if anonymous_access: + print '\tenabled, disabling it ' + set_anonymous_access(enable=False) + time.sleep(1) clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ {'user':USER, @@ -163,8 +209,8 @@ assert """abort""" not in stderr , 'got error from clone' - -def test_clone_anonymous_ok(): +@test_wrapp +def test_clone_anonymous(): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) try: @@ -175,11 +221,12 @@ raise - print 'checking if anonymous access is enabled' + print '\tchecking if anonymous access is enabled' anonymous_access = get_anonymous_access() if not anonymous_access: - print 'not enabled, enabling it ' + print '\tnot enabled, enabling it ' set_anonymous_access(enable=True) + time.sleep(1) clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \ {'user':USER, @@ -189,18 +236,16 @@ 'dest':path} stdout, stderr = Command(cwd).execute('hg clone', clone_url) - print stdout, stderr - assert """adding file changes""" in stdout, 'no messages about cloning' assert """abort""" not in stderr , 'got error from clone' #disable if it was enabled if not anonymous_access: - print 'disabling anonymous access' + print '\tdisabling anonymous access' set_anonymous_access(enable=False) - +@test_wrapp def test_clone_wrong_credentials(): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) @@ -211,6 +256,11 @@ except OSError: raise + print '\tchecking if anonymous access is enabled' + anonymous_access = get_anonymous_access() + if anonymous_access: + print '\tenabled, disabling it ' + set_anonymous_access(enable=False) clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \ {'user':USER + 'error', @@ -221,12 +271,14 @@ stdout, stderr = Command(cwd).execute('hg clone', clone_url) - assert """abort: authorization failed""" in stderr , 'no error from wrong credentials' + if not """abort: authorization failed""" in stderr: + raise Exception('Failure') - +@test_wrapp def test_pull(): pass +@test_wrapp def test_push_modify_file(f_name='setup.py'): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name) @@ -239,10 +291,11 @@ Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO)) +@test_wrapp def test_push_new_file(commits=15, with_clone=True): if with_clone: - test_clone(no_errors=True) + test_clone_with_credentials(no_errors=True) cwd = path = jn(TESTS_TMP_PATH, HG_REPO) added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next()) @@ -269,6 +322,7 @@ Command(cwd).execute('hg push --verbose --debug %s' % push_url) +@test_wrapp def test_push_wrong_credentials(): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \ @@ -288,6 +342,7 @@ Command(cwd).execute('hg push %s' % clone_url) +@test_wrapp def test_push_wrong_path(): cwd = path = jn(TESTS_TMP_PATH, HG_REPO) added_file = jn(path, 'somefile.py') @@ -295,7 +350,7 @@ try: shutil.rmtree(path, ignore_errors=True) os.makedirs(path) - print 'made dirs %s' % jn(path) + print '\tmade dirs %s' % jn(path) except OSError: raise @@ -318,20 +373,40 @@ 'dest':jn(TESTS_TMP_PATH, HG_REPO)} stdout, stderr = Command(cwd).execute('hg push %s' % clone_url) - assert """abort: HTTP Error 403: Forbidden""" in stderr + if not """abort: HTTP Error 403: Forbidden""" in stderr: + raise Exception('Failure') + +@test_wrapp +def get_logs(): + sa = get_session() + return len(sa.query(UserLog).all()) + +@test_wrapp +def test_logs(initial): + sa = get_session() + logs = sa.query(UserLog).all() + operations = 7 + if initial + operations != len(logs): + raise Exception("missing number of logs %s vs %s" % (initial, len(logs))) if __name__ == '__main__': create_test_user(force=False) create_test_repo() - #test_push_modify_file() - #test_clone() - #test_clone_anonymous_ok() + + initial_logs = get_logs() - #test_clone_wrong_credentials() +# test_push_modify_file() + test_clone_with_credentials() + test_clone_wrong_credentials() - test_pull() + test_push_new_file(commits=2, with_clone=True) - #test_push_wrong_path() - #test_push_wrong_credentials() + test_clone_anonymous() + test_push_wrong_path() + + + test_push_wrong_credentials() + + test_logs(initial_logs)
--- a/rhodecode/tests/test_libs.py Tue Sep 27 22:20:24 2011 +0300 +++ b/rhodecode/tests/test_libs.py Sun Oct 09 23:49:00 2011 +0200 @@ -7,9 +7,21 @@ Package for testing various lib/helper functions in rhodecode :created_on: Jun 9, 2011 - :copyright: (c) 2011 by marcink. - :license: LICENSE_NAME, see LICENSE_FILE for more details. + :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> + :license: GPLv3, see COPYING for more details. """ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>.
--- a/setup.py Tue Sep 27 22:20:24 2011 +0300 +++ b/setup.py Sun Oct 09 23:49:00 2011 +0200 @@ -12,6 +12,7 @@ requirements = [ "Pylons==1.0.0", "WebHelpers>=1.2", + "formencode==1.2.4", "SQLAlchemy>=0.7.2,<0.8", "Mako>=0.4.2", "pygments>=1.4", @@ -22,12 +23,12 @@ "python-dateutil>=1.5.0,<2.0.0", "dulwich>=0.8.0", "vcs>=0.2.1.dev", - "webob==1.0.8" + "webob==1.0.8" ] dependency_links = [ - "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.1.dev", - "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.1.dev", + "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.2.dev", + "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.2.dev", ] classifiers = ['Development Status :: 4 - Beta',
--- a/test.ini Tue Sep 27 22:20:24 2011 +0300 +++ b/test.ini Sun Oct 09 23:49:00 2011 +0200 @@ -23,6 +23,7 @@ #smtp_password = #smtp_port = #smtp_use_tls = false +#smtp_use_ssl = true [server:main] ##nr of threads to spawn @@ -49,6 +50,7 @@ cut_off_limit = 256000 force_https = false commit_parse_limit = 25 +use_gravatar = true #################################### ### CELERY CONFIG #### @@ -93,7 +95,6 @@ beaker.cache.long_term.type=memory beaker.cache.long_term.expire=36000 - beaker.cache.sql_cache_short.type=memory beaker.cache.sql_cache_short.expire=10 @@ -150,13 +151,13 @@ ### LOGGING CONFIGURATION #### ################################ [loggers] -keys = root, routes, rhodecode, sqlalchemy,beaker,templates +keys = root, routes, rhodecode, sqlalchemy, beaker, templates [handlers] keys = console [formatters] -keys = generic,color_formatter +keys = generic, color_formatter ############# ## LOGGERS ## @@ -167,9 +168,10 @@ [logger_routes] level = ERROR -handlers = console +handlers = qualname = routes.middleware # "level = DEBUG" logs the route matched and routing variables. +propagate = 1 [logger_beaker] level = DEBUG @@ -185,9 +187,9 @@ [logger_rhodecode] level = ERROR -handlers = console +handlers = qualname = rhodecode -propagate = 0 +propagate = 1 [logger_sqlalchemy] level = ERROR @@ -203,7 +205,7 @@ class = StreamHandler args = (sys.stderr,) level = NOTSET -formatter = color_formatter +formatter = generic ################ ## FORMATTERS ##