Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Rémi PAILHAREY
cyber-signal
Commits
33c62528
Commit
33c62528
authored
May 20, 2021
by
Rémi PAILHAREY
Browse files
Factorize rootmux + tests auth, rootmux
parent
e52e3195
Changes
13
Hide whitespace changes
Inline
Side-by-side
.vscode/launch.json
View file @
33c62528
...
...
@@ -12,7 +12,6 @@
"program"
:
"${workspaceFolder}"
,
"env"
:
{
"ADMIN_ROLE"
:
"ADMINS"
,
"HOSTNAME"
:
"localhost"
,
"INMEMORY_TOKEN_LIFE_DAYS"
:
"2"
,
"LOGOUT_URL"
:
"/"
}
...
...
internal/auth/auth.go
View file @
33c62528
...
...
@@ -27,7 +27,6 @@ const (
var
(
// AdminRole represents the role reserved for admins
AdminRole
=
common
.
StringValueFromEnv
(
"ADMIN_ROLE"
,
"ADMINS"
)
hostname
=
common
.
StringValueFromEnv
(
"HOSTNAME"
,
"localhost"
)
)
// User represents a logged in user
...
...
@@ -64,16 +63,11 @@ func ValidateAuthMiddleware(next http.Handler, allowedRoles []string, checkXSRF
return
}
// Default to redirect to authentication
redirectTo
:=
hostname
redirectTo
:=
"/"
_
,
port
,
perr
:=
net
.
SplitHostPort
(
r
.
Host
)
if
perr
==
nil
{
redirectTo
+=
":"
+
port
}
// Write the requested url in a cookie
if
r
.
Host
!=
redirectTo
{
cookie
:=
http
.
Cookie
{
Name
:
"redirectAfterLogin"
,
Domain
:
hostname
,
Value
:
r
.
Host
+
r
.
URL
.
Path
,
MaxAge
:
10
,
Secure
:
true
,
HttpOnly
:
false
,
SameSite
:
http
.
SameSiteLaxMode
}
http
.
SetCookie
(
w
,
&
cookie
)
}
w
.
Header
()
.
Set
(
"Content-Type"
,
"text/html"
)
w
.
WriteHeader
(
http
.
StatusUnauthorized
)
responseContent
:=
fmt
.
Sprintf
(
"error extracting token: %v<meta http-equiv=
\"
Refresh
\"
content=
\"
0; url=https://%v#login
\"
/>"
,
err
.
Error
(),
redirectTo
)
...
...
@@ -119,7 +113,6 @@ func HandleLogout(w http.ResponseWriter, r *http.Request) {
// Delete the auth cookie
c
:=
http
.
Cookie
{
Name
:
authTokenKey
,
Domain
:
hostname
,
MaxAge
:
-
1
,
}
http
.
SetCookie
(
w
,
&
c
)
...
...
internal/auth/auth_test.go
0 → 100644
View file @
33c62528
package
auth
import
(
"encoding/json"
"io/ioutil"
"net/http"
"os"
"reflect"
"testing"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/tester"
)
var
noH
map
[
string
]
string
func
TestCheckUserHasRole
(
t
*
testing
.
T
)
{
type
args
struct
{
user
TokenData
allowedRoles
[]
string
}
tests
:=
[]
struct
{
name
string
args
args
wantErr
bool
}{
{
"has_all_roles"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{
"role01"
,
"role02"
}}},
allowedRoles
:
[]
string
{
"role01"
,
"role02"
}},
false
},
{
"has_one_role"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{
"role01"
,
"role03"
}}},
allowedRoles
:
[]
string
{
"role01"
,
"role02"
}},
false
},
{
"has_not_role"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{
"role03"
,
"role04"
}}},
allowedRoles
:
[]
string
{
"role01"
,
"role02"
}},
true
},
{
"user_roles_are_empty"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{}}},
allowedRoles
:
[]
string
{
"role01"
,
"role02"
}},
true
},
{
"allowed_roles_are_empty"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{
"role01"
,
"role02"
}}},
allowedRoles
:
[]
string
{}},
true
},
{
"all_roles_are_empty"
,
args
{
user
:
TokenData
{
User
:
User
{
Roles
:
[]
string
{}}},
allowedRoles
:
[]
string
{}},
true
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
if
err
:=
checkUserHasRole
(
tt
.
args
.
user
,
tt
.
args
.
allowedRoles
);
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"checkUserHasRole() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
}
})
}
}
func
TestAddUser
(
t
*
testing
.
T
)
{
UsersFile
=
writeUsers
()
defer
os
.
Remove
(
UsersFile
)
handler
:=
http
.
HandlerFunc
(
AddUser
)
// Alter the password of the admin user, must create an hash
tester
.
DoRequestOnHandler
(
t
,
handler
,
"POST"
,
"/"
,
noH
,
`{"id":"1","login":"admin","password": "password"}`
,
http
.
StatusOK
,
`[{"id":"1","login":"admin","memberOf":null,"passwordHash":"$2a`
)
// Test that altering an user without altering the password keep the password hash
tester
.
DoRequestOnHandler
(
t
,
handler
,
"POST"
,
"/"
,
noH
,
`{"id":"1","login":"admin_altered"}`
,
http
.
StatusOK
,
`[{"id":"1","login":"admin_altered","memberOf":null,"passwordHash":"$2a`
)
// Add a new user with a password, must pass
tester
.
DoRequestOnHandler
(
t
,
handler
,
"POST"
,
"/"
,
noH
,
`{"id":"3","login":"user3","password": "password_user3"}`
,
http
.
StatusOK
,
`[{"id":"1","login":"admin`
)
// Add a new user with no password, must fail
tester
.
DoRequestOnHandler
(
t
,
handler
,
"POST"
,
"/"
,
noH
,
`{"id":"4","login":"user4","password": ""}`
,
http
.
StatusBadRequest
,
`password cannot be empty`
)
}
func
TestMatchUser
(
t
*
testing
.
T
)
{
UsersFile
=
"../../configs/users.json"
existingUser
:=
User
{
ID
:
"2"
,
Login
:
"user"
,
Roles
:
[]
string
{
"USERS"
},
PasswordHash
:
"$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"
}
veryLongString
,
_
:=
common
.
GenerateRandomString
(
10000
)
specialCharString
:=
"
\"
"
type
args
struct
{
sentUser
User
}
tests
:=
[]
struct
{
name
string
args
args
want
User
wantErr
bool
}{
{
"user_exists"
,
args
{
User
{
Login
:
"user"
,
Password
:
"password"
}},
existingUser
,
false
},
{
"user_does_not_exists"
,
args
{
User
{
Login
:
"notuser"
,
Password
:
"password"
}},
User
{},
true
},
{
"user_does_not_exists_and_wrong_password"
,
args
{
User
{
Login
:
"notuser"
,
Password
:
"wrongpassword"
}},
User
{},
true
},
{
"wrong_password"
,
args
{
User
{
Login
:
"user"
,
Password
:
"wrongpassword"
}},
User
{},
true
},
{
"no_password"
,
args
{
User
{
Login
:
"user"
,
Password
:
""
}},
User
{},
true
},
{
"empty_user"
,
args
{
User
{
Login
:
""
,
Password
:
"password"
}},
User
{},
true
},
{
"empty_user_and_password"
,
args
{
User
{
Login
:
""
,
Password
:
""
}},
User
{},
true
},
{
"very_long_string_as_user"
,
args
{
User
{
Login
:
veryLongString
,
Password
:
"password"
}},
User
{},
true
},
{
"very_long_string_as_password"
,
args
{
User
{
Login
:
"user"
,
Password
:
veryLongString
}},
User
{},
true
},
{
"very_long_string_as_user_and_password"
,
args
{
User
{
Login
:
veryLongString
,
Password
:
veryLongString
}},
User
{},
true
},
{
"special_char_string_as_user"
,
args
{
User
{
Login
:
specialCharString
,
Password
:
"password"
}},
User
{},
true
},
{
"special_char_string_as_password"
,
args
{
User
{
Login
:
"user"
,
Password
:
specialCharString
}},
User
{},
true
},
{
"special_char_string_as_user_and_password"
,
args
{
User
{
Login
:
specialCharString
,
Password
:
specialCharString
}},
User
{},
true
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
MatchUser
(
tt
.
args
.
sentUser
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"MatchUser() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
reflect
.
DeepEqual
(
got
,
tt
.
want
)
{
t
.
Errorf
(
"MatchUser() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
func
writeUsers
()
(
name
string
)
{
users
:=
[]
*
User
{
{
ID
:
"1"
,
Login
:
"admin"
},
{
ID
:
"2"
,
Login
:
"user"
},
}
f
,
err
:=
ioutil
.
TempFile
(
""
,
"users"
)
if
err
!=
nil
{
panic
(
err
)
}
defer
f
.
Close
()
err
=
json
.
NewEncoder
(
f
)
.
Encode
(
users
)
if
err
!=
nil
{
panic
(
err
)
}
return
f
.
Name
()
}
internal/auth/inmemory.go
View file @
33c62528
...
...
@@ -71,7 +71,7 @@ func HandleInMemoryLogin(w http.ResponseWriter, r *http.Request) {
return
}
tokenData
:=
TokenData
{
User
:
User
{
ID
:
user
.
ID
,
Login
:
user
.
Login
,
Email
:
user
.
Email
,
Roles
:
user
.
Roles
},
XSRFToken
:
xsrfToken
}
tokens
.
Manager
.
StoreData
(
tokenData
,
hostname
,
authTokenKey
,
tokenLifetime
,
w
)
tokens
.
Manager
.
StoreData
(
tokenData
,
authTokenKey
,
tokenLifetime
,
w
)
// Log the connexion
log
.
Logger
.
Printf
(
"| %v (%v %v) | Login success | %v | %v"
,
user
.
Login
,
user
.
Name
,
user
.
Surname
,
r
.
RemoteAddr
,
log
.
GetCityAndCountryFromRequest
(
r
))
}
...
...
internal/auth/inmemory_test.go
0 → 100644
View file @
33c62528
package
auth
import
(
"os"
"testing"
"time"
)
func
TestSetTokenLifetime
(
t
*
testing
.
T
)
{
type
args
struct
{
key
string
value
string
}
tests
:=
[]
struct
{
name
string
args
args
want
time
.
Duration
}{
{
"no environnement"
,
args
{
"OTHER_ENV"
,
"10"
},
24
*
time
.
Hour
},
{
"wrong type"
,
args
{
"INMEMORY_TOKEN_LIFE_DAYS"
,
"A_STRING"
},
24
*
time
.
Hour
},
{
"to small"
,
args
{
"INMEMORY_TOKEN_LIFE_DAYS"
,
"-1"
},
24
*
time
.
Hour
},
{
"to big"
,
args
{
"INMEMORY_TOKEN_LIFE_DAYS"
,
"11 000"
},
24
*
time
.
Hour
},
{
"ok"
,
args
{
"INMEMORY_TOKEN_LIFE_DAYS"
,
"3"
},
72
*
time
.
Hour
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
os
.
Setenv
(
tt
.
args
.
key
,
tt
.
args
.
value
)
if
got
:=
setTokenLifetime
();
got
!=
tt
.
want
{
t
.
Errorf
(
"setTokenLifetime() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
internal/rootmux/rootmux.go
0 → 100644
View file @
33c62528
package
rootmux
import
(
"net/http"
"os"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/auth"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/tokens"
"github.com/nicolaspernoud/vestibule/pkg/middlewares"
)
func
CreateRootMux
(
staticDir
string
)
http
.
Handler
{
mainMux
:=
http
.
NewServeMux
()
mainMux
.
HandleFunc
(
"/Logout"
,
auth
.
HandleLogout
)
mainMux
.
HandleFunc
(
"/Login"
,
auth
.
HandleInMemoryLogin
)
mainMux
.
Handle
(
"/"
,
middlewares
.
NoCache
(
http
.
FileServer
(
&
common
.
FallBackWrapper
{
Assets
:
http
.
Dir
(
staticDir
)})))
// commonMux := http.NewServeMux()
mainMux
.
Handle
(
"/api/common/WhoAmI"
,
auth
.
ValidateAuthMiddleware
(
auth
.
WhoAmI
(),
[]
string
{
"*"
},
false
))
// mainMux.Handle("/api/common/", http.StripPrefix("/api/common", auth.ValidateAuthMiddleware(commonMux, []string{"*"}, true)))
adminMux
:=
http
.
NewServeMux
()
adminMux
.
HandleFunc
(
"/newShareToken"
,
tokens
.
NewShareToken
)
adminMux
.
HandleFunc
(
"/users/"
,
auth
.
ProcessUsers
)
mainMux
.
Handle
(
"/api/admin/"
,
http
.
StripPrefix
(
"/api/admin"
,
auth
.
ValidateAuthMiddleware
(
adminMux
,
[]
string
{
os
.
Getenv
(
"ADMIN_ROLE"
)},
true
)))
return
mainMux
}
internal/rootmux/rootmux_test.go
0 → 100644
View file @
33c62528
package
rootmux
import
(
"encoding/json"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"os"
"testing"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/auth"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/tester"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/tokens"
)
var
(
// reg, _ = regexp.Compile("[\n \t]+")
// initialUsersBuff, _ = ioutil.ReadFile("../../configs/users.json")
// initialUsers = reg.ReplaceAllString(string(initialUsersBuff), "")
newUser
=
`{"id":"3","login":"new_user","memberOf":["USERS"],"password":"test"}`
noH
map
[
string
]
string
)
func
init
()
{
tokens
.
Init
(
"../../configs/tokenskey.json"
,
true
)
}
func
TestAll
(
t
*
testing
.
T
)
{
// Set the users file
auth
.
UsersFile
=
"../../configs/users.json"
os
.
Setenv
(
"ADMIN_ROLE"
,
"ADMINS"
)
os
.
Setenv
(
"LOGOUT_URL"
,
"/"
)
unloggedTests
(
t
)
userTests
(
t
)
adminTests
(
t
)
}
func
unloggedTests
(
t
*
testing
.
T
)
{
ts
,
do
:=
createTester
(
t
)
defer
ts
.
Close
()
// Try to get the users (must fail)
do
(
"GET"
,
"/api/admin/users/"
,
noH
,
""
,
http
.
StatusUnauthorized
,
"error extracting token"
)
// Try to create an user (must fail)
do
(
"POST"
,
"/api/admin/users/"
,
noH
,
newUser
,
http
.
StatusUnauthorized
,
"error extracting token"
)
// Try to delete an user (must fail)
do
(
"DELETE"
,
"/api/admin/users/0"
,
noH
,
""
,
http
.
StatusUnauthorized
,
"error extracting token"
)
// Try to access the main page (must pass)
do
(
"GET"
,
"/"
,
noH
,
""
,
http
.
StatusOK
,
"<!DOCTYPE html>"
)
// Try to get the user informations (must fail)
do
(
"GET"
,
"/api/common/WhoAmI"
,
noH
,
""
,
http
.
StatusUnauthorized
,
"error extracting token"
)
// Do a in memory login with an unknown user
do
(
"POST"
,
"/Login"
,
noH
,
`{"login": "unknownuser","password": "password"}`
,
http
.
StatusForbidden
,
`user not found`
)
// Do a in memory login with a known user but bad password
do
(
"POST"
,
"/Login"
,
noH
,
`{"login": "admin","password": "badpassword"}`
,
http
.
StatusForbidden
,
`user not found`
)
// Try to create a new sharetoken (must fail)
do
(
"POST"
,
"/api/admin/newShareToken"
,
noH
,
""
,
http
.
StatusUnauthorized
,
"error extracting token"
)
}
// TODO: Complete user and admin tests
func
userTests
(
t
*
testing
.
T
)
{
ts
,
do
:=
createTester
(
t
)
defer
ts
.
Close
()
tests
:=
func
()
{
// Get the XSRF Token
response
:=
do
(
"GET"
,
"/api/common/WhoAmI"
,
noH
,
""
,
http
.
StatusOK
,
""
)
token
:=
auth
.
TokenData
{}
json
.
Unmarshal
([]
byte
(
response
),
&
token
)
xsrfHeader
:=
map
[
string
]
string
{
"XSRF-TOKEN"
:
token
.
XSRFToken
}
// Try to get the users (must fail)
do
(
"GET"
,
"/api/admin/users/"
,
xsrfHeader
,
""
,
http
.
StatusForbidden
,
"no user role among"
)
// Try to create an user (must fail)
do
(
"POST"
,
"/api/admin/users/"
,
xsrfHeader
,
newUser
,
http
.
StatusForbidden
,
"no user role among"
)
// Try to delete an user (must fail)
do
(
"DELETE"
,
"/api/admin/users/0"
,
xsrfHeader
,
""
,
http
.
StatusForbidden
,
"no user role among"
)
// Try to create a new sharetoken (must fail)
do
(
"POST"
,
"/api/admin/newShareToken"
,
xsrfHeader
,
""
,
http
.
StatusForbidden
,
"no user role among"
)
}
// Do a in memory login with an known user
do
(
"POST"
,
"/Login"
,
nil
,
`{"login": "user","password": "password"}`
,
http
.
StatusOK
,
""
)
// Run the tests
tests
()
// Try to logout (must pass)
do
(
"GET"
,
"/Logout"
,
noH
,
""
,
http
.
StatusOK
,
""
)
}
func
adminTests
(
t
*
testing
.
T
)
{
ts
,
do
:=
createTester
(
t
)
defer
ts
.
Close
()
tests
:=
func
()
{
// Get the XSRF Token
response
:=
do
(
"GET"
,
"/api/common/WhoAmI"
,
noH
,
""
,
http
.
StatusOK
,
""
)
token
:=
auth
.
TokenData
{}
json
.
Unmarshal
([]
byte
(
response
),
&
token
)
xsrfHeader
:=
map
[
string
]
string
{
"XSRF-TOKEN"
:
token
.
XSRFToken
}
// Try to get the users (must pass)
do
(
"GET"
,
"/api/admin/users/"
,
xsrfHeader
,
""
,
http
.
StatusOK
,
`[{"id":"1",`
)
// Try to create an user (must pass)
do
(
"POST"
,
"/api/admin/users/"
,
xsrfHeader
,
newUser
,
http
.
StatusOK
,
`[{"id":"1",`
)
// Try to delete an user (must pass)
do
(
"DELETE"
,
"/api/admin/users/0"
,
xsrfHeader
,
""
,
http
.
StatusOK
,
`[{"id":"1",`
)
// Try to create a new sharetoken (must pass)
do
(
"POST"
,
"/api/admin/newShareToken"
,
xsrfHeader
,
""
,
http
.
StatusOK
,
""
)
}
// Do a in memory login with an known user
do
(
"POST"
,
"/Login"
,
nil
,
`{"login": "admin","password": "password"}`
,
http
.
StatusOK
,
""
)
// Run the tests
tests
()
// Try to logout (must pass)
do
(
"GET"
,
"/Logout"
,
noH
,
""
,
http
.
StatusOK
,
""
)
}
func
createTester
(
t
*
testing
.
T
)
(
*
httptest
.
Server
,
func
(
method
string
,
route
string
,
headers
map
[
string
]
string
,
payload
string
,
expectedStatus
int
,
expectedBody
string
)
string
)
{
// Create the server
mux
:=
CreateRootMux
(
"../../web"
)
ts
:=
httptest
.
NewServer
(
mux
)
url
,
_
:=
url
.
Parse
(
ts
.
URL
)
port
:=
url
.
Port
()
// Create the cookie jar
jar
,
_
:=
cookiejar
.
New
(
nil
)
// wrap the testing function
return
ts
,
tester
.
CreateServerTester
(
t
,
"localhost"
,
port
,
jar
)
}
internal/tester/tester.go
View file @
33c62528
package
tester
import
(
"context"
"io/ioutil"
"net"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
)
// DoRequestOnHandler does a request on a router (or handler) and check the response
...
...
@@ -26,3 +32,56 @@ func DoRequestOnHandler(t *testing.T, router http.Handler, method string, route
}
return
string
(
rr
.
Body
.
String
())
}
// DoRequestOnServer does a request on listening server
func
DoRequestOnServer
(
t
*
testing
.
T
,
hostname
string
,
port
string
,
jar
*
cookiejar
.
Jar
,
method
string
,
testURL
string
,
headers
map
[
string
]
string
,
payload
string
,
expectedStatus
int
,
expectedBody
string
)
string
{
dialer
:=
&
net
.
Dialer
{
Timeout
:
30
*
time
.
Second
,
KeepAlive
:
30
*
time
.
Second
,
DualStack
:
true
,
}
// or create your own transport, there's an example on godoc.
http
.
DefaultTransport
.
(
*
http
.
Transport
)
.
DialContext
=
func
(
ctx
context
.
Context
,
network
,
addr
string
)
(
net
.
Conn
,
error
)
{
return
dialer
.
DialContext
(
ctx
,
network
,
addr
)
}
if
strings
.
HasPrefix
(
testURL
,
"/"
)
{
testURL
=
"http://"
+
hostname
+
":"
+
port
+
testURL
}
else
{
u
,
_
:=
url
.
Parse
(
"http://"
+
testURL
)
testURL
=
"http://"
+
u
.
Host
+
":"
+
port
+
u
.
Path
+
"?"
+
u
.
RawQuery
}
req
,
err
:=
http
.
NewRequest
(
method
,
testURL
,
strings
.
NewReader
(
payload
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
for
i
,
v
:=
range
headers
{
req
.
Header
.
Set
(
i
,
v
)
}
var
client
*
http
.
Client
if
jar
!=
nil
{
client
=
&
http
.
Client
{
Jar
:
jar
}
}
else
{
client
=
&
http
.
Client
{}
}
res
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
body
,
_
:=
ioutil
.
ReadAll
(
res
.
Body
)
bodyString
:=
string
(
body
)
if
status
:=
res
.
StatusCode
;
status
!=
expectedStatus
{
t
.
Errorf
(
"Tested %v %v %v ; handler returned wrong status code: got %v want %v"
,
method
,
testURL
,
payload
,
status
,
expectedStatus
)
}
if
!
strings
.
HasPrefix
(
bodyString
,
expectedBody
)
{
t
.
Errorf
(
"Tested %v %v %v ; handler returned unexpected body: got %v want %v"
,
method
,
testURL
,
payload
,
bodyString
,
expectedBody
)
}
return
bodyString
}
// CreateServerTester wraps DoRequestOnServer to factorize t, port and jar
func
CreateServerTester
(
t
*
testing
.
T
,
hostname
string
,
port
string
,
jar
*
cookiejar
.
Jar
)
func
(
method
string
,
route
string
,
headers
map
[
string
]
string
,
payload
string
,
expectedStatus
int
,
expectedBody
string
)
string
{
return
func
(
method
string
,
url
string
,
headers
map
[
string
]
string
,
payload
string
,
expectedStatus
int
,
expectedBody
string
)
string
{
return
DoRequestOnServer
(
t
,
hostname
,
port
,
jar
,
method
,
url
,
headers
,
payload
,
expectedStatus
,
expectedBody
)
}
}
internal/tokens/tokens.go
View file @
33c62528
...
...
@@ -5,7 +5,7 @@ import (
"compress/flate"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
cryptorand
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
...
...
@@ -13,6 +13,7 @@ import (
"io"
"io/ioutil"
"log"
"math/rand"
"net/http"
"strings"
"time"
...
...
@@ -68,14 +69,14 @@ type Token struct {
}
// StoreData creates a token with the given data and returns it in a cookie
func
(
m
manager
)
StoreData
(
data
interface
{},
hostName
string
,
cookieName
string
,
duration
time
.
Duration
,
w
http
.
ResponseWriter
)
{
func
(
m
manager
)
StoreData
(
data
interface
{},
cookieName
string
,
duration
time
.
Duration
,
w
http
.
ResponseWriter
)
{
expiration
:=
now
()
.
Add
(
duration
)
value
,
err
:=
m
.
CreateToken
(
data
,
expiration
)
if
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
500
)
return
}
cookie
:=
http
.
Cookie
{
Name
:
cookieName
,
Domain
:
hostName
,
Value
:
value
,
Expires
:
expiration
,
Secure
:
!
m
.
debugMode
,
HttpOnly
:
true
,
SameSite
:
http
.
SameSiteLaxMode
}
cookie
:=
http
.
Cookie
{
Name
:
cookieName
,
Value
:
value
,
Expires
:
expiration
,
Secure
:
!
m
.
debugMode
,
HttpOnly
:
true
,
SameSite
:
http
.
SameSiteLaxMode
}
http
.
SetCookie
(
w
,
&
cookie
)
}
...
...
@@ -196,7 +197,7 @@ func Encrypt(data []byte, key []byte) ([]byte, error) {
return
[]
byte
{},
err
}
nonce
:=
make
([]
byte
,
gcm
.
NonceSize
())
if
_
,
err
=
io
.
ReadFull
(
rand
.
Reader
,
nonce
);
err
!=
nil
{
if
_
,
err
=
io
.
ReadFull
(
crypto
rand
.
Reader
,
nonce
);
err
!=
nil
{
return
[]
byte
{},
err
}
cipherData
:=
gcm
.
Seal
(
nonce
,
nonce
,
data
,
nil
)
...
...
@@ -221,3 +222,17 @@ func Decrypt(data []byte, key []byte) ([]byte, error) {
}
return
plainData
,
nil
}
func
NewShareToken
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
r
.
Method
!=
http
.
MethodPost
{
http
.
Error
(
w
,
"method not allowed"
,
http
.
StatusMethodNotAllowed
)
return
}
const
possible
=
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
const
token_length
=
64
b
:=
make
([]
byte
,
token_length
)
for
i
:=
range
b
{
b
[
i
]
=
possible
[
rand
.
Intn
(
len
(
possible
))]
}
fmt
.
Fprintf
(
w
,
string
(
b
))
}
main.go
View file @
33c62528
...
...
@@ -6,7 +6,6 @@ import (
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"os"
"os/signal"
...
...
@@ -14,28 +13,21 @@ import (
"syscall"
"time"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/auth"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/cuckoo"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/log"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/mail"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/rootmux"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/thehive"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/tokens"
"github.com/nicolaspernoud/vestibule/pkg/middlewares"
)
var
(
i
int
hostname
string
logFile
=
common
.
StringValueFromEnv
(
"LOG_FILE"
,
""
)
// Optional file to log to
debugMode
=
flag
.
Bool
(
"debug"
,
false
,
"Debug mode, disable let's encrypt, enable CORS and more logging"
)
)
func
init
()
{
defaultHostname
:=
"cyber-signal"
hostname
=
common
.
StringValueFromEnv
(
"HOSTNAME"
,
defaultHostname
)
}
func
main
()
{
// Initialize logger
if
logFile
!=
""
{
...
...
@@ -51,26 +43,12 @@ func main() {
}()
}
log
.
Logger
.
Println
(
"--- Server is starting ---"
)
fullHostname
:=
middlewares
.
GetFullHostname
(
hostname
,
8080
)
log
.
Logger
.
Println
(
"Main hostname is "
,
fullHostname
)
// Initializations
tokens
.
Init
(
"./configs/tokenskey.json"
,
*
debugMode
)
mainMux
:=
http
.
NewServeMux
()
mainMux
.
HandleFunc
(
"/Logout"
,
auth
.
HandleLogout
)
mainMux
.
HandleFunc
(
"/Login"
,
auth
.
HandleInMemoryLogin
)
mainMux
.
Handle
(
"/"
,
middlewares
.
NoCache
(
http
.
FileServer
(
&
common
.
FallBackWrapper
{
Assets
:
http
.
Dir
(
"web"
)})))
// commonMux := http.NewServeMux()
mainMux
.
Handle
(
"/api/common/WhoAmI"
,
auth
.
ValidateAuthMiddleware
(
auth
.
WhoAmI
(),
[]
string
{
"*"
},
false
))
// mainMux.Handle("/api/common/", http.StripPrefix("/api/common", auth.ValidateAuthMiddleware(commonMux, []string{"*"}, true)))
adminMux
:=
http
.
NewServeMux
()
adminMux
.
HandleFunc
(
"/newToken"
,
createNewToken
)
adminMux
.
HandleFunc
(
"/users/"
,
auth
.
ProcessUsers
)
mainMux
.
Handle
(
"/api/admin/"
,
http
.
StripPrefix
(
"/api/admin"
,
auth
.
ValidateAuthMiddleware
(
adminMux
,
[]
string
{
os
.
Getenv
(
"ADMIN_ROLE"
)},
true
)))
// Create main server
mainMux
:=
rootmux
.
CreateRootMux
(
"web"
)
http
.
ListenAndServe
(
":8080"
,
mainMux
)
// tags := make([]string, 1)
...
...
@@ -109,20 +87,6 @@ func hello(w http.ResponseWriter, r *http.Request) {
fmt
.
Fprintf
(
w
,
"Hello"
)
}
func
createNewToken
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
r
.
Method
!=
http
.
MethodPost
{
http
.
Error
(
w
,
"method not allowed"
,
http
.
StatusMethodNotAllowed
)
return
}
const
possible
=
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
const
token_length
=
64
b
:=
make
([]
byte
,
token_length
)
for
i
:=
range
b
{
b
[
i
]
=
possible
[
rand
.
Intn
(
len
(
possible
))]
}
fmt
.
Fprintf
(
w
,
string
(
b
))
}
// SaveFile is a function allowing to save locally the file in .msg format received. The file in .msg is receveid in binary stream.
func
SaveFile
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
// file allows you to create a new file in a predefined directory
...
...
web/components/sharetoken/sharetoken.js
View file @
33c62528
...
...
@@ -39,8 +39,8 @@ class ShareToken {
});
this
.
token_textarea
=
document
.
getElementById
(
"
token-textarea
"
);
this
.
copy_token_button
=
document
.
getElementById
(
"
token-copy
"
);
this
.
copy_token_button
.
addEventListener
(
"
click
"
,
function
()
{
this
.
copy
(
token_textarea
);
this
.
copy_token_button
.
addEventListener
(
"
click
"
,
()
=>
{
this
.
copy
(
this
.
token_textarea
);
});
}
...
...
web/index.html
View file @
33c62528
...
...
@@ -8,6 +8,11 @@