]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m
bump version numbers for Xcode build
[sope] / sope-appserver / NGObjWeb / SoObjects / SoHTTPAuthenticator.m
1 /*
2   Copyright (C) 2002-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "SoHTTPAuthenticator.h"
23 #include "SoUser.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>
31 #include "common.h"
32
33 #if APPLE_RUNTIME || NeXT_RUNTIME
34 @interface NSObject(Miss)
35 - (void)subclassResponsibility:(SEL)cmd;
36 @end
37 #endif
38
39 @implementation SoHTTPAuthenticator
40
41 + (int)version {
42   return 1;
43 }
44
45 /* HTTP basic authentication */
46
47 - (NSString *)authRealm {
48   return [(WOApplication *)[WOApplication application] name];
49 }
50
51 /* check for roles */
52
53 - (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd {
54   [self subclassResponsibility:_cmd];
55   return NO;
56 }
57
58 + (NSArray *)parseCredentials:(NSString *)_creds {
59   /*
60     returns an array containing two items, user and password.
61   */
62   NSRange  rng;
63   NSString *login, *pwd;
64   NSString *k;
65   
66   if ([_creds length] == 0) {
67     static NSArray *anon = nil;
68     if (anon == nil)
69       anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil];
70     return anon;
71   }
72   if ([_creds length] < 6) {
73     [self logWithFormat:@"cannot handle authentication token: %@", _creds];
74     return nil;
75   }
76   
77   k = [[_creds substringToIndex:5] lowercaseString];
78   if (![k hasPrefix:@"basic"]) {
79     [self logWithFormat:@"tried unknown authentication method: %@", _creds];
80     return nil;
81   }
82   
83   k = [_creds substringFromIndex:6];
84   k = [k stringByDecodingBase64];
85   if (k == nil) return nil;
86
87   rng = [k rangeOfString:@":"];
88   if (rng.length <= 0) {
89     [self logWithFormat:@"got malformed basic credentials!"];
90     return nil;
91   }
92   login = [k substringToIndex:rng.location];
93   pwd   = [k substringFromIndex:(rng.location + rng.length)];
94   
95   rng = [login rangeOfString:@"\\"];
96   if (rng.length > 0) {
97     [self debugWithFormat:@"splitting of domain in login: '%@'", login];
98     login = [login substringFromIndex:(rng.location + rng.length)];
99   }
100   return [NSArray arrayWithObjects:login, pwd, nil];
101 }
102 - (NSArray *)parseCredentials:(NSString *)_creds {
103   return [[self class] parseCredentials:_creds];
104 }
105
106 - (NSString *)checkCredentials:(NSString *)_creds {
107   /* checks credentials, returnes login if successful */
108   NSString *login, *pwd;
109   NSArray  *creds;
110   
111   if ((creds = [self parseCredentials:_creds]) == nil)
112     return nil;
113
114   login = [creds objectAtIndex:0];
115   if ([login isEqualToString:@"anonymous"])
116     return @"anonymous";
117   
118   pwd = [creds objectAtIndex:1];
119   if (![self checkLogin:login password:pwd])
120     return nil;
121   
122   return login;
123 }
124
125 - (NSString *)checkCredentialsInContext:(WOContext *)_ctx {
126   WORequest *rq;
127   NSString  *auth;
128   
129   rq = [_ctx request];
130   if ((auth = [rq headerForKey:@"authorization"]) == nil) {
131     /* no auth supplied */
132     return @"anonymous";
133   }
134   return [self checkCredentials:auth];
135 }
136
137 - (NSArray *)rolesForLogin:(NSString *)_login {
138   NSArray *uroles = nil;
139   
140   // could add manager of login=root
141   
142   uroles = [NSArray arrayWithObjects:
143                       SoRole_Authenticated,
144                       SoRole_Anonymous,
145                       nil];
146   return uroles;
147 }
148
149 - (SoUser *)userInContext:(WOContext *)_ctx {
150   static SoUser *anonymous = nil;
151   NSString  *login;
152   NSArray   *uroles;
153   
154   if (anonymous == nil) {
155     NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous];
156     anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar];
157   }
158   
159   if ((login = [self checkCredentialsInContext:_ctx]) == nil)
160     /* some error (otherwise result would have been anonymous */
161     return nil;
162   
163   if ([login isEqualToString:@"anonymous"])
164     return anonymous;
165   
166   uroles = [self rolesForLogin:login];
167   return [[[SoUser alloc] initWithLogin:login roles:uroles] autorelease];
168 }
169
170 - (WOResponse *)unauthorized:(NSString *)_reason inContext:(WOContext *)_ctx {
171   WOResponse *r;
172   NSString *auth;
173
174   if ([_reason length] == 0) _reason = @"Unauthorized";
175   
176   auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
177   
178   r = [_ctx response];
179   [r setStatus:401 /* unauthorized */];
180   [r setHeader:auth forKey:@"www-authenticate"];
181   [r appendContentString:_reason];
182   return r;
183 }
184
185 - (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx {
186   WOResponse *r;
187   NSString *auth;
188   NSString *k;
189   NSString *user, *pwd;
190   NSRange rng;
191
192   if ((auth = [[_ctx request] headerForKey:@"authorization"]) == nil) {
193     /* no authentication provided */
194     static NSArray *anon = nil;
195     if (anon == nil)
196       anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil];
197     
198     [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"];
199     return nil;
200   }
201   
202   /* authentication provided, check whether it's valid */
203   
204   r = [_ctx response];
205   if ([auth length] < 6) {
206     [self logWithFormat:@"tried unknown authentication method: %@ (A)", auth];
207     return [self unauthorized:@"unsupported authentication method"
208                  inContext:_ctx];
209   }
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"
214                  inContext:_ctx];
215   }
216   
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"
221                  inContext:_ctx];
222   }
223
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];
228   }
229   
230   user = [k substringToIndex:rng.location];
231   pwd  = [k substringFromIndex:(rng.location + rng.length)];
232   
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)];
237   }
238   
239   if ([user length] == 0) {
240     [self logWithFormat:@"got malformed basic credentials!"];
241     return [self unauthorized:@"empty login in credentials?" inContext:_ctx];
242   }
243   if ([pwd length] == 0) {
244     [self logWithFormat:@"got empty password for user '%@'!", user];
245     return [self unauthorized:@"empty passwords unsupported!" inContext:_ctx];
246   }
247   
248   /* authenticate valid credentials */
249   
250   if (![self checkLogin:user password:pwd]) {
251     [self logWithFormat:@"tried wrong password for user '%@'!", user];
252     return [self unauthorized:nil inContext:_ctx];
253   }
254   
255   //[self debugWithFormat:@"authenticated user '%@'", user];
256   
257   /* authentication succeeded */
258   {
259     static NSArray *auth = nil;
260     if (auth == nil) {
261       auth = [[NSArray alloc] initWithObjects:
262                                 SoRole_Authenticated, SoRole_Anonymous, nil];
263     }
264     [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"];
265   }
266   return nil;
267 }
268
269 /* render auth exceptions */
270
271 - (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx {
272   if ([_e httpStatus] == 401) {
273     WOResponse *r;
274     NSString   *auth;
275     
276     r = [_ctx response];
277     auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
278     [r setStatus:[_e httpStatus] /* unauthorized */];
279     [r setHeader:auth forKey:@"www-authenticate"];
280     return YES;
281   }
282   return NO;
283 }
284
285 @end /* SoHTTPAuthenticator */