diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2014-05-08 11:41:25 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2014-05-08 11:41:25 (GMT) |
commit | 70c1b1a26a610c082a49bd5fb29e6dca2bf47943 (patch) | |
tree | 935412a227a5bcd63580010a65a15e5dcc5d3050 /sugar_network | |
parent | 5ebeca1a965aa4028fde7a73c7baffbdba705ef5 (diff) |
Start local node router for admin needs
Diffstat (limited to 'sugar_network')
-rw-r--r-- | sugar_network/node/auth.py | 23 | ||||
-rw-r--r-- | sugar_network/node/routes.py | 9 | ||||
-rw-r--r-- | sugar_network/node/slave.py | 4 | ||||
-rw-r--r-- | sugar_network/toolkit/__init__.py | 8 | ||||
-rw-r--r-- | sugar_network/toolkit/application.py | 18 | ||||
-rw-r--r-- | sugar_network/toolkit/coroutine.py | 26 | ||||
-rw-r--r-- | sugar_network/toolkit/http.py | 3 | ||||
-rw-r--r-- | sugar_network/toolkit/router.py | 3 |
8 files changed, 62 insertions, 32 deletions
diff --git a/sugar_network/node/auth.py b/sugar_network/node/auth.py index d14bde6..13a9608 100644 --- a/sugar_network/node/auth.py +++ b/sugar_network/node/auth.py @@ -62,14 +62,25 @@ class Principal(str): @property def cap_create_with_guid(self): - return self._caps & 1 + return self._caps & 2 @cap_create_with_guid.setter def cap_create_with_guid(self, value): if value: - self._caps |= 1 + self._caps |= 2 else: - self._caps ^= 1 + self._caps ^= 2 + + @property + def cap_admin(self): + return self._caps & 4 + + @cap_admin.setter + def cap_admin(self, value): + if value: + self._caps |= 4 + else: + self._caps ^= 4 def __enter__(self): self._backup = self._caps @@ -141,3 +152,9 @@ class SugarAuth(object): # TODO return principal + + +class RootAuth(object): + + def logon(self, request): + return Principal('root', 0xFFFF) diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py index 68beb65..79c4056 100644 --- a/sugar_network/node/routes.py +++ b/sugar_network/node/routes.py @@ -82,6 +82,9 @@ class NodeRoutes(db.Routes, FrontRoutes): allowed = True enforce(allowed, http.Forbidden, 'Authors only') + if op.acl & ACL.ADMIN: + enforce(this.principal.cap_admin, http.Forbidden, 'Admins only') + @postroute def postroute(self, result, exception): request = this.request @@ -172,9 +175,9 @@ class NodeRoutes(db.Routes, FrontRoutes): return solution @route('GET', ['context', None], cmd='resolve', - arguments={'requires': list, 'stability': list}) - def resolve(self): - solution = self.solve() + arguments={'requires': list, 'stability': list, 'assume': list}) + def resolve(self, assume=None): + solution = self.solve(assume) return self.volume.blobs.get(solution[this.request.guid]['blob']) @route('GET', [None, None], cmd='diff') diff --git a/sugar_network/node/slave.py b/sugar_network/node/slave.py index a1195ab..7bf29e2 100644 --- a/sugar_network/node/slave.py +++ b/sugar_network/node/slave.py @@ -57,7 +57,7 @@ class SlaveRoutes(NodeRoutes): self._master_guid = urlsplit(master_api).netloc self._master_api = master_api - @route('POST', cmd='online_sync', acl=ACL.LOCAL, + @route('POST', cmd='online_sync', acl=ACL.AUTH | ACL.ADMIN, arguments={'no_pull': bool}) def online_sync(self, no_pull=False): conn = http.Connection(self._master_api) @@ -70,7 +70,7 @@ class SlaveRoutes(NodeRoutes): headers={'Transfer-Encoding': 'chunked'}) self._import(packets.decode(response.raw)) - @route('POST', cmd='offline_sync', acl=ACL.LOCAL) + @route('POST', cmd='offline_sync', acl=ACL.AUTH | ACL.ADMIN) def offline_sync(self, path): enforce(isabs(path), "Argument 'path' is not an absolute path") diff --git a/sugar_network/toolkit/__init__.py b/sugar_network/toolkit/__init__.py index bf80271..9cad5e0 100644 --- a/sugar_network/toolkit/__init__.py +++ b/sugar_network/toolkit/__init__.py @@ -34,7 +34,7 @@ BUFFER_SIZE = 1024 * 10 cachedir = Option( 'path to a directory to keep cached files; such files ' 'might take considerable number of bytes', - default='/var/cache/sugar-network', name='cachedir') + name='cachedir') _logger = logging.getLogger('toolkit') @@ -390,7 +390,7 @@ def unique_filename(root, filename): class mkdtemp(str): def __new__(cls, *args, **kwargs): - if 'dir' not in kwargs: + if cachedir.value and 'dir' not in kwargs: kwargs['dir'] = cachedir.value if not exists(kwargs['dir']): os.makedirs(kwargs['dir']) @@ -431,7 +431,7 @@ def svg_to_png(data, w, h=None): def TemporaryFile(*args, **kwargs): - if 'dir' not in kwargs: + if cachedir.value and 'dir' not in kwargs: kwargs['dir'] = cachedir.value if not exists(kwargs['dir']): os.makedirs(kwargs['dir']) @@ -441,7 +441,7 @@ def TemporaryFile(*args, **kwargs): class NamedTemporaryFile(object): def __init__(self, *args, **kwargs): - if 'dir' not in kwargs: + if cachedir.value and 'dir' not in kwargs: kwargs['dir'] = cachedir.value if not exists(kwargs['dir']): os.makedirs(kwargs['dir']) diff --git a/sugar_network/toolkit/application.py b/sugar_network/toolkit/application.py index bc6b99c..670a2d3 100644 --- a/sugar_network/toolkit/application.py +++ b/sugar_network/toolkit/application.py @@ -175,8 +175,6 @@ class Application(object): pass def start(self): - self._rundir = abspath(rundir.value or '/var/run/' + self.name) - cmd_name = self.args.pop(0) try: cmd = self._commands.get(cmd_name) @@ -186,10 +184,11 @@ class Application(object): logging.info('Load configuration from %s file(s)', ', '.join(Option.config_files)) - if cmd.options.get('keep_stdout') and not foreground.value: - self._keep_stdout() - self.prolog() + self._rundir = abspath(rundir.value or '/var/run/' + self.name) + + if cmd.options.get('keep_stdout') and not foreground.value: + self.reopen_logs() exit(cmd() or 0) except Exception: printf.exception('%s %s', _('Aborted'), self.name) @@ -214,9 +213,6 @@ class Application(object): pid = None return pid - def ensure_run(self): - pass - def ensure_pidfile(self): if not exists(self._rundir): os.makedirs(self._rundir) @@ -237,7 +233,7 @@ class Application(object): else: print Option.help() - def _keep_stdout(self): + def reopen_logs(self): log_dir = abspath(logdir.value) if not exists(log_dir): os.makedirs(log_dir) @@ -280,7 +276,6 @@ class Daemon(Application): pass time.sleep(.5) - self.ensure_run() if foreground.value: self._launch() else: @@ -333,7 +328,7 @@ class Daemon(Application): def sighup_cb(): logging.info('Reload %s on SIGHUP signal', self.name) - self._keep_stdout() + self.reopen_logs() coroutine.signal(signal.SIGINT, sigterm_cb, signal.SIGINT) coroutine.signal(signal.SIGTERM, sigterm_cb, signal.SIGTERM) @@ -343,7 +338,6 @@ class Daemon(Application): try: self.run() finally: - self.epilog() os.unlink(pid_path) def _daemonize(self): diff --git a/sugar_network/toolkit/coroutine.py b/sugar_network/toolkit/coroutine.py index 1f3d842..c3d4a47 100644 --- a/sugar_network/toolkit/coroutine.py +++ b/sugar_network/toolkit/coroutine.py @@ -80,13 +80,15 @@ def socket(*args, **kwargs): return gevent.socket.socket(*args, **kwargs) -def listen_unix_socket(path, backlog=5): +def listen_unix_socket(path, backlog=5, reuse_address=False, mode=None): # pylint: disable-msg=E1101 from tempfile import NamedTemporaryFile import _socket if exists(path): - raise RuntimeError('The socket address is in use') + if not reuse_address: + raise RuntimeError('The socket address is in use') + os.unlink(path) sock = socket(_socket.AF_UNIX, _socket.SOCK_STREAM) sock.setblocking(0) @@ -94,6 +96,8 @@ def listen_unix_socket(path, backlog=5): with NamedTemporaryFile(dir=dirname(path)) as tmp_path: pass sock.bind(tmp_path.name) + if mode is not None: + os.chmod(tmp_path.name, mode) try: os.rename(tmp_path.name, path) except Exception, error: @@ -141,7 +145,11 @@ def Server(*args, **kwargs): def WSGIServer(*args, **kwargs): import gevent.wsgi - class WSGIHandler(gevent.wsgi.WSGIHandler): + class Server(gevent.wsgi.WSGIServer): + + http_log = kwargs.pop('http_log') if 'http_log' in kwargs else None + + class Handler(gevent.wsgi.WSGIHandler): def log_error(self, msg, *args): if args: @@ -149,14 +157,16 @@ def WSGIServer(*args, **kwargs): _wsgi_logger.error('%s %s', self.format_request(), msg) def log_request(self): - _wsgi_logger.debug('%s', self.format_request()) + logfile = server.http_log + if logfile is not None: + logfile.write(self.format_request()) + logfile.write('\n') kwargs['spawn'] = Pool() if 'handler_class' not in kwargs: - if logging.getLogger().level >= logging.DEBUG: - WSGIHandler.log_request = lambda * args: None - kwargs['handler_class'] = WSGIHandler - return gevent.wsgi.WSGIServer(*args, **kwargs) + kwargs['handler_class'] = Handler + server = Server(*args, **kwargs) + return server def Event(): diff --git a/sugar_network/toolkit/http.py b/sugar_network/toolkit/http.py index f3c2ef8..51b34ee 100644 --- a/sugar_network/toolkit/http.py +++ b/sugar_network/toolkit/http.py @@ -119,6 +119,9 @@ class Connection(object): self._session = None self._auth_request = auth_request + if self.url and self.url.startswith('file://'): + self._session_args['trust_env'] = False + def __repr__(self): return '<Connection url=%s>' % self.url diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py index ba498d6..7b2186d 100644 --- a/sugar_network/toolkit/router.py +++ b/sugar_network/toolkit/router.py @@ -88,6 +88,7 @@ class ACL(object): AGG_AUTHOR = 1 << 12 LOCAL = 1 << 20 + ADMIN = 1 << 21 NAMES = { CREATE: 'Create', @@ -854,6 +855,8 @@ class _Route(object): 'ACL.AUTHOR requires longer path') enforce(acl ^ ACL.AGG_AUTHOR or len(path) >= 3, 'ACL.AGG_AUTHOR requires longer path') + enforce(acl ^ ACL.ADMIN or acl & ACL.AUTH, + 'ACL.ADMIN without ACL.AUTH') self.op = (method, cmd) self.callback = callback |