##// END OF EJS Templates
invalidate the repo list also for online changes. Small fixes in LoginRequired decorator....
marcink -
r194:3d1dd138 default
parent child Browse files
Show More
@@ -1,20 +1,16 b''
1 1 [hooks]
2 2 #to do push with autoupdate
3 3 changegroup = hg update >&2
4 4
5 5 [extensions]
6 6 hgext.highlight=
7 7 #hgk=
8 8
9 9 [web]
10 10 push_ssl = false
11 11 allow_archive = gz zip bz2
12 12 allow_push = *
13 templates=/home/marcink/python_workspace/hg_app/pylons_app/templates/
14 style = monoblue_custom
15 pygments_style = trac
16 staticurl = /hg_static/
17 13 baseurl = /
18 14
19 15 [paths]
20 16 / = /home/marcink/python_workspace/**
@@ -1,98 +1,98 b''
1 1 from datetime import datetime
2 2 from decorator import decorator
3 3 from functools import wraps
4 4 from pylons import session, url
5 5 from pylons.controllers.util import abort, redirect
6 6 from pylons_app.model import meta
7 7 from pylons_app.model.db import Users, UserLogs
8 8 from sqlalchemy.exc import OperationalError
9 9 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
10 10 import crypt
11 11 import logging
12 12 log = logging.getLogger(__name__)
13 13
14 14 def get_crypt_password(password):
15 15 """
16 16 Cryptographic function used for password hashing
17 17 @param password: password to hash
18 18 """
19 19 return crypt.crypt(password, '6a')
20 20
21 21 def authfunc(environ, username, password):
22 22 sa = meta.Session
23 23 password_crypt = get_crypt_password(password)
24 24 try:
25 25 user = sa.query(Users).filter(Users.username == username).one()
26 26 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
27 27 log.error(e)
28 28 user = None
29 29
30 30 if user:
31 31 if user.active:
32 32 if user.username == username and user.password == password_crypt:
33 33 log.info('user %s authenticated correctly', username)
34 34 if environ:
35 35 http_accept = environ.get('HTTP_ACCEPT')
36 36
37 37 if http_accept.startswith('application/mercurial') or \
38 38 environ['PATH_INFO'].find('raw-file') != -1:
39 39 repo = environ['PATH_INFO']
40 40 for qry in environ['QUERY_STRING'].split('&'):
41 41 if qry.startswith('cmd'):
42 42
43 43 try:
44 44 user_log = UserLogs()
45 45 user_log.user_id = user.user_id
46 46 user_log.action = qry
47 47 user_log.repository = repo
48 48 user_log.action_date = datetime.now()
49 49 sa.add(user_log)
50 50 sa.commit()
51 51 log.info('Adding user %s, action %s', username, qry)
52 52 except Exception as e:
53 53 sa.rollback()
54 54 log.error(e)
55 55
56 56 return True
57 57 else:
58 58 log.error('user %s is disabled', username)
59 59
60 60 return False
61 61
62 62 class AuthUser(object):
63 63 """
64 64 A simple object that handles a mercurial username for authentication
65 65 """
66 66 username = 'Empty'
67 67 is_authenticated = False
68 68 is_admin = False
69 69 permissions = set()
70 70 group = set()
71 71
72 72 def __init__(self):
73 73 pass
74 74
75 75 #===============================================================================
76 76 # DECORATORS
77 77 #===============================================================================
78 78 class LoginRequired(object):
79 79 """
80 80 Must be logged in to execute this function else redirect to login page
81 81 """
82 82 def __init__(self):
83 83 pass
84 84
85 85 def __call__(self, func):
86 log.info('Checking login required')
86 user = session.get('hg_app_user', AuthUser())
87 log.info('Checking login required for %s', user.username)
87 88
88 89 @wraps(func)
89 90 def _wrapper(*fargs, **fkwargs):
90 user = session.get('hg_app_user', AuthUser())
91 91 if user.is_authenticated:
92 92 log.info('user %s is authenticated', user.username)
93 93 func(*fargs)
94 94 else:
95 95 logging.info('user %s not authenticated', user.username)
96 96 return redirect(url('login_home'))
97 97
98 98 return _wrapper
@@ -1,86 +1,87 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 #
4 4 # Copyright (c) 2010 marcink. All rights reserved.
5 5 #
6 6 """
7 7 Created on 2010-04-28
8 8
9 9 @author: marcink
10 10 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
11 11 It's implemented with basic auth function
12 12 """
13 13
14 14 from mercurial.hgweb import hgweb
15 15 from mercurial.hgweb.request import wsgiapplication
16 16 from paste.auth.basic import AuthBasicAuthenticator
17 17 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
18 18 from pylons_app.lib.utils import is_mercurial
19 19 from pylons_app.lib.auth import authfunc
20 20 from pylons_app.lib.utils import make_ui, invalidate_cache
21 21 from webob.exc import HTTPNotFound
22 22 import os
23 23
24 24 class SimpleHg(object):
25 25
26 26 def __init__(self, application, config):
27 27 self.application = application
28 28 self.config = config
29 29 #authenticate this mercurial request using
30 30 realm = '%s %s' % (config['hg_app_name'], 'mercurial repository')
31 31 self.authenticate = AuthBasicAuthenticator(realm, authfunc)
32 32
33 33 def __call__(self, environ, start_response):
34 34 if not is_mercurial(environ):
35 35 return self.application(environ, start_response)
36 36 else:
37 37 #===================================================================
38 38 # AUTHENTICATE THIS MERCURIAL REQUEST
39 39 #===================================================================
40 40 username = REMOTE_USER(environ)
41 41 if not username:
42 42 result = self.authenticate(environ)
43 43 if isinstance(result, str):
44 44 AUTH_TYPE.update(environ, 'basic')
45 45 REMOTE_USER.update(environ, result)
46 46 else:
47 47 return result.wsgi_application(environ, start_response)
48 48
49 49 try:
50 50 repo_name = environ['PATH_INFO'].split('/')[1]
51 51 except:
52 52 return HTTPNotFound()(environ, start_response)
53 53
54 54 #since we wrap into hgweb, just reset the path
55 55 environ['PATH_INFO'] = '/'
56 56 self.baseui = make_ui()
57 57 self.basepath = self.baseui.configitems('paths')[0][1]\
58 58 .replace('*', '')
59 59 self.repo_path = os.path.join(self.basepath, repo_name)
60 60 try:
61 61 app = wsgiapplication(self._make_app)
62 62 except Exception as e:
63 63 return HTTPNotFound()(environ, start_response)
64 64
65 65 """we know that some change was made to repositories and we should
66 66 invalidate the cache to see the changes right away"""
67 67 invalidate_cache('full_changelog', repo_name)
68 invalidate_cache('cached_repo_list')
68 69 return app(environ, start_response)
69 70
70 71 def _make_app(self):
71 72 hgserve = hgweb(self.repo_path)
72 73 return self.load_web_settings(hgserve)
73 74
74 75
75 76 def load_web_settings(self, hgserve):
76 77 repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
77 78 #set the global ui for hgserve
78 79 hgserve.repo.ui = self.baseui
79 80
80 81 if repoui:
81 82 #set the repository based config
82 83 hgserve.repo.ui = repoui
83 84
84 85 return hgserve
85 86
86 87
@@ -1,125 +1,127 b''
1 1 import os
2 2 import logging
3 3 from mercurial import ui, config, hg
4 4 from mercurial.error import RepoError
5 5 log = logging.getLogger(__name__)
6 6
7 7
8 8 def get_repo_slug(request):
9 9 path_info = request.environ.get('PATH_INFO')
10 10 uri_lst = path_info.split('/')
11 11 repo_name = uri_lst[1]
12 12 return repo_name
13 13
14 14 def is_mercurial(environ):
15 15 """
16 16 Returns True if request's target is mercurial server - header
17 17 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
18 18 """
19 19 http_accept = environ.get('HTTP_ACCEPT')
20 20 if http_accept and http_accept.startswith('application/mercurial'):
21 21 return True
22 22 return False
23 23
24 24 def check_repo_dir(paths):
25 25 repos_path = paths[0][1].split('/')
26 26 if repos_path[-1] in ['*', '**']:
27 27 repos_path = repos_path[:-1]
28 28 if repos_path[0] != '/':
29 29 repos_path[0] = '/'
30 30 if not os.path.isdir(os.path.join(*repos_path)):
31 31 raise Exception('Not a valid repository in %s' % paths[0][1])
32 32
33 33 def check_repo(repo_name, base_path):
34 34
35 35 repo_path = os.path.join(base_path, repo_name)
36 36
37 37 try:
38 38 r = hg.repository(ui.ui(), repo_path)
39 39 hg.verify(r)
40 40 #here we hnow that repo exists it was verified
41 41 log.info('%s repo is already created', repo_name)
42 42 return False
43 43 #raise Exception('Repo exists')
44 44 except RepoError:
45 45 log.info('%s repo is free for creation', repo_name)
46 46 #it means that there is no valid repo there...
47 47 return True
48 48
49 49 def make_ui(path='hgwebdir.config', checkpaths=True):
50 50 """
51 51 A funcion that will read python rc files and make an ui from read options
52 52
53 53 @param path: path to mercurial config file
54 54 """
55 55 if not os.path.isfile(path):
56 56 log.warning('Unable to read config file %s' % path)
57 57 return False
58 58 #propagated from mercurial documentation
59 59 sections = [
60 60 'alias',
61 61 'auth',
62 62 'decode/encode',
63 63 'defaults',
64 64 'diff',
65 65 'email',
66 66 'extensions',
67 67 'format',
68 68 'merge-patterns',
69 69 'merge-tools',
70 70 'hooks',
71 71 'http_proxy',
72 72 'smtp',
73 73 'patch',
74 74 'paths',
75 75 'profiling',
76 76 'server',
77 77 'trusted',
78 78 'ui',
79 79 'web',
80 80 ]
81 81
82 82 baseui = ui.ui()
83 83 cfg = config.config()
84 84 cfg.read(path)
85 85 if checkpaths:check_repo_dir(cfg.items('paths'))
86 86
87 87 for section in sections:
88 88 for k, v in cfg.items(section):
89 89 baseui.setconfig(section, k, v)
90 90
91 91 return baseui
92 92
93 93 def invalidate_cache(name, *args):
94 """Invalidates given name cache"""
95
94 96 from beaker.cache import region_invalidate
95 97 log.info('INVALIDATING CACHE FOR %s', name)
96 98
97 """propaget our arguments to make sure invalidation works. First
99 """propagate our arguments to make sure invalidation works. First
98 100 argument has to be the name of cached func name give to cache decorator
99 101 without that the invalidation would not work"""
100 102 tmp = [name]
101 103 tmp.extend(args)
102 104 args = tuple(tmp)
103 105
104 106 if name == 'cached_repo_list':
105 107 from pylons_app.lib.base import _get_repos_cached
106 108 region_invalidate(_get_repos_cached, None, *args)
107 109
108 110 if name == 'full_changelog':
109 111 from pylons_app.controllers.changelog import _full_changelog_cached
110 112 region_invalidate(_full_changelog_cached, None, *args)
111 113
112 114 from vcs.backends.base import BaseChangeset
113 115 from vcs.utils.lazy import LazyProperty
114 116 class EmptyChangeset(BaseChangeset):
115 117
116 118 revision = -1
117 119
118 120 @LazyProperty
119 121 def raw_id(self):
120 122 """
121 123 Returns raw string identifing this changeset, useful for web
122 124 representation.
123 125 """
124 126 return '0' * 12
125 127
General Comments 0
You need to be logged in to leave comments. Login now