Changeset 1141


Ignore:
Timestamp:
08/06/06 12:51:22 (13 years ago)
Author:
spascoe
Message:

There are various changes here.

The server now calls python for authorisation. This is untested, although the test suite passes. More flesh on delivery.server and it's now used in the
test suite. The server now logs python exceptions to syslog from within C code.

Location:
TI05-delivery/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • TI05-delivery/trunk/lib/python/delivery/server.py

    r1133 r1141  
    77import bbftpd 
    88 
    9 def start(authHandler): 
     9def start(authHandler, args): 
    1010    """Start the bbftp server. 
    1111 
    1212    The server performs a fork() during initialisation, the child process remains in 
    1313    the server's main loop and the parent returns from this function.  The server process 
    14     calls authHandler() on each connection to do authentication/authorisation. 
     14    calls authHandler.authorise() on each connection to do authentication/authorisation. 
    1515 
    1616    @note: because the server process forks, authHandler will not see any changes to the python 
    1717        interpreter following the call to start(). 
    1818 
     19    @param authHandler: an instance of AuthHandler. 
     20    @param args: a list of command line arguments. 
    1921    @return: the PID of the server process. 
    2022    """ 
    2123     
    22     bbftpd.run(authHandler) 
     24    return bbftpd.run(authHandler, args) 
    2325 
    2426 
     
    3032    """ 
    3133     
    32     def __call__(self): 
    33         """Entry point called by the server to do authorisation. 
    34  
    35         This function simply calls the authorise() method.  See authorise() for details. 
    36         """ 
    37         return self.authorise() 
    38  
    3934    def send(self, buffer): 
    4035        """Send a message to the client. 
     
    7065class AuthzHandler(object): 
    7166    """Abstract base class for implementing authorisation. 
     67 
     68    @ivar username the client's username 
    7269    """ 
    7370 
     
    9289        raise NotImplementedError 
    9390 
    94     def authzSend(self, path): 
    95         """Authorise a send request. 
     91    def authzStore(self, path): 
     92        """Authorise a store request. 
    9693 
    9794        @param path: the destination file. 
     
    9996        """ 
    10097 
     98        raise NotImplementedError 
     99     
     100    def _raiseNoUsername(self): 
     101        raise ValueError, "No username has been set" 
     102    username = property(_raiseNoUsername) 
    101103 
    102  
     104#-------------------------------------------------------------------------------------------------------------- 
    103105 
    104106class BasicClientAuthHandler(AuthHandler): 
     
    124126        return msg 
    125127 
     128 
     129 
     130class LiberalAuthzHandler(AuthzHandler): 
     131    """Allow everything. 
     132    """ 
     133 
     134    username = None 
     135 
     136    def __init__(self, username): 
     137        self.username = username 
     138 
     139    def authzControl(self, m, t, p): 
     140        return 0; 
     141 
     142    def authzRetr(self, p): 
     143        return 0; 
     144 
     145    def authzStore(self, p): 
     146        return 0; 
     147     
  • TI05-delivery/trunk/src/bbftp-server-3.2.0/includes/ndg.h

    r1139 r1141  
    3535char *ndg_getusername(char *logmessage); 
    3636 
     37int ndg_authz_control(int msgcode, int transferoption, char *path, char *logmessage); 
     38int ndg_authz_retr(char *path, char *logmessage); 
     39int ndg_authz_store(char *path, char *logmessage); 
     40 
     41 
    3742#endif // NDG_PYTHON_EMBED 
  • TI05-delivery/trunk/src/python_ext/bbftpd.c

    r1139 r1141  
    1111 
    1212#include <Python.h> 
     13#include <ndg.h> 
    1314 
    1415extern char **environ; 
     
    2728 * Static variables. 
    2829 */ 
    29 static PyObject *authContext = NULL; 
    30 static PyObject *authzContext = NULL; 
     30static PyObject *authHandler = NULL; 
     31static PyObject *authzHandler = NULL; 
     32 
     33static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix); 
    3134 
    3235/*--------------------------------------------------------------------------------------------------- 
     
    3740 
    3841/** 
     42 * Copy the python exception message to a bbftp logmessage. 
     43 * 
     44 * If the python error indicator is set it will be cleared. 
     45 * If the python error indicator is not set the empty string will be copied to \a logmessage. 
     46 * The exception message is truncated if it exceeds \c NDG_MAX_LOGMESSAGE. 
     47 * 
     48 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE 
     49 *     for storing an error message when returning -1. 
     50 * @param prefix a string to be prefixed to the logmessage. 
     51 */ 
     52static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix) { 
     53  PyObject *ptype, *pvalue, *ptraceback, *obj; 
     54  char *exname, *exvalue; 
     55 
     56  PyErr_Fetch(&ptype, &pvalue, &ptraceback); 
     57   
     58  if ((obj = PyObject_GetAttrString(ptype, "__name__")) == NULL) goto error; 
     59  exname = PyString_AsString(obj); Py_DECREF(obj); 
     60  if (exname == NULL) goto error; 
     61 
     62  if ((obj = PyObject_Str(pvalue)) == NULL) goto error; 
     63  exvalue = PyString_AsString(obj); Py_DECREF(obj); 
     64  if (exvalue == NULL) goto error; 
     65 
     66  snprintf(logmessage, NDG_MAX_LOGMESSAGE, "%s: %s: %s", prefix, exname, exvalue); 
     67  return; 
     68   
     69 error: 
     70  sprintf("ndg_pyerr_to_logmessage: internal python error", logmessage); 
     71  return; 
     72} 
     73 
     74/** 
    3975 * Make a callback bbftpd->python to handle authentication. 
    4076 * 
     
    4581int ndg_auth(char *logmessage) { 
    4682 
    47   if (authzContext != NULL) { 
    48     sprintf(logmessage, "bbftpd_private_auth_callback: authzContext already present"); 
    49     return -1; 
    50   } 
    51    
    52   if (authContext == NULL) { 
    53     sprintf(logmessage, "bbftpd_private_auth_callback: no authContext set"); 
    54     return -1; 
    55   } 
    56  
    57   if ((authzContext = PyObject_CallObject(authContext, NULL)) == NULL) { 
    58     //!TODO: should probably read the exception object to fill logmessage. 
    59     sprintf(logmessage, "bbftpd_private_auth_callback: authContext.authorise() failed"); 
    60     return -1; 
    61   } 
    62  
    63   /* If authContext.authorise() returned None then authorisation failed.  */ 
    64   if (authzContext == Py_None) { 
    65     sprintf(logmessage, "bbftpd_private_auth_callback: python authorisation failed"); 
     83  if (authzHandler != NULL) { 
     84    sprintf(logmessage, "ndg_auth: authzHandler already present"); 
     85    return -1; 
     86  } 
     87   
     88  if (authHandler == NULL) { 
     89    sprintf(logmessage, "ndg_auth: no authHandler set"); 
     90    return -1; 
     91  } 
     92 
     93  if ((authzHandler = PyObject_CallMethod(authHandler, "authorise", "")) == NULL) { 
     94    ndg_pyerr_to_logmessage(logmessage, "ndg_auth"); 
     95    return -1; 
     96  } 
     97 
     98  /* If authHandler.authorise() returned None then authorisation failed.  */ 
     99  if (authzHandler == Py_None) { 
     100    sprintf(logmessage, "ndg_auth: python authorisation failed"); 
    66101    return -1; 
    67102  } 
     
    71106 
    72107/** 
    73  * retrieve the username from authzContext. 
     108 * retrieve the username from authzHandler. 
    74109 * 
    75110 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE 
     
    81116  PyObject *str_obj; 
    82117 
    83   if (authzContext == NULL) { 
    84     sprintf(logmessage, "bbftpd_private_auth_getusername: no authzContext set"); 
    85     return NULL; 
    86   } 
    87  
    88   if ((str_obj = PyObject_GetAttrString(authzContext, "username")) == NULL) { 
    89     sprintf(logmessage, "bbftpd_private_auth_getusername: authzContext.username lookup failed"); 
     118  if (authzHandler == NULL) { 
     119    sprintf(logmessage, "bbftpd_private_auth_getusername: no authzHandler set"); 
     120    return NULL; 
     121  } 
     122 
     123  if ((str_obj = PyObject_GetAttrString(authzHandler, "username")) == NULL) { 
     124    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername"); 
    90125    return NULL; 
    91126  } 
    92127 
    93128  if ((username = PyString_AsString(str_obj)) == NULL) { 
     129    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername"); 
    94130    Py_DECREF(str_obj); 
    95     sprintf(logmessage, "bbftpd_private_auth_getusername: authzContext.username not a string"); 
    96131    return NULL; 
    97132  } 
     
    101136 
    102137 
    103        
     138/** 
     139 * Make a callback to do authorisation of a control command. 
     140 * 
     141 * @see bbftpd_private_authz_control() 
     142 */ 
     143int ndg_authz_control(int msgcode, int transferoption, char *path, char *logmessage) { 
     144  PyObject *ret_obj; 
     145  int ret; 
     146 
     147  if (authzHandler == NULL) { 
     148    sprintf(logmessage, "ndg_authz_control: no authzHandler set"); 
     149    return -1; 
     150  } 
     151   
     152  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzControl", "iis", msgcode, transferoption, path)) == NULL) { 
     153    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control"); 
     154    return -1; 
     155  } 
     156   
     157  if (!PyInt_Check(ret_obj)) { 
     158    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control"); 
     159    Py_DECREF(ret_obj); 
     160    return -1; 
     161  } 
     162 
     163  ret = (int)PyInt_AsLong(ret_obj); 
     164  Py_DECREF(ret_obj); 
     165  return ret; 
     166} 
     167 
     168/** 
     169 * Make a callback to do authorisation of a retr command. 
     170 * 
     171 * @see bbftpd_private_authz_store() 
     172 */ 
     173int ndg_authz_retr(char *path, char *logmessage) { 
     174  PyObject *ret_obj; 
     175  int ret; 
     176 
     177  if (authzHandler == NULL) { 
     178    sprintf(logmessage, "ndg_authz_retr: no authzHandler set"); 
     179    return -1; 
     180  } 
     181   
     182  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzRetr", "s", path)) == NULL) { 
     183    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr"); 
     184    return -1; 
     185  } 
     186   
     187  if (!PyInt_Check(ret_obj)) { 
     188    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr"); 
     189    Py_DECREF(ret_obj); 
     190    return -1; 
     191  } 
     192 
     193  ret = (int)PyInt_AsLong(ret_obj); 
     194  Py_DECREF(ret_obj); 
     195  return ret; 
     196} 
     197 
     198 
     199/** 
     200 * Make a callback to do authorisation of a store command. 
     201 * 
     202 * @see bbftpd_private_authz_store() 
     203 */ 
     204int ndg_authz_store(char *path, char *logmessage) { 
     205  PyObject *ret_obj; 
     206  int ret; 
     207 
     208  if (authzHandler == NULL) { 
     209    sprintf(logmessage, "ndg_authz_store: no authzHandler set"); 
     210    return -1; 
     211  } 
     212   
     213  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzStore", "s", path)) == NULL) { 
     214    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store"); 
     215    return -1; 
     216  } 
     217   
     218  if (!PyInt_Check(ret_obj)) { 
     219    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store"); 
     220    Py_DECREF(ret_obj); 
     221    return -1; 
     222  } 
     223 
     224  ret = (int)PyInt_AsLong(ret_obj); 
     225  Py_DECREF(ret_obj); 
     226  return ret; 
     227} 
     228  
     229 
     230      
    104231/*------------------------------------------------------------------------------------------------- 
    105232 * Functions exported to python 
     
    116243  } 
    117244 
    118   if (authContext == NULL) { 
     245  if (authHandler == NULL) { 
    119246    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback"); 
    120247    return NULL; 
     
    139266  } 
    140267 
    141   if (authContext == NULL) { 
     268  if (authHandler == NULL) { 
    142269    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback"); 
    143270    return NULL; 
     
    167294  PyObject *daemon_args, *item; 
    168295 
    169   if (!PyArg_ParseTuple(args, "OO", &authContext, &daemon_args)) { 
    170     return NULL; 
    171   } 
    172   Py_INCREF(authContext); 
     296  if (!PyArg_ParseTuple(args, "OO", &authHandler, &daemon_args)) { 
     297    return NULL; 
     298  } 
     299  Py_INCREF(authHandler); 
    173300  Py_INCREF(daemon_args); 
    174301 
     
    190317  for (i=0; i<argc; i++) { 
    191318    if ((item = PySequence_GetItem(daemon_args, i)) == NULL) { 
    192       Py_DECREF(authContext); 
     319      Py_DECREF(authHandler); 
    193320      Py_DECREF(daemon_args); 
    194321      return NULL; 
     
    197324      free(argv); 
    198325      Py_DECREF(item); 
    199       Py_DECREF(authContext); 
     326      Py_DECREF(authHandler); 
    200327      Py_DECREF(daemon_args); 
    201328      return NULL; 
     
    213340 
    214341  free(argv); 
    215   Py_DECREF(authContext); authContext = NULL; 
     342  Py_DECREF(authHandler); authHandler = NULL; 
    216343  Py_DECREF(daemon_args); 
    217344 
  • TI05-delivery/trunk/test/test_embedded.py

    r1133 r1141  
    2222 
    2323sys.path.append(BUILDDIR) 
    24 import delivery.bbftpd as bbftpd 
    25          
    26 class AuthzContext: 
    27     def __init__(self, version_msg, user): 
    28         self.version = version_msg 
    29         self.username = user 
    30  
    31 class AuthContext: 
    32     def __call__(self): 
    33         # Read the auth version message 
    34         msg = bbftpd.recv() 
    35         # Trim to first '\0' 
    36         x = msg.find('\0') 
    37         if x: 
    38             msg = msg[:x] 
     24 
     25import delivery.server as server 
     26 
     27 
     28class TestAuthHandler(server.BasicClientAuthHandler): 
     29    def authorise(self): 
     30        msg = self.recvCStr() 
    3931 
    4032        syslog.syslog(syslog.LOG_DEBUG, 'AuthContext received Auth message: %s' % msg) 
    4133 
    42         # Send the response 
    43         msg = NDG_HANDSHAKE + '\0' * (NDG_MESSAGE_LEN - len(NDG_HANDSHAKE)) 
    44         bbftpd.send(msg) 
    45  
    46         # Get privatestr 
    47         privatestr = bbftpd.recv() 
    48         # Trim to first '\0' 
    49         x = privatestr.find('\0') 
    50         if x: 
    51             privatestr = privatestr[:x] 
     34        self.send(NDG_HANDSHAKE) 
     35 
     36        privatestr = self.recvCStr() 
    5237        syslog.syslog(syslog.LOG_DEBUG, "AuthContext received privatestr: %s" % privatestr) 
    5338 
    54         return AuthzContext(msg, "TestCaseUser") 
    55  
    56  
    57 class FailingAuthContext(AuthContext): 
    58     def __call__(self): 
    59         az = super(FailingAuthContext).authorise() 
     39        return TestAuthzHandler(msg, "TestCaseUser") 
     40 
     41class TestAuthzHandler(server.LiberalAuthzHandler): 
     42    def __init__(self, version, username): 
     43        super(TestAuthzHandler, self).__init__(username) 
     44        self.version = version 
     45 
     46 
     47class TestFailAuthHandler(server.AuthHandler): 
     48    def authorise(self): 
    6049        return None 
    61  
    62  
    63  
    6450 
    6551class BaseFixture(unittest.TestCase): 
     
    8167    def _startServer(self, authContext=None): 
    8268        if not authContext: 
    83             authContext = AuthContext() 
     69            authContext = TestAuthHandler() 
    8470        # Start the server and store it's PID 
    85         self.pid = bbftpd.run(authContext, ['-l', 'DEBUG']) 
     71        self.pid = server.start(authContext, ['-l', 'DEBUG']) 
    8672 
    8773    def _stopServer(self): 
     
    217203        # Check the client output 
    218204        output = fh.read() 
     205 
    219206        self.assertLines(['get.*nogzip'], output) 
    220207 
     
    258245        syslog.syslog(syslog.LOG_DEBUG, 'Starting EmbeddedServerTestCase') 
    259246 
    260         self._startServer(authContext=FailingAuthContext()) 
     247        self._startServer(authContext=TestFailAuthHandler()) 
    261248 
    262249    def test(self): 
Note: See TracChangeset for help on using the changeset viewer.