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