initial commit
This commit is contained in:
commit
7e8842c630
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
9
.idea/loginToTemplate.iml
Normal file
9
.idea/loginToTemplate.iml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/loginToTemplate.iml" filepath="$PROJECT_DIR$/.idea/loginToTemplate.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
73
file_system_store.go
Normal file
73
file_system_store.go
Normal file
@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type FileSystemUserStore struct {
|
||||
database *json.Encoder
|
||||
userList UserList
|
||||
}
|
||||
|
||||
func NewFileSystemUserStore(file *os.File) (*FileSystemUserStore, error) {
|
||||
err := initialiseUserDBFile(file)
|
||||
if err != nil {
|
||||
fmt.Errorf("problem initializing user db file, %v", err)
|
||||
}
|
||||
|
||||
userList, err := NewUserList(file)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("problem loading user list from file %s, %v", file.Name(), err)
|
||||
}
|
||||
|
||||
return &FileSystemUserStore{
|
||||
database: json.NewEncoder(&Tape{file}),
|
||||
userList: userList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initialiseUserDBFile(file *os.File) error {
|
||||
file.Seek(0, 0)
|
||||
|
||||
info, err := file.Stat()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("problem getting file info from file %s, %v", file.Name(), err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
file.Write([]byte("[]"))
|
||||
file.Seek(0, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// todo add interface
|
||||
|
||||
func (f *FileSystemUserStore) AddUserToUserList(user User) {
|
||||
f.userList = append(f.userList, user)
|
||||
}
|
||||
|
||||
func (f *FileSystemUserStore) DeleteUserFromUserList(user User) {
|
||||
var userList []User
|
||||
for _, u := range f.userList {
|
||||
if !reflect.DeepEqual(u, user) {
|
||||
userList = append(userList, u)
|
||||
}
|
||||
}
|
||||
f.userList = userList
|
||||
}
|
||||
|
||||
func (f *FileSystemUserStore) GetUsers() UserList {
|
||||
return f.userList
|
||||
}
|
||||
|
||||
func (f *FileSystemUserStore) UpdateUserFromUserList(toUpdate, newState User) {
|
||||
user := f.userList.Find(toUpdate.Username)
|
||||
*user = newState
|
||||
}
|
139
file_system_store_test.go
Normal file
139
file_system_store_test.go
Normal file
@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func createTempFile(t testing.TB, initialDB string) (*os.File, func()) {
|
||||
t.Helper()
|
||||
|
||||
tempfile, err := ioutil.TempFile("", "db")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not create temp file %v", err)
|
||||
}
|
||||
|
||||
tempfile.Write([]byte(initialDB))
|
||||
|
||||
removeFile := func() {
|
||||
tempfile.Close()
|
||||
os.Remove(tempfile.Name())
|
||||
}
|
||||
|
||||
return tempfile, removeFile
|
||||
}
|
||||
|
||||
func TestFileSystemStore(t *testing.T) {
|
||||
t.Run("get username", func(t *testing.T) {
|
||||
database, cleanDatabase := createTempFile(t, `[
|
||||
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
|
||||
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
|
||||
defer cleanDatabase()
|
||||
|
||||
store, err := NewFileSystemUserStore(database)
|
||||
|
||||
//userList := store.userList
|
||||
//user := userList.Find("testUser")
|
||||
//user.UpdateAdminStatus(true)
|
||||
|
||||
AssertNoError(t, err)
|
||||
|
||||
got := store.GetUsers()
|
||||
|
||||
want := []User{
|
||||
{Username: "testUser", Key: "12345", IpAddress: "123.123.123.123.50120", IsAdmin: true},
|
||||
{Username: "jimmy", Key: "67890", IpAddress: "12.12.12.12:50121", IsAdmin: false},
|
||||
}
|
||||
|
||||
AssertUsers(t, got, want)
|
||||
|
||||
//read again
|
||||
got = store.GetUsers()
|
||||
AssertUsers(t, got, want)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
t.Run("get username", func(t *testing.T) {
|
||||
database, cleanDatabase := createTempFile(t, `[
|
||||
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
|
||||
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
|
||||
defer cleanDatabase()
|
||||
|
||||
store, err := NewFileSystemUserStore(database)
|
||||
|
||||
AssertNoError(t, err)
|
||||
|
||||
want := User{
|
||||
Username: "newUser",
|
||||
Key: "12345",
|
||||
IpAddress: "1.1.1.1:1111",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
store.AddUserToUserList(want)
|
||||
|
||||
got := store.GetUsers()
|
||||
AssertUserExists(t, got, want)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
t.Run("get username", func(t *testing.T) {
|
||||
database, cleanDatabase := createTempFile(t, `[
|
||||
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
|
||||
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
|
||||
defer cleanDatabase()
|
||||
|
||||
store, err := NewFileSystemUserStore(database)
|
||||
|
||||
AssertNoError(t, err)
|
||||
|
||||
want := User{
|
||||
Username: "testUser",
|
||||
Key: "12345",
|
||||
IpAddress: "123.123.123.123:50120",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
want = *store.userList.Find("testUser")
|
||||
store.DeleteUserFromUserList(want)
|
||||
|
||||
got := store.GetUsers()
|
||||
AssertUserNotExists(t, got, want)
|
||||
|
||||
want = *store.userList.Find("jimmy")
|
||||
AssertUserExists(t, got, want)
|
||||
})
|
||||
}
|
||||
|
||||
//todo move this to user_test.go and user.go
|
||||
func TestUpdateUserStore(t *testing.T) {
|
||||
t.Run("get username", func(t *testing.T) {
|
||||
database, cleanDatabase := createTempFile(t, `[
|
||||
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
|
||||
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
|
||||
defer cleanDatabase()
|
||||
|
||||
store, err := NewFileSystemUserStore(database)
|
||||
|
||||
AssertNoError(t, err)
|
||||
|
||||
userToUpdate := *store.userList.Find("testUser")
|
||||
|
||||
want := User{
|
||||
Username: "testUser2",
|
||||
Key: "123456",
|
||||
IpAddress: "124.124.124.124:50120",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
want = *store.userList.Find("testUser")
|
||||
store.UpdateUserFromUserList(userToUpdate, want)
|
||||
|
||||
got := store.GetUsers()
|
||||
AssertUserExists(t, got, want)
|
||||
})
|
||||
}
|
17
in_memory_user_store.go
Normal file
17
in_memory_user_store.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func NewInMemoryUserStore() *InMemoryUserStore {
|
||||
return &InMemoryUserStore{map[string]string{}}
|
||||
}
|
||||
|
||||
type InMemoryUserStore struct {
|
||||
store map[string]string
|
||||
}
|
||||
|
||||
func (i InMemoryUserStore) GetUserName(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func (i InMemoryUserStore) ShowIP(name string) string {
|
||||
return i.store[name]
|
||||
}
|
16
main.go
Normal file
16
main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const hostAndPort = ":5000"
|
||||
|
||||
func main() {
|
||||
server := NewLoginServer(NewInMemoryUserStore())
|
||||
|
||||
if err := http.ListenAndServe(hostAndPort, server); err != nil {
|
||||
log.Fatalf("could not listen on port %v", err)
|
||||
}
|
||||
}
|
57
server.go
Normal file
57
server.go
Normal file
@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type UserStore interface {
|
||||
GetUserName(name string) string
|
||||
ShowIP(name string) string
|
||||
}
|
||||
|
||||
type ServerHandler struct {
|
||||
store UserStore
|
||||
http.Handler
|
||||
userIpMap map[string]string
|
||||
}
|
||||
|
||||
func NewLoginServer(store UserStore) *ServerHandler {
|
||||
s := &ServerHandler{
|
||||
store,
|
||||
http.NewServeMux(),
|
||||
nil,
|
||||
}
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/", http.HandlerFunc(s.homeHandler))
|
||||
s.Handler = router
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *ServerHandler) homeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
s.updateIpHandler(w, r)
|
||||
case http.MethodGet:
|
||||
s.sendHomeHandler(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerHandler) sendHomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(r.RemoteAddr))
|
||||
}
|
||||
|
||||
func (s *ServerHandler) updateIpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
//userName := strings.TrimPrefix(r.URL.Path, "/")
|
||||
//if ! s.store.GetUserName(userName)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Write([]byte(r.RemoteAddr))
|
||||
}
|
||||
|
||||
func (s *ServerHandler) GetUserName(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func (s *ServerHandler) ShowIP(name string) string {
|
||||
return s.userIpMap[name]
|
||||
}
|
11
server_integration_test.go
Normal file
11
server_integration_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
//func TestPortalLoads(t *testing.T) {
|
||||
// t.Run("Test Portal Loads", func(t *testing.T) {
|
||||
// server := NewServer()
|
||||
//
|
||||
// servr.ServeHTTP(httptest.NewRecorder(), request)
|
||||
//
|
||||
// t.Run("")
|
||||
// })
|
||||
//}
|
48
server_test.go
Normal file
48
server_test.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const jsonContentType = "application/json"
|
||||
|
||||
func TestPortal(t *testing.T) {
|
||||
store := StubUserStore{
|
||||
map[string]string{
|
||||
"testUser": "123.123.123.123:50120",
|
||||
"jimmy": "12.12.12.12:50121",
|
||||
},
|
||||
}
|
||||
server := NewLoginServer(&store)
|
||||
|
||||
t.Run("Logs client ip", func(t *testing.T) {
|
||||
for _, want := range store.userIpMap {
|
||||
t.Run(fmt.Sprintf("got ip from "+want), func(t *testing.T) {
|
||||
request := NewGetHomeRequest()
|
||||
request.RemoteAddr = want
|
||||
response := httptest.NewRecorder()
|
||||
server.ServeHTTP(response, request)
|
||||
got := response.Body.String()
|
||||
|
||||
AssertStatus(t, response.Code, http.StatusOK)
|
||||
//todo we should update on post not get
|
||||
AssertResponseBody(t, got, want)
|
||||
//t.Fatalf("This test is bad, we should store on post not get")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Update client IP", func(t *testing.T) {
|
||||
request := NewPutHomeRequest("testUser")
|
||||
want := "5.5.5.5:10121"
|
||||
request.RemoteAddr = want
|
||||
response := httptest.NewRecorder()
|
||||
server.ServeHTTP(response, request)
|
||||
//todo make this fail
|
||||
response.Body.String()
|
||||
AssertStatus(t, response.Code, http.StatusAccepted)
|
||||
})
|
||||
}
|
14
tape.go
Normal file
14
tape.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
// Tape represents an os.File that will re-write from the start on every Write call.
|
||||
type Tape struct {
|
||||
File *os.File
|
||||
}
|
||||
|
||||
func (t *Tape) Write(p []byte) (n int, err error) {
|
||||
t.File.Truncate(0)
|
||||
t.File.Seek(0, 0)
|
||||
return t.File.Write(p)
|
||||
}
|
25
tape_test.go
Normal file
25
tape_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Tape_Write(t *testing.T) {
|
||||
file, clean := createTempFile(t, "12345")
|
||||
defer clean()
|
||||
|
||||
tape := Tape{File: file}
|
||||
|
||||
tape.Write([]byte("foo"))
|
||||
|
||||
file.Seek(0, 0)
|
||||
contents, _ := ioutil.ReadAll(file)
|
||||
|
||||
got := string(contents)
|
||||
want := "foo"
|
||||
|
||||
if got != want {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
81
testing.go
Normal file
81
testing.go
Normal file
@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type StubUserStore struct {
|
||||
userIpMap map[string]string
|
||||
}
|
||||
|
||||
func (s *StubUserStore) GetUserName(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func (s *StubUserStore) ShowIP(name string) string {
|
||||
return s.userIpMap[name]
|
||||
}
|
||||
|
||||
func AssertNoError(t testing.TB, err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatalf("didn't expect an error, but got one, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewGetHomeRequest() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/"), nil)
|
||||
return req
|
||||
}
|
||||
|
||||
func NewPutHomeRequest(userName string) *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/"+userName), nil)
|
||||
return req
|
||||
}
|
||||
|
||||
func userInUserList(ul UserList, user User) bool {
|
||||
for _, u := range ul {
|
||||
if reflect.DeepEqual(u, user) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AssertResponseBody(t testing.TB, got, want string) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("response body is wrong, got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertStatus(t testing.TB, got, want int) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("did not get correct status, expected %d got %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertUsers(t testing.TB, got, want []User) {
|
||||
t.Helper()
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertUserExists(t testing.TB, got []User, want User) {
|
||||
t.Helper()
|
||||
if !userInUserList(got, want) {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertUserNotExists(t testing.TB, got []User, want User) {
|
||||
t.Helper()
|
||||
if userInUserList(got, want) {
|
||||
t.Errorf("Expected %v to be deleted but it was not. List is %v", want, got)
|
||||
}
|
||||
}
|
20
user.go
Normal file
20
user.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type User struct {
|
||||
Username string
|
||||
Key string
|
||||
IpAddress string
|
||||
IsAdmin bool
|
||||
}
|
||||
|
||||
func (u *User) UpdateUserKey(key string) {
|
||||
u.Key = key
|
||||
}
|
||||
|
||||
func (u *User) UpdateUserIp(ipAddress string) {
|
||||
u.IpAddress = ipAddress
|
||||
}
|
||||
|
||||
func (u *User) UpdateAdminStatus(isAdmin bool) {
|
||||
u.IsAdmin = isAdmin
|
||||
}
|
31
user_list.go
Normal file
31
user_list.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// UserList stores a collection of users.
|
||||
type UserList []User
|
||||
|
||||
// Find tries to return username from UserList
|
||||
func (ul UserList) Find(username string) *User {
|
||||
for i, u := range ul {
|
||||
if u.Username == username {
|
||||
return &ul[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewUserList creates a UserList from JSON
|
||||
func NewUserList(rdr io.Reader) (UserList, error) {
|
||||
var userList []User
|
||||
err := json.NewDecoder(rdr).Decode(&userList)
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("problem parsing userlist, %v", err)
|
||||
}
|
||||
return userList, err
|
||||
}
|
51
user_test.go
Normal file
51
user_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUpdateUserKey(t *testing.T) {
|
||||
user := User{
|
||||
Username: "bob",
|
||||
Key: "12345",
|
||||
IpAddress: "12.12.12.12:50123",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
want := "67890"
|
||||
|
||||
user.UpdateUserKey(want)
|
||||
if user.Key != want {
|
||||
t.Errorf("Expected key %v, got %v", want, user.Key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserIp(t *testing.T) {
|
||||
user := User{
|
||||
Username: "bob",
|
||||
Key: "12345",
|
||||
IpAddress: "12.12.12.12:50123",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
want := "13.13.13.13:50124"
|
||||
|
||||
user.UpdateUserIp(want)
|
||||
if user.IpAddress != want {
|
||||
t.Errorf("Expected key %v, got %v", want, user.IpAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAdmin(t *testing.T) {
|
||||
user := User{
|
||||
Username: "bob",
|
||||
Key: "12345",
|
||||
IpAddress: "12.12.12.12:50123",
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
want := true
|
||||
|
||||
user.UpdateAdminStatus(want)
|
||||
if user.IsAdmin != want {
|
||||
t.Errorf("Expected key %v, got %v", want, user.IsAdmin)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user