Permalink
Browse files

Administrative commands such as force_update{_all} can now be passwor…

…d protected.
  • Loading branch information...
1 parent bcc8fac commit c1b27dc18f498affba3bc5ed98287352a894881c @foucault committed Oct 8, 2011
Showing with 133 additions and 49 deletions.
  1. +21 −4 man/pkgupdate.8.rst
  2. +76 −29 pkgupdate_server.py
  3. +34 −16 src/pkgupdate/config.py
  4. +2 −0 src/pkgupdate/util.py
View
@@ -34,6 +34,13 @@ your options and pass them to the server. Example: ::
OPTS="-a -i -g -y -v -m -l /var/log/pkgupdate.log"
+or use the global configuration file (default /etc/pkgupdate.conf).
+
+-c, --conf *CONFFILE*
+---------------------
+
+Override the default configuration file with the one specificied.
+
-a, --aur
---------
@@ -57,13 +64,13 @@ synchronize the database yourself, for example using this cronjob ::
which is also shipped with the source distribution for your
convenience.
--t, --pacman-timeout= *INTERVAL*
+-t, --pacman-timeout *INTERVAL*
---------------------------------
Every how many seconds should the repositories be checked for
updates. Default is 300.
--r, --aur-timeout= *INTERVAL*
+-r, --aur-timeout *INTERVAL*
-----------------------------
Every how many seconds should AUR packages be checked for updates.
@@ -81,10 +88,10 @@ Ignore groups that are listed as such in /etc/pacman.conf. This
must always be invoked in conjuction to **--ignore-enable** otherwise
it will be dropped silently.
--f, --force-update= *{pacman|all}*
+-f, --force-update *{pacman|all}*
----------------------------------
-Force updating of the server cache.
+Force updating of the server cache. Might require authentication.
-s, --status
------------
@@ -107,6 +114,16 @@ Log DEBUG messages as well.
Monitor pacman database for updates and update the server if
a change has occurred (EXPERIMENTAL).
+-p, --gen-pass
+--------------
+
+Encrypt a new password for use with the configuration file. The
+resulting text will be: ::
+
+ password = <SALT>$<HASH>
+
+Input the result into your configuration file.
+
-h, --help
----------
View
@@ -26,7 +26,7 @@
except:
# Fallback supplied pyinotify
import pkgupdate.pyinotify as PIN
-from pkgupdate.util import GzipRotatingFileHandler
+from pkgupdate.util import GzipRotatingFileHandler, check_password
from pkgupdate.config import parse_configs
import logging as LOG
import asynchat
@@ -293,9 +293,10 @@ class Server(asyncore.dispatcher):
data to the clients.
"""
- def __init__(self, services):
+ def __init__(self, services, conf=None):
asyncore.dispatcher.__init__(self)
self.services = services
+ self.conf = conf
for service in self.services:
LOG.debug("Starting service %s"%service.aid)
service.start()
@@ -305,7 +306,7 @@ def __init__(self, services):
def handle_accepted(self, sock, addr):
""" Spawn a handler using the associated services. """
- ServerHandler(sock, self.services)
+ ServerHandler(sock, self.services, self.conf)
def handle_expt(self):
LOG.debug("handle_expt")
@@ -329,12 +330,13 @@ class ServerHandler(asynchat.async_chat):
RESP_ACK = 'ack'
RESP_ERR = 'error'
RESP_STA = 'status'
+ RESP_NEEDAUTH = 'auth_req'
- def __init__(self, sock, services):
+ def __init__(self, sock, services,conf=None):
asynchat.async_chat.__init__(self,sock)
self.ibuffer = []
self.isize = 0
- #self.services = services
+ self.conf = conf
self.services = dict()
for service in services:
self.services[service.aid] = service
@@ -381,31 +383,61 @@ def inner(f):
return f
return inner
+ def admin_pwd(self):
+ pwd = self.conf.password.strip()
+ if pwd == '':
+ return None
+ else:
+ return pwd
+
+ def is_authorized(self, pwd=None):
+ pw = self.admin_pwd()
+ if pw == None:
+ auth = True
+ else:
+ if(check_password(pw,pwd)):
+ auth = True
+ else:
+ auth = False
+ return auth
+
@syntax('nudge')
@close_me
def nudge(self):
self.push(json.dumps({self.RESP_ACK:'OK'}).encode("UTF-8"))
- @syntax('force_upd <SERVICE_ID>=repos')
+ @syntax('force_upd <SERVICE_ID>=repos password')
@close_me
- def force_upd(self, aid='repos'):
- LOG.info("Forcing update")
- if aid == 'all':
- self.force_upd_all()
- return
- service = self.service_by_id(aid)
- if service is not None:
- service.force_update()
+ def force_upd(self, aid='repos', pwd=None):
+ if self.is_authorized(pwd):
+ LOG.info("Forcing update")
+ if aid == 'all':
+ self.force_upd_all(pwd)
+ return
+ service = self.service_by_id(aid)
+ if service is not None:
+ service.force_update()
+ self.push(json.dumps({self.RESP_ACK:'ok'}).encode('UTF-8'))
+ else:
+ self.push(json.dumps({self.RESP_ERR:\
+ 'No such service'%aid}).encode('UTF-8'))
else:
- self.push(json.dumps({self.RESP_ERR:\
- 'No such service'%aid}).encode('UTF-8'))
+ LOG.warning("Wrong password")
+ self.push(json.dumps({self.RESP_NEEDAUTH:\
+ 'Wrong password'}).encode('UTF-8'))
- @syntax('force_upd_all')
+ @syntax('force_upd_all password')
@close_me
- def force_upd_all(self):
- LOG.info("Forcing update (all)")
- for service in self.services.keys():
- self.services[service].force_update()
+ def force_upd_all(self,pwd=None):
+ if self.is_authorized(pwd):
+ LOG.info("Forcing update (all)")
+ for service in self.services.keys():
+ self.services[service].force_update()
+ self.push(json.dumps({self.RESP_ACK:'ok'}).encode('UTF-8'))
+ else:
+ LOG.warning("Wrong password")
+ self.push(json.dumps({self.RESP_NEEDAUTH:\
+ 'Wrong password'}).encode('UTF-8'))
@syntax('status')
@close_me
@@ -442,7 +474,7 @@ def found_terminator(self):
parts = re.split("\s*", inp)
command = parts[0]
args = parts[1:]
- LOG.debug("Command: %s - Args: %s"%(command, args))
+ LOG.debug("Command: %s - No of Args: %s"%(command, len(args)))
del self.ibuffer[:] # clear the buffer
self.isize = 0
try:
@@ -465,13 +497,27 @@ def update_server(update_all=False):
""" Pseudo client to update the server. """
try:
+ from getpass import getpass
+ pw = getpass("Server admin password: ")
+ pw = pw.strip()
sock = S.socket(S.AF_UNIX, S.SOCK_STREAM)
sock.connect(SOCKET)
if update_all:
- sock.send("force_upd_all\r\n".encode("utf-8"))
+ sock.send(("force_upd_all %s\r\n"%pw).encode("utf-8"))
else:
- sock.send("force_upd\r\n".encode("utf-8"))
+ sock.send(("force_upd repos %s\r\n"%pw).encode("utf-8"))
+ response = b''
+ while True:
+ read = sock.recv(128)
+ if not read:
+ break
+ else:
+ response += read
sock.close()
+ data = json.loads(response.decode())
+ if ServerHandler.RESP_NEEDAUTH in data.keys():
+ print("Invalid password")
+ sys.exit(1)
sys.exit()
except Exception as error:
LOG.critical(error)
@@ -500,7 +546,7 @@ def socket_available():
signal.signal(signal.SIGQUIT, signals_handler)
options = parse_configs()
-
+
# Status reporting
if options.check_status == True:
try:
@@ -524,7 +570,7 @@ def socket_available():
if options.force == "all":
update_server(True)
if options.force == "pacman":
- update_server()
+ update_server(False)
if options.gen_pass == True:
import getpass as GP
@@ -555,7 +601,8 @@ def socket_available():
loglevel = LOG.DEBUG
else:
loglevel = LOG.INFO
- if options.logfile == None:
+ logfile = options.logfile.strip()
+ if logfile == '':
LOG.basicConfig(format=log_format, datefmt=datefmt, level=loglevel)
else:
handler = GzipRotatingFileHandler(options.logfile,\
@@ -607,7 +654,7 @@ def socket_available():
LOG.debug("Starting AUR thread")
aur_service = AURService(ALPM, \
ignored_packages=ignored_packages, \
- timeout=float(options.aur_timeout) )
+ timeout=float(options.aur_timeout))
else:
aur_service = None
@@ -637,7 +684,7 @@ def socket_available():
if sync_service:
services.append(sync_service)
- server = Server(services)
+ server = Server(services,conf=options)
except S.error as error:
if error.errno == 98:
Oops, something went wrong.

0 comments on commit c1b27dc

Please sign in to comment.