extending add account test

This commit is contained in:
Roland Osborne 2022-01-15 23:25:43 -08:00
parent 17602f169a
commit a5a02d7abb
14 changed files with 131 additions and 123 deletions

View File

@ -1,7 +1,6 @@
package databag
import (
"log"
"testing"
"net/http/httptest"
"encoding/base64"
@ -22,6 +21,7 @@ func TestAccount(t *testing.T) {
dec.Decode(&token);
if resp.StatusCode != 200 {
t.Errorf("failed to create account")
return
}
// validate account token
@ -32,13 +32,32 @@ func TestAccount(t *testing.T) {
resp = w.Result();
if resp.StatusCode != 200 {
t.Errorf("invalid token value")
return
}
dec = json.NewDecoder(resp.Body);
var tokenType string;
dec.Decode(&tokenType);
if tokenType != "create" {
t.Errorf("invalid token type")
return
}
// check if username is available
r = httptest.NewRequest("GET", "/account/claimable?username=databag", nil)
r.Header.Add("Authorization","Bearer " + token)
w = httptest.NewRecorder()
GetAccountUsername(w, r);
resp = w.Result();
if resp.StatusCode != 200 {
t.Errorf("invalid token value")
return
}
dec = json.NewDecoder(resp.Body);
var available bool;
dec.Decode(&available);
if !available {
t.Errorf("username not available")
return
}
log.Println("TestAccount: done");
}

View File

@ -1,8 +1,6 @@
package databag
import (
"log"
"encoding/json"
"net/http"
"databag/internal/store"
"github.com/theckman/go-securerandom"
@ -10,35 +8,26 @@ import (
func AddNodeAccount(w http.ResponseWriter, r *http.Request) {
// validate login
if !adminLogin(r) {
log.Printf("AddNodeAccount - invalid admin credentials");
LogMsg("invalid admin credentials");
w.WriteHeader(http.StatusUnauthorized);
return
}
data, err := securerandom.Base64OfBytes(32)
if err != nil {
log.Println("AddNodeAccount - failed to generate token");
LogMsg("failed to generate token");
w.WriteHeader(http.StatusInternalServerError);
return
}
token := store.AccountToken{TokenType: "create", Token: data };
if res := store.DB.Create(&token).Error; res != nil {
log.Println("AddNodeAccount - failed to store token");
LogMsg("failed to store token");
w.WriteHeader(http.StatusInternalServerError);
return
}
body, err := json.Marshal(data);
if err != nil {
log.Println("GetNodeConfig - failed to marshal response");
w.WriteHeader(http.StatusInternalServerError);
return
}
w.Write(body)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
WriteResponse(w, data);
}

View File

@ -1,37 +0,0 @@
package databag
import (
"net/http"
"golang.org/x/crypto/bcrypt"
)
func adminLogin(r *http.Request) bool {
// extract request auth
username, password, ok := r.BasicAuth();
if !ok || username == "" || password == "" {
return false
}
// nothing to do if not configured
if !getBoolConfigValue(CONFIG_CONFIGURED, false) {
return false;
}
// compare username
if getStrConfigValue(CONFIG_USERNAME, "") != username {
return false
}
// compare password
p := getBinConfigValue(CONFIG_PASSWORD, nil);
if bcrypt.CompareHashAndPassword(p, []byte(password)) != nil {
return false
}
return true;
}
func bearerAuth(r *http.Request) string {
return "";
}

View File

@ -63,11 +63,6 @@ func GetAccountStatus(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func GetAccountUsername(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
func GetPublicClaimable(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)

View File

@ -49,13 +49,13 @@ func Status(w http.ResponseWriter, r *http.Request) {
var act uint = 0
// get revisions for the account
var ar accountRevision
err = store.DB.Model(&Revision{}).Where("ID = ?", act).First(&ar).Error
var account accountRevision
err = store.DB.Model(&store.Account{}).Where("ID = ?", act).First(&account).Error
if err != nil {
log.Println("Status - failed to get account revision")
// return
}
rev := getRevision(ar)
rev := getRevision(account)
// send current version
var msg []byte

View File

@ -1,45 +1,19 @@
package databag
import (
"log"
"strings"
"errors"
"net/http"
"encoding/json"
"gorm.io/gorm"
"databag/internal/store"
)
func GetAccountToken(w http.ResponseWriter, r *http.Request) {
// extract token
auth := r.Header.Get("Authorization")
token := strings.TrimSpace(strings.TrimPrefix(auth, "Bearer"))
// lookup token
var accountToken store.AccountToken
err := store.DB.Where("token = ?", token).First(&accountToken).Error
accountToken, err := bearerAccountToken(r);
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Println("GetAccountToken - token not found");
w.WriteHeader(http.StatusNotFound)
} else {
log.Println("GetAccountToken - failed to retrieve token");
w.WriteHeader(http.StatusInternalServerError)
}
LogMsg("token not found");
w.WriteHeader(http.StatusNotFound)
return
}
// return token type
body, err := json.Marshal(accountToken.TokenType);
if err != nil {
log.Println("GetNodeConfig - failed to marshal response");
w.WriteHeader(http.StatusInternalServerError);
return
}
w.Write(body);
w.Header().Set("Content-Type", "application/json charset=UTF-8")
w.WriteHeader(http.StatusOK)
WriteResponse(w, accountToken.TokenType);
}

View File

@ -0,0 +1,42 @@
package databag
import (
"net/http"
"databag/internal/store"
)
type accountUsername struct {
Username string
}
func GetAccountUsername(w http.ResponseWriter, r *http.Request) {
_, err := bearerAccountToken(r);
if err != nil {
LogMsg("authentication failed")
w.WriteHeader(http.StatusUnauthorized)
return
}
username := r.URL.Query().Get("username")
if username == "" {
LogMsg("invalid username")
w.WriteHeader(http.StatusBadRequest)
return
}
var accounts []accountUsername;
err = store.DB.Model(&store.Account{}).Where("username = ?", username).Find(&accounts).Error
if err != nil {
LogMsg("failed to query accounts")
w.WriteHeader(http.StatusInternalServerError)
return
}
if len(accounts) == 0 {
WriteResponse(w, true)
} else {
WriteResponse(w, false)
}
}

View File

@ -1,20 +1,23 @@
package databag
import (
"log"
"encoding/json"
"errors"
"net/http"
"gorm.io/gorm"
"databag/internal/store"
)
func GetNodeClaimable(w http.ResponseWriter, r *http.Request) {
c := getBoolConfigValue(CONFIG_CONFIGURED, false);
body, err := json.Marshal(!c);
var config store.Config
err := store.DB.Where("config_id = ?", CONFIG_CONFIGURED).First(&config).Error
if err != nil {
log.Println("GetNodeClaimable - failed to marshal response");
if errors.Is(err, gorm.ErrRecordNotFound) {
WriteResponse(w, true)
} else {
w.WriteHeader(http.StatusInternalServerError);
}
return
}
w.Write(body);
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
WriteResponse(w, false);
}

View File

@ -1,8 +1,6 @@
package databag
import (
"log"
"encoding/json"
"net/http"
)
@ -10,7 +8,7 @@ func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
// validate login
if !adminLogin(r) {
log.Printf("SetNodeConfig - invalid admin credentials");
LogMsg("SetNodeConfig - invalid admin credentials");
w.WriteHeader(http.StatusUnauthorized);
return
}
@ -21,12 +19,6 @@ func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
config.PublicLimit = getNumConfigValue(CONFIG_PUBLICLIMIT, 0);
config.AccountStorage = getNumConfigValue(CONFIG_STORAGE, 0);
body, err := json.Marshal(config);
if err != nil {
log.Println("GetNodeConfig - failed to marshal response");
}
w.Write(body);
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
WriteResponse(w, config);
}

View File

@ -13,6 +13,9 @@ import (
"log"
"net/http"
"time"
"os"
"runtime"
"strings"
)
func Logger(inner http.Handler, name string) http.Handler {
@ -30,3 +33,9 @@ func Logger(inner http.Handler, name string) http.Handler {
)
})
}
func LogMsg(msg string) {
_, file, line, _ := runtime.Caller(1)
p, _ := os.Getwd()
log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, msg)
}

View File

@ -47,6 +47,7 @@ func Claim() {
w := httptest.NewRecorder()
SetNodeClaim(w, r)
if w.Code != 200 {
LogMsg("HERE");
panic("server not initially claimable")
}
}

View File

@ -1,7 +1,7 @@
package databag
import (
"log"
"errors"
"net/http"
"gorm.io/gorm"
"databag/internal/store"
@ -10,27 +10,30 @@ import (
func SetNodeClaim(w http.ResponseWriter, r *http.Request) {
// confirm node hasn't been configured
if getBoolConfigValue(CONFIG_CONFIGURED, false) {
var config store.Config
err := store.DB.Where("config_id = ?", CONFIG_CONFIGURED).First(&config).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
w.WriteHeader(http.StatusInternalServerError)
return
}
if config.BoolValue {
w.WriteHeader(http.StatusUnauthorized)
return
}
// extract credentials
username, password, ok := r.BasicAuth();
if !ok || username == "" || password == "" {
log.Printf("SetNodeClaim - invalid credenitals");
LogMsg("SetNodeClaim - invalid credenitals");
w.WriteHeader(http.StatusBadRequest)
return
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
log.Printf("SetNodeClaim - failed to hash password");
LogMsg("SetNodeClaim - failed to hash password");
w.WriteHeader(http.StatusInternalServerError)
return
}
// store credentials
err = store.DB.Transaction(func(tx *gorm.DB) error {
if res := tx.Create(&store.Config{ConfigId: CONFIG_USERNAME, StrValue: username}).Error; res != nil {
return res
@ -44,7 +47,7 @@ func SetNodeClaim(w http.ResponseWriter, r *http.Request) {
return nil;
})
if(err != nil) {
log.Printf("SetNodeCalim - failed to store credentials");
LogMsg("SetNodeCalim - failed to store credentials");
w.WriteHeader(http.StatusInternalServerError)
return
}

View File

@ -46,10 +46,10 @@ type AccountToken struct {
type Account struct {
ID uint `gorm:"primaryKey;not null;unique;autoIncrement"`
PublicKey []byte `gorm:"not null"`
PrivateKey []byte `gorm:"not null"`
KeyType []byte `gorm:"not null"`
ProfileId string `gorm:"not null;uniqueIndex"`
PublicKey []byte `gorm:"not null"`
PrivateKey []byte `gorm:"not null"`
KeyType []byte `gorm:"not null"`
ProfileId string `gorm:"not null;uniqueIndex"`
Username string `gorm:"not null;uniqueIndex"`
Password []byte `gorm:"not null"`
Name string

View File

@ -0,0 +1,18 @@
package databag
import (
"encoding/json"
"net/http"
)
func WriteResponse(w http.ResponseWriter, v interface{}) {
body, err := json.Marshal(v);
if err != nil {
LogMsg("marshal failed")
w.WriteHeader(http.StatusInternalServerError)
} else {
w.Write(body);
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
}