diff --git a/internal/member/dummy/adapter.go b/internal/member/dummy/adapter.go deleted file mode 100644 index b4888048..00000000 --- a/internal/member/dummy/adapter.go +++ /dev/null @@ -1,35 +0,0 @@ -package dummy - -import ( - "demodesk/neko/internal/types" -) - -func New() types.MemberManager { - return &MemberManagerCtx{} -} - -type MemberManagerCtx struct{} - -func (manager *MemberManagerCtx) Connect() error { - return nil -} - -func (manager *MemberManagerCtx) Disconnect() error { - return nil -} - -func (manager *MemberManagerCtx) Insert(id string, profile types.MemberProfile) error { - return nil -} - -func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { - return nil -} - -func (manager *MemberManagerCtx) Delete(id string) error { - return nil -} - -func (manager *MemberManagerCtx) Select() (map[string]types.MemberProfile, error) { - return map[string]types.MemberProfile{}, nil -} diff --git a/internal/member/dummy/manager.go b/internal/member/dummy/manager.go new file mode 100644 index 00000000..3ef7d123 --- /dev/null +++ b/internal/member/dummy/manager.go @@ -0,0 +1,47 @@ +package dummy + +import ( + "demodesk/neko/internal/types" +) + +func New() types.MemberManager { + return &MemberManagerCtx{} +} + +type MemberManagerCtx struct{} + +func (manager *MemberManagerCtx) Connect() error { + return nil +} + +func (manager *MemberManagerCtx) Disconnect() error { + return nil +} + +func (manager *MemberManagerCtx) Authenticate(username string, password string) (string, types.MemberProfile, error) { + return username, types.MemberProfile{}, nil +} + +func (manager *MemberManagerCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) { + return "", nil +} + +func (manager *MemberManagerCtx) Select(id string) (types.MemberProfile, error) { + return types.MemberProfile{}, nil +} + +func (manager *MemberManagerCtx) SelectAll(limit int, offset int) (map[string]types.MemberProfile, error) { + return map[string]types.MemberProfile{}, nil +} + +func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { + return nil +} + +func (manager *MemberManagerCtx) UpdatePassword(id string, passwrod string) error { + return nil +} + +func (manager *MemberManagerCtx) Delete(id string) error { + return nil +} diff --git a/internal/member/file/adapter.go b/internal/member/file/adapter.go deleted file mode 100644 index 6218f445..00000000 --- a/internal/member/file/adapter.go +++ /dev/null @@ -1,128 +0,0 @@ -package file - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "sync" - - "demodesk/neko/internal/types" -) - -func New(file string) types.MemberManager { - return &MemberManagerCtx{ - file: file, - mu: sync.Mutex{}, - } -} - -type MemberManagerCtx struct { - file string - mu sync.Mutex -} - -func (manager *MemberManagerCtx) Connect() error { - return nil -} - -func (manager *MemberManagerCtx) Disconnect() error { - return nil -} - -func (manager *MemberManagerCtx) Insert(id string, profile types.MemberProfile) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - profiles, err := manager.deserialize() - if err != nil { - return err - } - - _, ok := profiles[id] - if ok { - return fmt.Errorf("Member ID already exists.") - } - - profiles[id] = profile - - return manager.serialize(profiles) -} - -func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - profiles, err := manager.deserialize() - if err != nil { - return err - } - - _, ok := profiles[id] - if !ok { - return fmt.Errorf("Member ID does not exist.") - } - - profiles[id] = profile - - return manager.serialize(profiles) -} - -func (manager *MemberManagerCtx) Delete(id string) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - profiles, err := manager.deserialize() - if err != nil { - return err - } - - _, ok := profiles[id] - if !ok { - return fmt.Errorf("Member ID does not exist.") - } - - delete(profiles, id) - - return manager.serialize(profiles) -} - -func (manager *MemberManagerCtx) Select() (map[string]types.MemberProfile, error) { - manager.mu.Lock() - defer manager.mu.Unlock() - - profiles, err := manager.deserialize() - return profiles, err -} - -func (manager *MemberManagerCtx) deserialize() (map[string]types.MemberProfile, error) { - file, err := os.OpenFile(manager.file, os.O_RDONLY|os.O_CREATE, os.ModePerm) - if err != nil { - return nil, err - } - - raw, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - - if len(raw) == 0 { - return map[string]types.MemberProfile{}, nil - } - - var profiles map[string]types.MemberProfile - if err := json.Unmarshal([]byte(raw), &profiles); err != nil { - return nil, err - } - - return profiles, nil -} - -func (manager *MemberManagerCtx) serialize(data map[string]types.MemberProfile) error { - raw, err := json.Marshal(data) - if err != nil { - return err - } - - return ioutil.WriteFile(manager.file, raw, os.ModePerm) -} diff --git a/internal/member/file/manager.go b/internal/member/file/manager.go new file mode 100644 index 00000000..c570b676 --- /dev/null +++ b/internal/member/file/manager.go @@ -0,0 +1,223 @@ +package file + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "sync" + + "demodesk/neko/internal/types" +) + +func New(file string) types.MemberManager { + return &MemberManagerCtx{ + file: file, + mu: sync.Mutex{}, + } +} + +type MemberManagerCtx struct { + file string + mu sync.Mutex +} + +type MemberEntry struct { + Password string `json:"password"` + Profile types.MemberProfile `json:"profile"` +} + +func (manager *MemberManagerCtx) Connect() error { + return nil +} + +func (manager *MemberManagerCtx) Disconnect() error { + return nil +} + +func (manager *MemberManagerCtx) Authenticate(username string, password string) (string, types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + // id will be also username + id := username + + entry, err := manager.getEntry(id) + if err != nil { + return "", types.MemberProfile{}, err + } + + // TODO: Use hash function. + if entry.Password != password { + return "", types.MemberProfile{}, fmt.Errorf("Invalid password.") + } + + return id, entry.Profile, nil +} + +func (manager *MemberManagerCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + // id will be also username + id := username + + entries, err := manager.deserialize() + if err != nil { + return "", err + } + + entry, ok := entries[id] + if ok { + return "", fmt.Errorf("Member ID already exists.") + } + + // TODO: Use hash function. + entry.Password = password + entry.Profile = profile + entries[id] = entry + + return id, manager.serialize(entries) +} + +func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + entries, err := manager.deserialize() + if err != nil { + return err + } + + entry, ok := entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + entry.Profile = profile + entries[id] = entry + + return manager.serialize(entries) +} + +func (manager *MemberManagerCtx) UpdatePassword(id string, password string) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + entries, err := manager.deserialize() + if err != nil { + return err + } + + entry, ok := entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + // TODO: Use hash function. + entry.Password = password + entries[id] = entry + + return manager.serialize(entries) +} + +func (manager *MemberManagerCtx) Select(id string) (types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + entry, err := manager.getEntry(id) + if err != nil { + return types.MemberProfile{}, err + } + + return entry.Profile, nil +} + +func (manager *MemberManagerCtx) SelectAll(limit int, offset int) (map[string]types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles := map[string]types.MemberProfile{} + + entries, err := manager.deserialize() + if err != nil { + return profiles, err + } + + i := 0 + for id, entry := range entries { + if i < offset || i > offset + limit { + continue + } + + profiles[id] = entry.Profile + i = i + 1 + } + + return profiles, nil +} + +func (manager *MemberManagerCtx) Delete(id string) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + entries, err := manager.deserialize() + if err != nil { + return err + } + + _, ok := entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + delete(entries, id) + + return manager.serialize(entries) +} + +func (manager *MemberManagerCtx) deserialize() (map[string]MemberEntry, error) { + file, err := os.OpenFile(manager.file, os.O_RDONLY|os.O_CREATE, os.ModePerm) + if err != nil { + return nil, err + } + + raw, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + if len(raw) == 0 { + return map[string]MemberEntry{}, nil + } + + var entries map[string]MemberEntry + if err := json.Unmarshal([]byte(raw), &entries); err != nil { + return nil, err + } + + return entries, nil +} + +func (manager *MemberManagerCtx) getEntry(id string) (MemberEntry, error) { + entries, err := manager.deserialize() + if err != nil { + return MemberEntry{}, err + } + + entry, ok := entries[id] + if !ok { + return MemberEntry{}, fmt.Errorf("Member ID does not exist.") + } + + return entry, nil +} + +func (manager *MemberManagerCtx) serialize(data map[string]MemberEntry) error { + raw, err := json.Marshal(data) + if err != nil { + return err + } + + return ioutil.WriteFile(manager.file, raw, os.ModePerm) +} diff --git a/internal/member/object/adapter.go b/internal/member/object/adapter.go deleted file mode 100644 index 412ea012..00000000 --- a/internal/member/object/adapter.go +++ /dev/null @@ -1,74 +0,0 @@ -package object - -import ( - "fmt" - "sync" - - "demodesk/neko/internal/types" -) - -func New() types.MemberManager { - return &MemberManagerCtx{ - profiles: make(map[string]types.MemberProfile), - mu: sync.Mutex{}, - } -} - -type MemberManagerCtx struct { - profiles map[string]types.MemberProfile - mu sync.Mutex -} - -func (manager *MemberManagerCtx) Connect() error { - return nil -} - -func (manager *MemberManagerCtx) Disconnect() error { - return nil -} - -func (manager *MemberManagerCtx) Insert(id string, profile types.MemberProfile) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - _, ok := manager.profiles[id] - if ok { - return fmt.Errorf("Member ID already exists.") - } - - manager.profiles[id] = profile - return nil -} - -func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - _, ok := manager.profiles[id] - if !ok { - return fmt.Errorf("Member ID does not exist.") - } - - manager.profiles[id] = profile - return nil -} - -func (manager *MemberManagerCtx) Delete(id string) error { - manager.mu.Lock() - defer manager.mu.Unlock() - - _, ok := manager.profiles[id] - if !ok { - return fmt.Errorf("Member ID does not exist.") - } - - delete(manager.profiles, id) - return nil -} - -func (manager *MemberManagerCtx) Select() (map[string]types.MemberProfile, error) { - manager.mu.Lock() - defer manager.mu.Unlock() - - return manager.profiles, nil -} diff --git a/internal/member/object/manager.go b/internal/member/object/manager.go new file mode 100644 index 00000000..ed2c148f --- /dev/null +++ b/internal/member/object/manager.go @@ -0,0 +1,149 @@ +package object + +import ( + "fmt" + "sync" + + "demodesk/neko/internal/types" +) + +func New() types.MemberManager { + return &MemberManagerCtx{ + entries: make(map[string]MemberEntry), + mu: sync.Mutex{}, + } +} + +type MemberManagerCtx struct { + entries map[string]MemberEntry + mu sync.Mutex +} + +type MemberEntry struct { + Password string `json:"password"` + Profile types.MemberProfile `json:"profile"` +} + +func (manager *MemberManagerCtx) Connect() error { + return nil +} + +func (manager *MemberManagerCtx) Disconnect() error { + return nil +} + +func (manager *MemberManagerCtx) Authenticate(username string, password string) (string, types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + // id will be also username + id := username + + entry, ok := manager.entries[id] + if !ok { + return "", types.MemberProfile{}, fmt.Errorf("Member ID does not exist.") + } + + // TODO: Use hash function. + if entry.Password != password { + return "", types.MemberProfile{}, fmt.Errorf("Invalid password.") + } + + return id, entry.Profile, nil +} + +func (manager *MemberManagerCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + // id will be also username + id := username + + entry, ok := manager.entries[id] + if ok { + return "", fmt.Errorf("Member ID already exists.") + } + + // TODO: Use hash function. + entry.Password = password + entry.Profile = profile + manager.entries[id] = entry + + return id, nil +} + +func (manager *MemberManagerCtx) Update(id string, profile types.MemberProfile) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + entry, ok := manager.entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + entry.Profile = profile + manager.entries[id] = entry + + return nil +} + +func (manager *MemberManagerCtx) UpdatePassword(id string, password string) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + entry, ok := manager.entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + // TODO: Use hash function. + entry.Password = password + manager.entries[id] = entry + + return nil +} + +func (manager *MemberManagerCtx) Select(id string) (types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + entry, ok := manager.entries[id] + if ok { + return types.MemberProfile{}, fmt.Errorf("Member ID already exists.") + } + + return entry.Profile, nil +} + +func (manager *MemberManagerCtx) SelectAll(limit int, offset int) (map[string]types.MemberProfile, error) { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles := map[string]types.MemberProfile{} + + i := 0 + for id, entry := range manager.entries { + if i < offset || i > offset + limit { + continue + } + + profiles[id] = entry.Profile + i = i + 1 + } + + return profiles, nil +} + +func (manager *MemberManagerCtx) Delete(id string) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + _, ok := manager.entries[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + delete(manager.entries, id) + + return nil +} diff --git a/internal/types/member.go b/internal/types/member.go index 90e4e196..8b680370 100644 --- a/internal/types/member.go +++ b/internal/types/member.go @@ -1,11 +1,25 @@ package types +type MemberProfile struct { + Name string `json:"name"` + IsAdmin bool `json:"is_admin"` + CanLogin bool `json:"can_login"` + CanConnect bool `json:"can_connect"` + CanWatch bool `json:"can_watch"` + CanHost bool `json:"can_host"` + CanAccessClipboard bool `json:"can_access_clipboard"` +} + type MemberManager interface { Connect() error Disconnect() error - Insert(id string, profile MemberProfile) error + Authenticate(username string, password string) (string, MemberProfile, error) + + Insert(username string, password string, profile MemberProfile) (string, error) + Select(id string) (MemberProfile, error) + SelectAll(limit int, offset int) (map[string]MemberProfile, error) Update(id string, profile MemberProfile) error + UpdatePassword(id string, passwrod string) error Delete(id string) error - Select() (map[string]MemberProfile, error) } diff --git a/internal/types/session.go b/internal/types/session.go index dc517b6c..1a69f103 100644 --- a/internal/types/session.go +++ b/internal/types/session.go @@ -2,16 +2,6 @@ package types import "net/http" -type MemberProfile struct { - Name string `json:"name"` - IsAdmin bool `json:"is_admin"` - CanLogin bool `json:"can_login"` - CanConnect bool `json:"can_connect"` - CanWatch bool `json:"can_watch"` - CanHost bool `json:"can_host"` - CanAccessClipboard bool `json:"can_access_clipboard"` -} - type SessionState struct { IsConnected bool `json:"is_connected"` IsWatching bool `json:"is_watching"`