2 Copyright (C) 2002-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "SoHTTPAuthenticator.h"
24 #include "SoPermissions.h"
25 #include "NSException+HTTP.h"
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WORequest.h>
28 #include <NGObjWeb/WOResponse.h>
29 #include <NGObjWeb/WOContext.h>
30 #include <NGExtensions/NSString+Ext.h>
33 #if APPLE_RUNTIME || NeXT_RUNTIME
34 @interface NSObject(Miss)
35 - (void)subclassResponsibility:(SEL)cmd;
39 @implementation SoHTTPAuthenticator
45 /* HTTP basic authentication */
47 - (NSString *)authRealm {
48 return [(WOApplication *)[WOApplication application] name];
53 - (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd {
54 [self subclassResponsibility:_cmd];
58 + (NSArray *)parseCredentials:(NSString *)_creds {
60 returns an array containing two items, user and password.
63 NSString *login, *pwd;
66 if ([_creds length] == 0) {
67 static NSArray *anon = nil;
69 anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil];
72 if ([_creds length] < 6) {
73 [self logWithFormat:@"cannot handle authentication token: %@", _creds];
77 k = [[_creds substringToIndex:5] lowercaseString];
78 if (![k hasPrefix:@"basic"]) {
79 [self logWithFormat:@"tried unknown authentication method: %@", _creds];
83 k = [_creds substringFromIndex:6];
84 k = [k stringByDecodingBase64];
85 if (k == nil) return nil;
87 rng = [k rangeOfString:@":"];
88 if (rng.length <= 0) {
89 [self logWithFormat:@"got malformed basic credentials!"];
92 login = [k substringToIndex:rng.location];
93 pwd = [k substringFromIndex:(rng.location + rng.length)];
95 rng = [login rangeOfString:@"\\"];
97 [self debugWithFormat:@"splitting of domain in login: '%@'", login];
98 login = [login substringFromIndex:(rng.location + rng.length)];
100 return [NSArray arrayWithObjects:login, pwd, nil];
102 - (NSArray *)parseCredentials:(NSString *)_creds {
103 return [[self class] parseCredentials:_creds];
106 - (NSString *)checkCredentials:(NSString *)_creds {
107 /* checks credentials, returnes login if successful */
108 NSString *login, *pwd;
111 if ((creds = [self parseCredentials:_creds]) == nil)
114 login = [creds objectAtIndex:0];
115 if ([login isEqualToString:@"anonymous"])
118 pwd = [creds objectAtIndex:1];
119 if (![self checkLogin:login password:pwd])
125 - (NSString *)checkCredentialsInContext:(WOContext *)_ctx {
130 if ((auth = [rq headerForKey:@"authorization"]) == nil) {
131 /* no auth supplied */
134 return [self checkCredentials:auth];
137 - (NSArray *)rolesForLogin:(NSString *)_login {
138 NSArray *uroles = nil;
140 // could add manager of login=root
142 uroles = [NSArray arrayWithObjects:
143 SoRole_Authenticated,
149 - (SoUser *)userInContext:(WOContext *)_ctx {
150 static SoUser *anonymous = nil;
154 if (anonymous == nil) {
155 NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous];
156 anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar];
159 if ((login = [self checkCredentialsInContext:_ctx]) == nil)
160 /* some error (otherwise result would have been anonymous */
163 if ([login isEqualToString:@"anonymous"])
166 uroles = [self rolesForLogin:login];
167 return [[[SoUser alloc] initWithLogin:login roles:uroles] autorelease];
170 - (WOResponse *)unauthorized:(NSString *)_reason inContext:(WOContext *)_ctx {
174 if ([_reason length] == 0) _reason = @"Unauthorized";
176 auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
179 [r setStatus:401 /* unauthorized */];
180 [r setHeader:auth forKey:@"www-authenticate"];
181 [r appendContentString:_reason];
185 - (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx {
189 NSString *user, *pwd;
192 if ((auth = [[_ctx request] headerForKey:@"authorization"]) == nil) {
193 /* no authentication provided */
194 static NSArray *anon = nil;
196 anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil];
198 [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"];
202 /* authentication provided, check whether it's valid */
205 if ([auth length] < 6) {
206 [self logWithFormat:@"tried unknown authentication method: %@ (A)", auth];
207 return [self unauthorized:@"unsupported authentication method"
210 k = [[auth substringToIndex:5] lowercaseString];
211 if (![k hasPrefix:@"basic"]) {
212 [self logWithFormat:@"tried unknown authentication method: %@ (B)", auth];
213 return [self unauthorized:@"unsupported authentication method"
217 k = [auth substringFromIndex:6];
218 if ((k = [k stringByDecodingBase64]) == nil) {
219 [self logWithFormat:@"tried unknown authentication method: %@ (C)", auth];
220 return [self unauthorized:@"unsupported authentication method"
224 rng = [k rangeOfString:@":"];
225 if (rng.length <= 0) {
226 [self logWithFormat:@"got malformed basic credentials (missing colon)!"];
227 return [self unauthorized:@"malformed basic credentials!" inContext:_ctx];
230 user = [k substringToIndex:rng.location];
231 pwd = [k substringFromIndex:(rng.location + rng.length)];
233 rng = [user rangeOfString:@"\\"];
234 if (rng.length > 0) {
235 [self debugWithFormat:@"splitting of domain in user: '%@'", user];
236 user = [user substringFromIndex:(rng.location + rng.length)];
239 if ([user length] == 0) {
240 [self logWithFormat:@"got malformed basic credentials!"];
241 return [self unauthorized:@"empty login in credentials?" inContext:_ctx];
243 if ([pwd length] == 0) {
244 [self logWithFormat:@"got empty password for user '%@'!", user];
245 return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx];
248 /* authenticate valid credentials */
250 if (![self checkLogin:user password:pwd]) {
251 [self logWithFormat:@"tried wrong password for user '%@'!", user];
252 return [self unauthorized:nil inContext:_ctx];
255 //[self debugWithFormat:@"authenticated user '%@'", user];
257 /* authentication succeeded */
259 static NSArray *auth = nil;
261 auth = [[NSArray alloc] initWithObjects:
262 SoRole_Authenticated, SoRole_Anonymous, nil];
264 [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"];
269 /* render auth exceptions */
271 - (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx {
272 if ([_e httpStatus] == 401) {
277 auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
278 [r setStatus:[_e httpStatus] /* unauthorized */];
279 [r setHeader:auth forKey:@"www-authenticate"];
285 @end /* SoHTTPAuthenticator */