mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2024-10-07 00:08:05 +08:00
Allow multiple user accounts for authentication
This commit is contained in:
parent
80cff01154
commit
1ca5a56952
@ -48,8 +48,7 @@ extern NSString* const GCDWebServerOption_MaxPendingConnections; // NSNumber /
|
||||
extern NSString* const GCDWebServerOption_ServerName; // NSString (default is server class name)
|
||||
extern NSString* const GCDWebServerOption_AuthenticationMethod; // One of "GCDWebServerAuthenticationMethod_..." (default is nil i.e. no authentication)
|
||||
extern NSString* const GCDWebServerOption_AuthenticationRealm; // NSString (default is server name)
|
||||
extern NSString* const GCDWebServerOption_AuthenticationUser; // NSString
|
||||
extern NSString* const GCDWebServerOption_AuthenticationPassword; // NSString
|
||||
extern NSString* const GCDWebServerOption_AuthenticationAccounts; // NSDictionary of username / password (default is nil i.e. no accounts)
|
||||
extern NSString* const GCDWebServerOption_ConnectionClass; // Subclass of GCDWebServerConnection (default is GCDWebServerConnection class)
|
||||
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; // NSNumber / BOOL (default is YES)
|
||||
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; // NSNumber / double (default is 1.0 seconds - set to <=0.0 to disable coaslescing of -webServerDidConnect: / -webServerDidDisconnect:)
|
||||
@ -57,7 +56,7 @@ extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; //
|
||||
extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground; // NSNumber / BOOL (default is YES)
|
||||
#endif
|
||||
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_Basic;
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_Basic; // Not recommended as password is sent in clear
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
|
||||
@class GCDWebServer;
|
||||
|
@ -55,8 +55,8 @@
|
||||
NSDictionary* _options;
|
||||
NSString* _serverName;
|
||||
NSString* _authenticationRealm;
|
||||
NSString* _authenticationBasicAccount;
|
||||
NSString* _authenticationDigestAccount;
|
||||
NSMutableDictionary* _authenticationBasicAccounts;
|
||||
NSMutableDictionary* _authenticationDigestAccounts;
|
||||
Class _connectionClass;
|
||||
BOOL _mapHEADToGET;
|
||||
CFTimeInterval _disconnectDelay;
|
||||
@ -86,8 +86,7 @@ NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnectio
|
||||
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
||||
NSString* const GCDWebServerOption_AuthenticationMethod = @"AuthenticationMethod";
|
||||
NSString* const GCDWebServerOption_AuthenticationRealm = @"AuthenticationRealm";
|
||||
NSString* const GCDWebServerOption_AuthenticationUser = @"AuthenticationUser";
|
||||
NSString* const GCDWebServerOption_AuthenticationPassword = @"AuthenticationPassword";
|
||||
NSString* const GCDWebServerOption_AuthenticationAccounts = @"AuthenticationAccounts";
|
||||
NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass";
|
||||
NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET";
|
||||
NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval";
|
||||
@ -157,7 +156,7 @@ static void _SignalHandler(int signal) {
|
||||
@implementation GCDWebServer
|
||||
|
||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
||||
authenticationBasicAccount=_authenticationBasicAccount, authenticationDigestAccount=_authenticationDigestAccount,
|
||||
authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
|
||||
shouldAutomaticallyMapHEADToGET=_mapHEADToGET;
|
||||
|
||||
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
||||
@ -383,14 +382,18 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
||||
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
||||
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
|
||||
NSString* user = _GetOption(_options, GCDWebServerOption_AuthenticationUser, @"");
|
||||
NSString* password = _GetOption(_options, GCDWebServerOption_AuthenticationPassword, @"");
|
||||
_authenticationBasicAccount = ARC_RETAIN(_EncodeBase64([NSString stringWithFormat:@"%@:%@", user, password]));
|
||||
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
|
||||
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
|
||||
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
|
||||
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
|
||||
}];
|
||||
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
|
||||
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
|
||||
NSString* user = _GetOption(_options, GCDWebServerOption_AuthenticationUser, @"");
|
||||
NSString* password = _GetOption(_options, GCDWebServerOption_AuthenticationPassword, @"");
|
||||
_authenticationDigestAccount = ARC_RETAIN(GCDWebServerComputeMD5Digest(@"%@:%@:%@", user, _authenticationRealm, password));
|
||||
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
|
||||
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
|
||||
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
|
||||
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username];
|
||||
}];
|
||||
}
|
||||
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
|
||||
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
||||
@ -508,10 +511,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
_serverName = nil;
|
||||
ARC_RELEASE(_authenticationRealm);
|
||||
_authenticationRealm = nil;
|
||||
ARC_RELEASE(_authenticationBasicAccount);
|
||||
_authenticationBasicAccount = nil;
|
||||
ARC_RELEASE(_authenticationDigestAccount);
|
||||
_authenticationDigestAccount = nil;
|
||||
ARC_RELEASE(_authenticationBasicAccounts);
|
||||
_authenticationBasicAccounts = nil;
|
||||
ARC_RELEASE(_authenticationDigestAccounts);
|
||||
_authenticationDigestAccounts = nil;
|
||||
|
||||
LOG_INFO(@"%@ stopped", [self class]);
|
||||
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
||||
|
@ -717,36 +717,43 @@ static NSString* _StringFromAddressData(NSData* data) {
|
||||
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
|
||||
LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
||||
GCDWebServerResponse* response = nil;
|
||||
if (_server.authenticationBasicAccount) {
|
||||
BOOL authenticated = NO;
|
||||
if (_server.authenticationBasicAccounts) {
|
||||
__block BOOL authenticated = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Basic "]) {
|
||||
NSString* basicAccount = [authorizationHeader substringFromIndex:6];
|
||||
if ([basicAccount isEqualToString:_server.authenticationBasicAccount]) {
|
||||
authenticated = YES;
|
||||
}
|
||||
[_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) {
|
||||
if ([basicAccount isEqualToString:digest]) {
|
||||
authenticated = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
if (!authenticated) {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||
[response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
|
||||
}
|
||||
} else if (_server.authenticationDigestAccount) {
|
||||
} else if (_server.authenticationDigestAccounts) {
|
||||
BOOL authenticated = NO;
|
||||
BOOL isStaled = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Digest "]) {
|
||||
NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
|
||||
if ([nonce isEqualToString:_digestAuthenticationNonce]) { // TODO: Also check "realm" and "username" provided by client
|
||||
NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
|
||||
NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
|
||||
NSString* ha1 = _server.authenticationDigestAccount;
|
||||
NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required
|
||||
NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
|
||||
if ([actualResponse isEqualToString:expectedResponse]) {
|
||||
authenticated = YES;
|
||||
NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm");
|
||||
if ([realm isEqualToString:_server.authenticationRealm]) {
|
||||
NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
|
||||
if ([nonce isEqualToString:_digestAuthenticationNonce]) {
|
||||
NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username");
|
||||
NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
|
||||
NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
|
||||
NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username];
|
||||
NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required
|
||||
NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
|
||||
if ([actualResponse isEqualToString:expectedResponse]) {
|
||||
authenticated = YES;
|
||||
}
|
||||
} else if (nonce.length) {
|
||||
isStaled = YES;
|
||||
}
|
||||
} else if (nonce.length) {
|
||||
isStaled = YES;
|
||||
}
|
||||
}
|
||||
if (!authenticated) {
|
||||
|
@ -131,8 +131,8 @@ extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_F
|
||||
@property(nonatomic, readonly) NSArray* handlers;
|
||||
@property(nonatomic, readonly) NSString* serverName;
|
||||
@property(nonatomic, readonly) NSString* authenticationRealm;
|
||||
@property(nonatomic, readonly) NSString* authenticationBasicAccount;
|
||||
@property(nonatomic, readonly) NSString* authenticationDigestAccount;
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationBasicAccounts;
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationDigestAccounts;
|
||||
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||
|
16
Mac/main.m
16
Mac/main.m
@ -300,16 +300,14 @@ int main(int argc, const char* argv[]) {
|
||||
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
||||
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||
if ([authenticationMethod isEqualToString:@"Basic"]) {
|
||||
[options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod];
|
||||
if (authenticationUser && authenticationPassword) {
|
||||
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
||||
[options setValue:authenticationUser forKey:GCDWebServerOption_AuthenticationUser];
|
||||
[options setValue:authenticationPassword forKey:GCDWebServerOption_AuthenticationPassword];
|
||||
} else if ([authenticationMethod isEqualToString:@"Digest"]) {
|
||||
[options setObject:GCDWebServerAuthenticationMethod_DigestAccess forKey:GCDWebServerOption_AuthenticationMethod];
|
||||
[options setValue:authenticationRealm forKey:GCDWebServerOption_AuthenticationRealm];
|
||||
[options setValue:authenticationUser forKey:GCDWebServerOption_AuthenticationUser];
|
||||
[options setValue:authenticationPassword forKey:GCDWebServerOption_AuthenticationPassword];
|
||||
[options setObject:@{authenticationUser: authenticationPassword} forKey:GCDWebServerOption_AuthenticationAccounts];
|
||||
if ([authenticationMethod isEqualToString:@"Basic"]) {
|
||||
[options setObject:GCDWebServerAuthenticationMethod_Basic forKey:GCDWebServerOption_AuthenticationMethod];
|
||||
} else if ([authenticationMethod isEqualToString:@"Digest"]) {
|
||||
[options setObject:GCDWebServerAuthenticationMethod_DigestAccess forKey:GCDWebServerOption_AuthenticationMethod];
|
||||
}
|
||||
}
|
||||
if ([webServer runWithOptions:options]) {
|
||||
result = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user