thekid/rfc — Issue #52: RFC 0035: Interceptors for FTP storage
Author: thekid
Created: 2011-10-09 14:18:30+00:00
State: closed
Labels: implemented, rfc

Scope of Change
====
An API will be added to the peer.ftp.server package to allow attaching
interceptors that can "veto" FTP commands.

Rationale
====
In some cases, especially when working with more systems to set up a
FTP storage (e.g. filesystem and database), it's necessary to do
additional checks when modifying the storage (e.g. renaming a file,
uploading/replacing a file).
Further this can be used to do some permission checks using other
systems (e.g. LDAP or databases).

Functionality
====
An interceptor can throw an exception to indicate that processing with
this command is not acceptable and the command will be aborted. When
throwing an exception an additional error message can be passed to the
FTP server which sends it to the client to clarify what the problem is
and why the command has been aborted.

Interceptable actions
----
- Creating an entry (collection or element)
- Reading an element
- Modifying an element
- Renaming an entry (collection or element)
- Deleting an entry (collection or element)
- Changing an entry (collection or element)'s permissions
- Changing a directory

Example (abbreviated):

```php
conn->query('...')) return throw(new IllegalStateException('...'));

return TRUE;
}
}

// Add a global interceptor (applies to entire storage)
$storage->addInterceptor(new DeleteInterceptor());

// Add an interceptor for the base "/check/only/in/this/path", idea #1
$storage->addInterceptorFor('/check/only/in/this/path', new DeleteInterceptor());

?>
```

Extended implementation (abbreviated):

```php
path= $path;
}

function check(&$session, $event) {
return substr($event->getPath(), 0, strlen($this->path)) == $this->path;
}

} implements(__FILE__, 'peer.ftp.server.interceptor.InterceptorCondition');

// Add an interceptor for the base "/check/only/in/this/path", idea #2
$storage->addInterceptorFor(
new PathCondition('/check/only/in/this/path'),
new DeleteInterceptor()
);

// Add an interceptor for the base "/check/only/in/this/path", idea #3
// In this example, we'd only check between 3 AM and 4 AM (maybe because
// we have an extensive indexer search or backup running)
$storage->addInterceptorFor(
array(new PathCondition('/check/only/in/this/path'), new TimeCondition('03:00', '04:00')),
new DeleteInterceptor()
);

// Add interceptor to allow only specific filename extensions
$storage->addIntercetporFor(
array(new PathCondition('/only/gif-and-jpg/dir')),
new EntrynameInterceptor('/\.(gif|jpg)$/')
);
?>
```

Note: The DefaultInterceptor class implements the

peer.ftp.server.intercept.StorageActionInterceptor

interface and returns TRUE to all methods so that subclasses need not redefine
them if they desire no actions.

Interceptor priority
----

It's possible to add more than one interceptors to a storage. They'll be
invoked in the same order as added to the storage. If an interceptor
returns FALSE - indicating the command should be aborted - the following
interceptors will not be invoked!

Conditional interceptors are only invoked, when all the conditions match
or returns TRUE. If any of the condition fails the interceptor will not
be invoked.

Security considerations
====
n/a

Speed impact
====
Makes FTP server a bit slower, but only when interceptors are added.

Dependencies
====
n/a

Related documents
====
n/a

Comments
====
- friebe, Wed Aug 24 13:33:30 CEST 2005
Maybe addConditionalInterceptor() is a better name for addInterceptorFor()?

- olli, Thu Aug 25 08:51:21 CEST 2005
We should add a second parameter to the interceptor functions to pass a
context (e.g. the FtpSession)

- olli, Thu Aug 25 09:42:45 CEST 2005
Should we also add support to intercept reading a directory and rename
"Reading an element" to "Reading an entry (collection or element)"?

- olli, Thu Aug 25 15:35:34 CEST 2005
The following commands need to be mapped to an interceptor function:
onList => onRead // directory listing should also be interceptable
onNlst => onRead
onMdtm => onRead
onSize => onRead
onMkd => onCreate
onRmd => onDelete
onRetr => onRead
onStor => onCreate // distinguish creating and overwriting?
onDele => onDelete
onRnto => onRename
onSiteChmod => onChangePermission

- olli, Fri Aug 26 07:56:21 CEST 2005
The interceptors have to be added to the FtpThead, which holds them
temporarily and adds them to the Storage as soon as the Storage is
instantiated. We have no Storage instance which could be used to add the
interceptors before the server starts.

- olli, Fri Aug 26 15:04:44 CEST 2005
Should InterceptorCondition be an interface or just a simple class?

- olli, Tue Sep 6 15:36:29 CEST 2005
I added an EntrynameInterceptor which can be used to force client to
upload only special files with specific extensions (e.g. jpg or .gif)