Nicko,
Your suggestion to create a generic impersionation mechanism sounds good
- simple and does just what is needed, plus makes it usable by other
appenders.
Are you envisioning the appender would be supplied with an external
(consuming-app) object that can do the impersonation (via
IExternalCredential)? In other words, do you see the logic for
Impersonate and Revert being supplied by the application that calls
log4net? Hopefully this is what you meant, since it gives much more
flexibility in what type of impersonation is actually used, since this
will vary considerably per application.
Some comments:
1) For every call to Impersonate, we need to guarantee a call to
Revert.
- This means try...finally in each Appender around any I/O that
requires impersonation
2) Since Appenders need to impersonate as a different users, there must
be some way to configure them. I suggest a app-supplied factory that is
called during log4net configuration:
public interface IImpersonationFactory
{
// Create an object that can impersonate for the
appender
IExternalCredential Create( Appender appender );
}
Using an assembly-level attribute, this could then be supplied by an
external app to allow it to provide impersonation objects as needed. If
no external factory is supplied, or it returns null, a NullImpersonation
object could be supplied by log4net (or all the appenders can have null
check guard logic around impersonation calls).
For example, the following skeleton shows a scenario that would meet our
current needs for FileAppender and RollingFileAppender
public class MyExternalCredential : IExternalCredential
{
WindowsImpersonationContext wic = null;
// Constructor
public MyExternalCredential()
{
... lookup the admin user credentials from
registry, decrypt and then store username / password / domain
This is app-specific, and not something
log4net could do or would want to try to do for us.
}
// Called before File Create/Delete operations
public void Impersonate()
{
if( null == wic )
{
... Call LogonUser, and get the
impersonation context
this.wic =
WindowsIdentity.Impersonate(userToken);
}
else
{
... Double impersonation attempt, so do
nothing since we are already impersonating
}
}
// Called after File Create/Delete operations
public void Revert()
{
if( null != wic )
{
wic.Undo();
wic = null;
}
}
}
public class MyImpersonationFactory : IImpersonationFactory
{
public IExternalCredential Create( Appender appender )
{
if( appender is FileAppender || appender is
RollingFileAppender )
{
// We want to impersonate as a specific
user for FileAppender and RollingFileAppender.
return new MyExternalCredential();
}
// Since we don't use other appenders, let
log4net defaults be used
return null;
}
}
This seems like it would be generic and extensible, allowing the
application to control how impersonation is done, and what user is used,
plus allow log4net to easily support impersonation for resource access.
What do you think?
-Doug
-----Original Message-----
From: Nicko Cadell [mailto:[EMAIL PROTECTED]
Sent: Sunday, September 12, 2004 12:13 PM
To: Log4NET Dev
Subject: RE: Allow log4net to impersonate a user via app-specific
mechanism.
Doug,
You make some good points here. Appenders need to have a mechanism to
utilise thread level impersonation to obtain access to system resources.
The impersonation must be configurable on an appender by appender basis.
I think that this needs to be a generalised extensibility point for
impersonation that can be used by the following appenders:
AdoNetAppender, EventLogAppender, FileAppender, NetSendAppender,
RollingFileAppender, and SmtpPickupDirAppender.
I don't see that this requires the abstraction of the file system
services into a IFileFactory interface (although there may be other
benefits to doing this).
A simple interface such as:
public interface IExternalCredential
{
public void Impersonate();
public void Revert();
}
should do the job. The appenders would need to be updated to call
Impersonate/Revert around calls that have access control constraints.
This should also allow for the appender to make informed decisions about
when to impersonate rather than impersonating for each file system call.
Coarser grained impersonation should not harm performance as much as
fine grained impersonation.
What are your thoughts on this?
Nicko
> 2) Allow log4net to impersonate a user via app-specific mechanism.
>
> Currently, log4net opens files as the user who logs a message.
> This means on a site with
> lots of users, in order to ensure fail-safe logging, all users
must
> have full permissions
> to the folder where logging occurs. This is problematic, since
> customers are reluctant
> to open up folders will full access, and when they are willing,
it is
> still tough to keep
> permissions on the folder in-sync with all the site users. If
even
> one user doesn't have
> full permissions, logging can fail (and stop
> thereafter) for all users.
>
> Impact would be to the RollingFileAppender and the FileAppender
> classes.
>
> How?
>
> Introduce a FileFactory mechanism, and specify the type via the
XML
> config file.
> Most users would see no difference, but this would give us a
point of
> flexibility
> to control how file I/O is done.
>
> Default factory does what log4net currently does (all file I/O
done
> as current user)
> When there is no XML override, then it falls back to using the
> current approach of file I/O,
> using a 'default' factory (which would do raw file I/O).
>
> Our application could could override via config XML
>
> <log4net>
> <fileFactory
> type="Customer.Specified.MyFileFactory,mydll" />
> </log4net>
>
> Our application's file factory could implement IFileFactory
>
> public interface IFileFactory
> {
> void CreateDirectory( string directoryToFile );
>
> StreamWriter OpenFile( string fileName, bool
append, string
> encoding );
>
> IFileInfo NewFileInfo( string fileName );
> }
>
> Then, when the IFileFactory methods are called, we could do
> impersonation around the file
> I/O, allowing the logfile to be created using a single account.
> By ensuring that one account
> has full permissions on the log folder, it is easier to ensure
> logging will always succeed
> for all users.
>
> Note that the messages themselves would still be logged as the
> windows identity of the user that
> was running when the message was logged. Only the file creation
(or
> deletion) would go through a
> factory, allowing those operations to be done differently if
needed.
>
> Need to impersonate for the FileInfo.Delete, FileInfo.MoveTo
checks,
> as well as the OpenFile call,
> and FileInfo.Directory.GetFiles
>
> Since FileInfo is sealed, need to create wrapper interfaces for
> common file actions:
>
> public interface IFileInfo
> {
> bool Exists { get; }
>
> long Length { get; }
>
> string DirectoryName { get; }
>
> IDirectoryInfo Directory { get; }
>
> DateTime LastWriteTime { get; }
>
> void Delete();
>
> void MoveTo( string destinationFile );
> }
>
> public interface IDirectoryInfo
> {
> bool Exists { get; }
>
> IFileInfo[] GetFiles( string wildcardPattern );
> }