Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Go Httpbin
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Jira
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Container registry
Monitor
Service Desk
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
OSS
Nix
Go Httpbin
Commits
71a6a044
Commit
71a6a044
authored
7 years ago
by
Will McCutchen
Browse files
Options
Downloads
Patches
Plain Diff
Add tests for digest package
parent
3ed4a706
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
Makefile
+1
-1
1 addition, 1 deletion
Makefile
httpbin/digest/digest.go
+14
-13
14 additions, 13 deletions
httpbin/digest/digest.go
httpbin/digest/digest_test.go
+253
-0
253 additions, 0 deletions
httpbin/digest/digest_test.go
with
268 additions
and
14 deletions
Makefile
+
1
−
1
View file @
71a6a044
...
@@ -8,7 +8,7 @@ assets: httpbin/assets/*
...
@@ -8,7 +8,7 @@ assets: httpbin/assets/*
go-bindata
-o
httpbin/assets.go
-pkg
=
httpbin
-prefix
=
httpbin/assets httpbin/assets
go-bindata
-o
httpbin/assets.go
-pkg
=
httpbin
-prefix
=
httpbin/assets httpbin/assets
test
:
assets
test
:
assets
go
test
github.com/mccutchen/go-httpbin/httpbin
go
test
./...
testcover
:
assets
testcover
:
assets
mkdir
-p
dist
mkdir
-p
dist
...
...
This diff is collapsed.
Click to expand it.
httpbin/digest/digest.go
+
14
−
13
View file @
71a6a044
...
@@ -24,11 +24,9 @@ import (
...
@@ -24,11 +24,9 @@ import (
// Check returns a bool indicating whether the request is correctly
// Check returns a bool indicating whether the request is correctly
// authenticated for the given username and password.
// authenticated for the given username and password.
//
// TODO: use constant-time equality comparison.
func
Check
(
req
*
http
.
Request
,
username
,
password
string
)
bool
{
func
Check
(
req
*
http
.
Request
,
username
,
password
string
)
bool
{
auth
:=
parseAuthorizationHeader
(
req
.
Header
.
Get
(
"Authorization"
))
auth
:=
parseAuthorizationHeader
(
req
.
Header
.
Get
(
"Authorization"
))
if
auth
==
nil
{
if
auth
==
nil
||
auth
.
username
!=
username
{
return
false
return
false
}
}
expectedResponse
:=
response
(
auth
,
password
,
req
.
Method
,
req
.
RequestURI
)
expectedResponse
:=
response
(
auth
,
password
,
req
.
Method
,
req
.
RequestURI
)
...
@@ -116,19 +114,22 @@ func parseAuthorizationHeader(value string) *authorization {
...
@@ -116,19 +114,22 @@ func parseAuthorizationHeader(value string) *authorization {
// parseDictHeader is a simplistic, buggy, and incomplete implementation of
// parseDictHeader is a simplistic, buggy, and incomplete implementation of
// parsing key-value pairs from a header value into a map.
// parsing key-value pairs from a header value into a map.
func
parseDictHeader
(
value
string
)
map
[
string
]
string
{
func
parseDictHeader
(
value
string
)
map
[
string
]
string
{
res
:=
make
(
map
[
string
]
string
)
pairs
:=
strings
.
Split
(
value
,
","
)
for
_
,
pair
:=
range
strings
.
Split
(
value
,
","
)
{
res
:=
make
(
map
[
string
]
string
,
len
(
pairs
))
parts
:=
strings
.
SplitN
(
pair
,
"="
,
2
)
for
_
,
pair
:=
range
pairs
{
if
len
(
parts
)
==
1
{
parts
:=
strings
.
SplitN
(
strings
.
TrimSpace
(
pair
),
"="
,
2
)
res
[
parts
[
0
]]
=
""
key
:=
strings
.
TrimSpace
(
parts
[
0
])
if
len
(
key
)
==
0
{
continue
continue
}
}
key
:=
parts
[
0
]
val
:=
""
val
:=
parts
[
1
]
if
len
(
parts
)
>
1
{
if
strings
.
HasPrefix
(
val
,
`"`
)
&&
strings
.
HasSuffix
(
val
,
`"`
)
{
val
=
strings
.
TrimSpace
(
parts
[
1
])
val
=
val
[
1
:
len
(
val
)
-
1
]
if
strings
.
HasPrefix
(
val
,
`"`
)
&&
strings
.
HasSuffix
(
val
,
`"`
)
{
val
=
val
[
1
:
len
(
val
)
-
1
]
}
}
}
res
[
strings
.
TrimSpace
(
key
)
]
=
strings
.
TrimSpace
(
val
)
res
[
key
]
=
val
}
}
return
res
return
res
}
}
...
...
This diff is collapsed.
Click to expand it.
httpbin/digest/digest_test.go
0 → 100644
+
253
−
0
View file @
71a6a044
package
digest
import
(
"crypto"
"fmt"
"net/http"
"reflect"
"testing"
)
// Well-formed examples from Wikipedia:
// https://en.wikipedia.org/wiki/Digest_access_authentication#Example_with_explanation
const
(
exampleUsername
=
"Mufasa"
examplePassword
=
"Circle Of Life"
exampleChallenge
string
=
`Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"`
exampleAuthorization
string
=
`Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"`
)
func
assertStringEquals
(
t
*
testing
.
T
,
expected
,
got
string
)
{
if
expected
!=
got
{
t
.
Errorf
(
"Expected %#v, got %#v"
,
expected
,
got
)
}
}
func
buildRequest
(
method
,
uri
,
authHeader
string
)
*
http
.
Request
{
req
,
_
:=
http
.
NewRequest
(
method
,
uri
,
nil
)
req
.
RequestURI
=
uri
if
authHeader
!=
""
{
req
.
Header
.
Set
(
"Authorization"
,
authHeader
)
}
return
req
}
func
TestCheck
(
t
*
testing
.
T
)
{
t
.
Run
(
"missing authorization"
,
func
(
t
*
testing
.
T
)
{
req
:=
buildRequest
(
"GET"
,
"/dir/index.html"
,
""
)
if
Check
(
req
,
exampleUsername
,
examplePassword
)
!=
false
{
t
.
Error
(
"Missing Authorization header should fail"
)
}
})
t
.
Run
(
"wrong username"
,
func
(
t
*
testing
.
T
)
{
req
:=
buildRequest
(
"GET"
,
"/dir/index.html"
,
exampleAuthorization
)
if
Check
(
req
,
"Simba"
,
examplePassword
)
!=
false
{
t
.
Error
(
"Incorrect username should fail"
)
}
})
t
.
Run
(
"wrong password"
,
func
(
t
*
testing
.
T
)
{
req
:=
buildRequest
(
"GET"
,
"/dir/index.html"
,
exampleAuthorization
)
if
Check
(
req
,
examplePassword
,
"foobar"
)
!=
false
{
t
.
Error
(
"Incorrect password should fail"
)
}
})
t
.
Run
(
"ok"
,
func
(
t
*
testing
.
T
)
{
req
:=
buildRequest
(
"GET"
,
"/dir/index.html"
,
exampleAuthorization
)
if
Check
(
req
,
exampleUsername
,
examplePassword
)
!=
true
{
t
.
Error
(
"Correct credentials should pass"
)
}
})
}
func
TestResponse
(
t
*
testing
.
T
)
{
auth
:=
parseAuthorizationHeader
(
exampleAuthorization
)
expected
:=
auth
.
response
got
:=
response
(
auth
,
examplePassword
,
"GET"
,
"/dir/index.html"
)
assertStringEquals
(
t
,
expected
,
got
)
}
func
TestHash
(
t
*
testing
.
T
)
{
var
tests
=
[]
struct
{
algorithm
crypto
.
Hash
data
[]
byte
expected
string
}{
{
crypto
.
SHA256
,
[]
byte
(
"hello, world!
\n
"
),
"4dca0fd5f424a31b03ab807cbae77eb32bf2d089eed1cee154b3afed458de0dc"
},
{
crypto
.
MD5
,
[]
byte
(
"hello, world!
\n
"
),
"910c8bc73110b0cd1bc5d2bcae782511"
},
// Any unhandled hash results in MD5 being used
{
crypto
.
MD4
,
[]
byte
(
"hello, world!
\n
"
),
"910c8bc73110b0cd1bc5d2bcae782511"
},
{
crypto
.
SHA512
,
[]
byte
(
"hello, world!
\n
"
),
"910c8bc73110b0cd1bc5d2bcae782511"
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
fmt
.
Sprintf
(
"hash/%v"
,
test
.
algorithm
),
func
(
t
*
testing
.
T
)
{
result
:=
hash
(
test
.
data
,
test
.
algorithm
)
assertStringEquals
(
t
,
test
.
expected
,
result
)
})
}
}
func
TestCompare
(
t
*
testing
.
T
)
{
if
compare
(
"foo"
,
"bar"
)
!=
false
{
t
.
Error
(
"Expected foo != bar"
)
}
if
compare
(
"foo"
,
"foo"
)
!=
true
{
t
.
Error
(
"Expected foo == foo"
)
}
}
func
TestParseDictHeader
(
t
*
testing
.
T
)
{
var
tests
=
[]
struct
{
input
string
expected
map
[
string
]
string
}{
{
"foo=bar"
,
map
[
string
]
string
{
"foo"
:
"bar"
}},
// keys without values get the empty string
{
"foo"
,
map
[
string
]
string
{
"foo"
:
""
}},
{
"foo=bar, baz"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
""
}},
// no spaces required
{
"foo=bar,baz=quux"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
// spaces are stripped
{
"foo=bar, baz=quux"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
{
"foo= bar, baz=quux"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
{
"foo=bar, baz = quux"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
{
" foo =bar, baz=quux"
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
{
"foo=bar,baz = quux "
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
"quux"
}},
// quotes around values are stripped
{
`foo="bar two three four", baz=quux`
,
map
[
string
]
string
{
"foo"
:
"bar two three four"
,
"baz"
:
"quux"
}},
{
`foo=bar, baz=""`
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
""
}},
// quotes around keys are not stripped
{
`"foo"="bar", "baz two"=quux`
,
map
[
string
]
string
{
`"foo"`
:
"bar"
,
`"baz two"`
:
"quux"
}},
// spaces within quotes around values are preserved
{
`foo=bar, baz=" quux "`
,
map
[
string
]
string
{
"foo"
:
"bar"
,
"baz"
:
" quux "
}},
// commas values are NOT handled correctly
{
`foo="one, two, three", baz=quux`
,
map
[
string
]
string
{
"foo"
:
`"one`
,
"two"
:
""
,
`three"`
:
""
,
"baz"
:
"quux"
}},
{
",,,"
,
make
(
map
[
string
]
string
)},
// trailing comma is okay
{
"foo=bar,"
,
map
[
string
]
string
{
"foo"
:
"bar"
}},
{
"foo=bar, "
,
map
[
string
]
string
{
"foo"
:
"bar"
}},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
input
,
func
(
t
*
testing
.
T
)
{
results
:=
parseDictHeader
(
test
.
input
)
if
!
reflect
.
DeepEqual
(
test
.
expected
,
results
)
{
t
.
Errorf
(
"expected %#v, got %#v"
,
test
.
expected
,
results
)
}
})
}
}
func
TestParseAuthorizationHeader
(
t
*
testing
.
T
)
{
var
tests
=
[]
struct
{
input
string
expected
*
authorization
}{
{
""
,
nil
},
{
"Digest"
,
nil
},
{
"Basic QWxhZGRpbjpPcGVuU2VzYW1l"
,
nil
},
// case sensitive on Digest
{
"digest username=u, realm=r, nonce=n"
,
nil
},
// incomplete headers are fine
{
"Digest username=u, realm=r, nonce=n"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
realm
:
"r"
,
nonce
:
"n"
,
}},
// algorithm can be either MD5 or SHA-256, with MD5 as default
{
"Digest username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
"Digest algorithm=MD5, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
"Digest algorithm=md5, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
"Digest algorithm=SHA-256, username=u"
,
&
authorization
{
algorithm
:
crypto
.
SHA256
,
username
:
"u"
,
}},
{
"Digest algorithm=foo, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
"Digest algorithm=SHA-512, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
// algorithm not case sensitive
{
"Digest algorithm=sha-256, username=u"
,
&
authorization
{
algorithm
:
crypto
.
SHA256
,
username
:
"u"
,
}},
// but dash is required in SHA-256 is not recognized
{
"Digest algorithm=SHA256, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
// session variants not recognized
{
"Digest algorithm=SHA-256-sess, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
"Digest algorithm=MD5-sess, username=u"
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
username
:
"u"
,
}},
{
exampleAuthorization
,
&
authorization
{
algorithm
:
crypto
.
MD5
,
cnonce
:
"0a4f113b"
,
nc
:
"00000001"
,
nonce
:
"dcd98b7102dd2f0e8b11d0f600bfb0c093"
,
opaque
:
"5ccc069c403ebaf9f0171e9517f40e41"
,
qop
:
"auth"
,
realm
:
"testrealm@host.com"
,
response
:
"6629fae49393a05397450978507c4ef1"
,
uri
:
"/dir/index.html"
,
username
:
exampleUsername
,
}},
}
for
i
,
test
:=
range
tests
{
t
.
Run
(
fmt
.
Sprintf
(
"%d"
,
i
),
func
(
t
*
testing
.
T
)
{
got
:=
parseAuthorizationHeader
(
test
.
input
)
if
!
reflect
.
DeepEqual
(
test
.
expected
,
got
)
{
t
.
Errorf
(
"expected %#v, got %#v"
,
test
.
expected
,
got
)
}
})
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment