root/LW/LWDigestAuthentication.lua

Revision 815 (checked in by rsz, 3 years ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               LWDigestAuthentication.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       February 1, 2005
6 -- Legal:               Copyright (C) 2005 Raphaël Szwarc
7 --------------------------------------------------------------------------------
8
9 -- import dependencies
10 local LUObject = require( "LUObject" )
11 local LUCrypto = require( "LUCrypto" )
12 local LUString = require( "LUString" )
13 local MIMEBase64 = require( "MIMEBase64" )
14 local os = require( "os" )
15
16 require( "md5" )
17
18 -- define the class
19 local super = LUObject
20 local self = super()
21
22 -- constants
23 local Key = LUCrypto:key()
24        
25 -- method to create a nonce
26 function self:nonce( aContext )
27         local aTime = os.time()
28         local anAddress = aContext:request():reader():socket():getpeername()
29               anAddress = MIMEBase64:encode( anAddress )
30         local aKey = LUCrypto:sign( Key, aTime .. anAddress )
31               aKey = aKey:sub( 1, 32 )
32               aKey = MIMEBase64:encode( LUString:decode( aKey ) )
33         local aNonce = aTime .. ":" .. anAddress .. ":" .. aKey
34        
35         return aNonce
36 end
37
38 -- method to validate a given nonce
39 function self:validateNonce( aContext, aNonce )
40         if aNonce ~= nil then
41                 local aNonceTime, aNonceAddress, aNonceKey = aNonce:match( "(%d+):(.+):(.+)" )
42                
43                 if aNonceTime ~= nil and aNonceAddress ~= nil and aNonceKey ~= nil then
44                         local anInterval = os.time() - tonumber( aNonceTime )
45                        
46                         if anInterval >= 0 then
47                                 local anAddress = aContext:request():reader():socket():getpeername()
48                                
49                                 if anAddress == MIMEBase64:decode( aNonceAddress ) == true then
50                                         if MIMEBase64:decode( aNonceKey ):len() == 16 then
51                                                 return true
52                                         end
53                                 end
54                         end
55                 end
56         end
57        
58         return false
59 end
60
61 -- method to access this authentication password for a given user name and realm
62 function self:password( aContext, anUserName, aRealm )
63         return anUserName
64 end
65
66 -- method to access this authentication realm
67 function self:realm( aContext )
68         return aContext:request():headers():get( "host" ) or ""
69 end
70
71 -- method to check if the given name requires authentication
72 function self:requiresAuthentication( aContext, anUserName )
73         return true
74 end
75
76 -- method to validate the given authentication
77 function self:validateAuthentication( aContext, anUserName, aRealm, aPassword )
78         if anUserName ~= nil then
79                 return true
80         end
81        
82         return false
83 end
84
85 -- method to authenticate a given context
86 function self:authenticate( aContext )
87         local someAttributes = aContext:request():headerAttributes( "authorization" )
88         local anUserName = someAttributes:get( "username" )
89        
90         if self:requiresAuthentication( aContext, anUserName ) == true then
91                 local aRealm = someAttributes:get( "realm" )
92                
93                 if self:validateAuthentication( aContext, anUserName, aRealm ) == true then
94                         local aNonce = someAttributes:get( "nonce" )
95                        
96                         if self:validateNonce( aContext, aNonce ) == true then
97                                 local aResponse = someAttributes:get( "response" )
98                                 local aNonceCount = someAttributes:get( "nc" )
99                                 local aClientNonce = someAttributes:get( "cnonce" )
100                                 local aProtection = someAttributes:get( "qop" )
101                                 local anURI = someAttributes:get( "uri" )
102                                
103                                 if aResponse ~= nil
104                                         and aNonceCount ~= nil
105                                         and aClientNonce ~= nil
106                                         and aProtection == "auth"
107                                         and anURI == aContext:request():rawURI() then
108                                        
109                                         local aPassword = self:password( aContext, anUserName, aRealm )
110                                         local anUserDigest = md5.digest( anUserName .. ":" .. aRealm .. ":" .. aPassword )
111                                         local aMethod = aContext:request():method()
112                                         local aRequestDigest = md5.digest( aMethod .. ":" .. anURI )
113                                         local aDigest = md5.digest( anUserDigest .. ":" .. aNonce .. ":" .. aNonceCount .. ":" .. aClientNonce .. ":" .. aProtection .. ":" ..  aRequestDigest )
114                                        
115                                         if aDigest:lower() == aResponse:lower() then
116                                                 return true
117                                         else
118                                                 self:challenge( aContext )
119                                                
120                                                 return false
121                                         end
122                                 else
123                                         self:challenge( aContext )
124                                        
125                                         return false
126                                 end
127                         else
128                                 self:challenge( aContext )
129                                
130                                 return false
131                         end
132                 else
133                         self:challenge( aContext )
134                        
135                         return false
136                 end
137         else
138                 return true       
139         end
140 end
141
142 -- method to challenge a given context
143 function self:challenge( aContext )
144         local aChallenge = "Digest "
145        
146         aChallenge = aChallenge .. "realm=\"" .. self:realm( aContext ) .. "\", "
147         aChallenge = aChallenge .. "domain=\"" .. aContext:request():rawURI() .. "\", "
148         aChallenge = aChallenge .. "nonce=\"" .. self:nonce( aContext ) .. "\", "
149         aChallenge = aChallenge .. "algorithm=\"MD5\", "
150         aChallenge = aChallenge .. "qop=\"auth\""
151
152         aContext:response():headers():put( "www-authenticate", aChallenge )
153         aContext:response():writeStatus( 401 )
154 end
155
156 return self
Note: See TracBrowser for help on using the browser.